太厉害了,终于有人能把 Android 事件分发机制讲的明明白白了!
关于 Android 事件分发机制网上的博文很多,但是很多都是写个 Demo 然后贴一下输出的 Log 或者拿源码分析,然后一堆的注释和说明,如果用心的去看肯定是收获不少但是确实很难把整个流程说清和记住。曾经也是拼命想记住整个流程,但是一段时间又忘了,最后觉得分析这种问题和事件流的走向,一张图来解释和说明会清晰很多,下面我根据画的一张事件分发流程图,说明的事件从用户点击之后,在不同函数不同返回值的情况的最终走向。
注:
仔细看的话,图分为 3 层,从上往下依次是 Activity、ViewGroup、View
事件从左上角那个白色箭头开始,由 Activity 的 dispatchTouchEvent 做分发
箭头的上面字代表方法返回值,(return true、return false、return super.xxxxx(),super 的意思是调用父类实现。
dispatchTouchEvent 和 onTouchEvent 的框里有个【true---->消费】的字,表示的意思是如果方法返回 true,那么代表事件就此消费,不会继续往别的地方传了,事件终止。
目前所有的图的事件是针对 ACTION_DOWN 的,对于 ACTION_MOVE 和 ACTION_UP 我们最后做分析。
之前图中的 Activity 的 dispatchTouchEvent 有误(图已修复),只有 return super.dispatchTouchEvent(ev) 才是往下走,返回 true 或者 false 事件就被消费了(终止传递)。
仔细看整个图,我们得出事件流 走向的几个结论(希望读者专心的看下图 1,多看几遍,脑子有比较清晰的概念。)
1、如果事件不被中断,整个事件流向是一个类 U 型图,我们来看下这张图,可能更能理解 U 型图的意思。
所以如果我们没有对控件里面的方法进行重写或更改返回值,而直接用 super 调用父类的默认实现,那么整个事件流向应该是从 Activity---->ViewGroup—>View 从上往下调用 dispatchTouchEvent 方法,一直到叶子节点(View)的时候,再由 View—>ViewGroup—>Activity 从下往上调用 onTouchEvent 方法。
2、dispatchTouchEvent 和 onTouchEvent 一旦 return true,事件就停止传递了(到达终点)(没有谁能再收到这个事件)。看下图中只要 return true 事件就没再继续传下去了,对于 return true 我们经常说事件被消费了,消费了的意思就是事件走到这里就是终点,不会往下传,没有谁能再收到这个事件了。
3、dispatchTouchEvent 和 onTouchEvent return false 的时候事件都回传给父控件的 onTouchEvent 处理。
看上图深蓝色的线,对于返回 false 的情况,事件都是传给父控件 onTouchEvent 处理。
对于 dispatchTouchEvent 返回 false 的含义应该是:事件停止往子 View 传递和分发同时开始往父控件回溯(父控件的 onTouchEvent 开始从下往上回传直到某个 onTouchEvent return true),事件分发机制就像递归,return false 的意义就是递归停止然后开始回溯。
对于 onTouchEvent return false 就比较简单了,它就是不消费事件,并让事件继续往父控件的方向从下往上流动。
**4、dispatchTouchEvent、onTouchEvent、onInterceptTouchEvent
ViewGroup 和 View 的这些方法的默认实现就是会让整个事件安装 U 型完整走完,所以 return super.xxxxxx() 就会让事件依照 U 型的方向的完整走完整个事件流动路径),中间不做任何改动,不回溯、不终止,每个环节都走到。**
所以如果看到方法 return super.xxxxx() 那么事件的下一个流向就是走 U 型下一个目标,稍微记住上面这张图,你就能很快判断出下一个走向是哪个控件的哪个函数。
5、onInterceptTouchEvent 的作用
Intercept 的意思就拦截,每个 ViewGroup 每次在做分发的时候,问一问拦截器要不要拦截(也就是问问自己这个事件要不要自己来处理)如果要自己处理那就在 onInterceptTouchEvent 方法中 return true 就会交给自己的 onTouchEvent 的处理,如果不拦截就是继续往子控件往下传。默认是不会去拦截的,因为子 View 也需要这个事件,所以 onInterceptTouchEvent 拦截器 return super.onInterceptTouchEvent()和 return false 是一样的,是不会拦截的,事件会继续往子 View 的 dispatchTouchEvent 传递。
6、ViewGroup 和 View 的 dispatchTouchEvent 方法返回 super.dispatchTouchEvent()的时候事件流走向。
首先看下 ViewGroup 的 dispatchTouchEvent,之前说的 return true 是终结传递。return false 是回溯到父 View 的 onTouchEvent,然后 ViewGroup 怎样通过 dispatchTouchEvent 方法能把事件分发到自己的 onTouchEvent 处理呢,return true 和 false 都不行,那么只能通过 Interceptor 把事件拦截下来给自己的 onTouchEvent,所以 ViewGroup dispatchTouchEvent 方法的 super 默认实现就是去调用 onInterceptTouchEvent,记住这一点。
那么对于 View 的 dispatchTouchEvent return super.dispatchTouchEvent()的时候呢事件会传到哪里呢,很遗憾 View 没有拦截器。但是同样的道理 return true 是终结。return false 是回溯会父类的 onTouchEvent,怎样把事件分发给自己的 onTouchEvent 处理呢,那只能 return super.dispatchTouchEvent,View 类的 dispatchTouchEvent()方法默认实现就是能帮你调用 View 自己的 onTouchEvent 方法的。
说了这么多,不知道有说清楚没有,我这边最后总结一下:
对于 dispatchTouchEvent,onTouchEvent,return true 是终结事件传递。return false 是回溯到父 View 的 onTouchEvent 方法。
ViewGroup 想把自己分发给自己的 onTouchEvent,需要拦截器 onInterceptTouchEvent 方法 return true 把事件拦截下来。
ViewGroup 的拦截器 onInterceptTouchEvent 默认是不拦截的,所以 return super.onInterceptTouchEvent()=return false;
View 没有拦截器,为了让 View 可以把事件分发给自己的 onTouchEvent,View 的 dispatchTouchEvent 默认实现(super)就是把事件分发给自己的 onTouchEvent。
ViewGroup 和 View 的 dispatchTouchEvent 是做事件分发,那么这个事件可能分发出去的四个目标
注:------> 后面代表事件目标需要怎么做。
1、 自己消费,终结传递。------->return true ;
2、 给自己的 onTouchEvent 处理-------> 调用 super.dispatchTouchEvent()系统默认会去调用 onInterceptTouchEvent,在 onInterceptTouchEvent return true 就会去把事件分给自己的 onTouchEvent 处理。
3、 传给子 View------>调用 super.dispatchTouchEvent()默认实现会去调用 onInterceptTouchEvent 在 onInterceptTouchEvent return false,就会把事件传给子类。
4、 不传给子 View,事件终止往下传递,事件开始回溯,从父 View 的 onTouchEvent 开始事件从下到上回归执行每个控件的 onTouchEvent------->return false;
注: 由于 View 没有子 View 所以不需要 onInterceptTouchEvent 来控件是否把事件传递给子 View 还是拦截,所以 View 的事件分发调用 super.dispatchTouchEvent()的时候默认把事件传给自己的 onTouchEvent 处理(相当于拦截),对比 ViewGroup 的 dispatchTouchEvent 事件分发,View 的事件分发没有上面提到的 4 个目标的第 3 点。
ViewGroup 和 View 的 onTouchEvent 方法是做事件处理的,那么这个事件只能有两个处理方式:
1、自己消费掉,事件终结,不再传给谁----->return true;
2、继续从下往上传,不消费事件,让父 View 也能收到到这个事件----->return false;View 的默认实现是不消费的。所以 super==false。
ViewGroup 的 onInterceptTouchEvent 方法对于事件有两种情况:
1、拦截下来,给自己的 onTouchEvent 处理—>return true;
2、不拦截,把事件往下传给子 View---->return false,ViewGroup 默认是不拦截的,所以 super==false;
关于 ACTION_MOVE 和 ACTION_UP####
上面讲解的都是针对 ACTION_DOWN 的事件传递,ACTION_MOVE 和 ACTION_UP 在传递的过程中并不是和 ACTION_DOWN 一样,你在执行 ACTION_DOWN 的时候返回了 false,后面一系列其它的 action 就不会再得到执行了。简单的说,就是当 dispatchTouchEvent 在进行事件分发的时候,只有前一个事件(如 ACTION_DOWN)返回 true,才会收到 ACTION_MOVE 和 ACTION_UP 的事件。具体这句话很多博客都说了,但是具体含义是什么呢?我们来看一下下面的具体分析。
上面提到过了,事件如果不被打断的话是会不断往下传到叶子层(View),然后又不断回传到 Activity,dispatchTouchEvent 和 onTouchEvent 可以通过 return true 消费事件,终结事件传递,而 onInterceptTouchEvent 并不能消费事件,它相当于是一个分叉口起到分流导流的作用,ACTION_MOVE 和 ACTION_UP 会在哪些函数被调用,之前说了并不是哪个函数收到了 ACTION_DOWN,就会收到 ACTION_MOVE 等后续的事件的。
下面通过几张图看看不同场景下,ACTION_MOVE 事件和 ACTION_UP 事件的具体走向并总结一下规律。
1、我们在 ViewGroup1 的 dispatchTouchEvent 方法返回 true 消费这次事件
ACTION_DOWN 事件从(Activity 的 dispatchTouchEvent)--------> (ViewGroup1 的 dispatchTouchEvent) 后结束传递,事件被消费(如下图红色的箭头代码 ACTION_DOWN 事件的流向)。
//打印日志
Activity | dispatchTouchEvent --> ACTION_DOWN
ViewGroup1 | dispatchTouchEvent --> ACTION_DOWN
---->消费
在这种场景下 ACTION_MOVE 和 ACTION_UP 将如何呢,看下面的打出来的日志
Activity | dispatchTouchEvent --> ACTION_MOVE
ViewGroup1 | dispatchTouchEvent --> ACTION_MOVE
TouchEventActivity | dispatchTouchEvent --> ACTION_UP
ViewGroup1 | dispatchTouchEvent --> ACTION_UP
下图中
红色的箭头代表 ACTION_DOWN 事件的流向
蓝色的箭头代表 ACTION_MOVE 和 ACTION_UP 事件的流向
2、我们在 ViewGroup2 的 dispatchTouchEvent 返回 true 消费这次事件
Activity | dispatchTouchEvent --> ACTION_DOWN
ViewGroup1 | dispatchTouchEvent --> ACTION_DOWN
ViewGroup1 | onInterceptTouchEvent --> ACTION_DOWN
ViewGroup2 | dispatchTouchEvent --> ACTION_DOWN
---->消费
Activity | dispatchTouchEvent --> ACTION_MOVE
ViewGroup1 | dispatchTouchEvent --> ACTION_MOVE
ViewGroup1 | onInterceptTouchEvent --> ACTION_MOVE
ViewGroup2 | dispatchTouchEvent --> ACTION_MOVE
TouchEventActivity | dispatchTouchEvent --> ACTION_UP
ViewGroup1 | dispatchTouchEvent --> ACTION_UP
ViewGroup1 | onInterceptTouchEvent --> ACTION_UP
ViewGroup2 | dispatchTouchEvent --> ACTION_UP
红色的箭头代表 ACTION_DOWN 事件的流向
蓝色的箭头代表 ACTION_MOVE 和 ACTION_UP 事件的流向
tZjFkOWVkYmMyMWU5NTVjOC5wbmc_aW1hZ2VNb2dyMi9hdXRvLW9yaWVudC9zdHJpcHxpbWFnZVZpZXcyLzIvdy83NjMvZm9ybWF0L3dlYnA?x-oss-process=image/format,png)
3、我们在 View 的 dispatchTouchEvent 返回 true 消费这次事件
这个我不就画图了,效果和在 ViewGroup2 的 dispatchTouchEvent return true 的差不多,同样的收到 ACTION_DOWN 的 dispatchTouchEvent 函数都能收到 ACTION_MOVE 和 ACTION_UP。
所以我们就基本可以得出结论如果在某个控件的 dispatchTouchEvent 返回 true 消费终结事件,那么收到 ACTION_DOWN 的函数也能收到 ACTION_MOVE 和 ACTION_UP。
4、我们在 View 的 onTouchEvent 返回 true 消费这次事件
红色的箭头代表 ACTION_DOWN 事件的流向
蓝色的箭头代表 ACTION_MOVE 和 ACTION_UP 事件的流向
5、我们在 ViewGroup 2 的 onTouchEvent 返回 true 消费这次事件
红色的箭头代表 ACTION_DOWN 事件的流向
蓝色的箭头代表 ACTION_MOVE 和 ACTION_UP 事件的流向
6、我们在 ViewGroup 1 的 onTouchEvent 返回 true 消费这次事件
红色的箭头代表 ACTION_DOWN 事件的流向
评论