Android 事件分发机制详解
1 完整流程图
下图就是完整的事件分发流程
- 建议点开、查看原图放大观看
- 心里反复多走几遍流程,三种主要方法 true 或 false 分别会走向哪里
- 此图可以说是本文的精髓所在,掌握了此图,也就相当于掌握了事件分发
2 具体分析
由于上面这张图太大,下面暂时用一张简化版的图代替,以便于我们分析
OK,我们开始!
- 首先可以看到,从上至下分为三层,依次是Activity、ViewGroup、View
事件分发的顺序基本上就是这样,假设有中间多层ViewGroup
顺序仍然是由 Activity -> ViewGroup ->ViewGroup ->.......->View
从上至下最终到达最末端的View - 接着我们需要清楚最主要的三个方法:
dispatchTouchEvent():分发
onInterceptTouchEvent():拦截
onTouchEvent():处理(对点击触摸事件进行处理)
而且,其中仅仅只有 ViewGroup 拥有 onInterceptTouchEvent 拦截的方法 - 这三个方法均都有返回值,return true、return false、return super.xxxxx(),super 的意思是调用父类实现
- dispatchTouchEvent和 onTouchEvent的框里有个【true---->消费】的字,表示的意思是如果方法返回true,那么代表事件就此消费,不会继续往别的地方传了,事件终止
- 目前所有的图的事件是针对ACTION_DOWN的,对于ACTION_MOVE和ACTION_UP我们最后做分析
2-1 要点
请记住以下的结论,当然我们会过一遍,然后帮助我们在理解中记忆
1 如果事件不被中断,整个事件流向是一个类U型图
- 仔细看上图
- 所以如果我们没有对控件里面的方法进行重写或更改返回值,而直接用super调用父类的默认实现,那么整个事件流向应该是从Activity---->ViewGroup--->View 从上往下调用dispatchTouchEvent方法,一直到末端View的时候,再由View--->ViewGroup--->Activity从下往上调用onTouchEvent方法。
2 dispatchTouchEvent 和 onTouchEvent 一旦return true,事件就停止传递了,我们可以理解为事件被消费了,消费了的意思就是事件走到这里就是终点,不会往下传,没有谁能再收到这个事件了
3 dispatchTouchEvent 和 onTouchEvent 都return false的时候事件都回传给父控件的onTouchEvent处理
- 可以简单理解为:任务从上级派发下来,我搞不定,只能反过来丢给我上级处理
4 dispatchTouchEvent、onTouchEvent、onInterceptTouchEvent
ViewGroup 和View的这些方法的默认实现就是会让整个事件安装U型完整走完,所以 return super.xxxxxx() 就会让事件依照U型的方向的完整走完整个事件流动路径),中间不做任何改动,不回溯、不终止,每个环节都走到
5 onInterceptTouchEvent的作用
- Intercept 的意思就拦截,每个ViewGroup每次在做分发的时候,问一问拦截器要不要拦截(也就是问问自己这个事件要不要自己来处理)
如果要自己处理那就在onInterceptTouchEvent方法中 return true就会交给自己的onTouchEvent的处理
如果不拦截就是继续往子控件往下传。默认是不会去拦截的,因为子View也需要这个事件,所以onInterceptTouchEvent拦截器return super.onInterceptTouchEvent()和return false是一样的,是不会拦截的,事件会继续往子View的dispatchTouchEvent传递
2-2 总结
最后总结一下:
- 对于 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。
2-3 关于ACTION_MOVE 和 ACTION_UP
1、我们在ViewGroup1 的dispatchTouchEvent 方法返回true消费这次事件
- 在这种场景下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消费这次事件
红色的箭头代表ACTION_DOWN 事件的流向
蓝色的箭头代表ACTION_MOVE 和 ACTION_UP 事件的流向
3、我们在View 的dispatchTouchEvent 返回true消费这次事件
这里就不赘述了,和第二个例子几乎一模一样
我们可以得出结论如果在某个控件的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 事件的流向
蓝色的箭头代表ACTION_MOVE 和 ACTION_UP 事件的流向
我们可以发现,在哪个View的onTouchEvent 返回true,那么ACTION_MOVE和ACTION_UP的事件从上往下传到这个View后就不再往下传递了,而直接传给自己的onTouchEvent 并结束本次事件传递过程。
还有其他几种也属于和上面类似的情况......
最后我们得出结论:
- ACTION_DOWN事件在哪个控件消费了(return true), 那么ACTION_MOVE和ACTION_UP就会从上往下(通过dispatchTouchEvent)做事件分发往下传,就只会传到这个控件,不会继续往下传
- 如果ACTION_DOWN事件是在dispatchTouchEvent消费,那么事件到此为止停止传递
如果ACTION_DOWN事件是在onTouchEvent消费的,那么会把ACTION_MOVE或ACTION_UP事件传给该控件的onTouchEvent处理并结束传递。
参考致谢:
https://www.jianshu.com/p/e99b5e8bd67b
https://blog.csdn.net/ezview_uniview/article/details/77619900#commentBox