Android事件分发机制(二)——ViewGroup的事件分发
一、ViewGroup事件分发的流程图

以上是ViewGroup事件分发的整体流程,其中主要的方法有:
- dispatchTouchEvent(MotionEvent ev)
- onInterceptTouchEvent(MotionEvent ev)
- onTouchEvent(MotionEvent ev)
二、ViewGroup事件分发源码解析
ViewGroup的dispatchTouchEvent方法主要做了以下三件事:
- 去判断是否需要拦截事件
- 在当前ViewGroup中找到用户真正点击的View
- 分发事件到View上
在ViewGroup的dispatchTouchEvent(ev)中有4个if判断:

首先是一个调试对象的判定,mInputEventConsistencyVerifier是在ViewGroup的父类(View)中进行实例化的。


其中事件分发的主要流程在onFilterTouchEventForSecurity(ev)这个判断中。

接下里进去看一下onFilterTouchEventForSecurity(ev)方法,此方法主要用于判定触摸事件是否符合安全策略,如果此方法返回true(即符合安全策略),则继续事件分发,否则dispatchTouchEvent(ev)方法将返回false,事件未被消费,整个流程结束:

符合安全策略的话就会继续走下面分发的逻辑,系统判断当前的事件是否为ACTION_DOWN,如果当前为ACTION_DOWN事件,则执行如图两个方法,通过cancelAndClearTouchTargets(ev)方法来取消并且清除触摸事件的目标列表,通过resetTouchState()方法来重置所有的触摸状态,为新的事件循环作准备:

接下来系统会检查拦截情况:

当intercepted为true,表示拦截事件,不会继续分发;返回false,表示不会拦截事件,继续分发。

onInterceptTouchEvent()的判断条件:如果当前事件来自鼠标事件输入,当前事件为ACTION_DOWN事件,当前是否按下了鼠标左键并且当前触摸点是在一个滚动条上,此时才会返回为true,否则就会返回false。
接下来就会进入事件派发的逻辑:

如果当前不是取消时间,并且不去拦截这个事件,那就就进行事件分发的逻辑中;
在事件分发逻辑中,首先会清空掉所有之前的触摸点的信息,然后获取到当前ViewGroup下所有子View的数量,如果newTouchTarget为空,并且子View的数量不为0的情况下,就会进入下面的逻辑中;
首先获取到当前触摸点的坐标值;获取到所有可以被分发的子View的集合;customOrder用于判断是否自定义了绘制顺序,接下来循环遍历子View的集合,通过getAndVerifyPreorderedIndex(childrenCount,i,customOrder)方法来获取到子View的索引值。

获取到子View索引值后,再通过getAndVerifyPreorderedView方法获取到View。

现在为止,目标View就已经获取到了。接下来是对当前的View进行处理。
通过canViewReceivePointerEvents()方法和isTransformedTouchPointInView()来判断当前的View是否是处理该事件的View。
canViewReceivePointerEvents()用于检查当前的目标View是否可以接受到触摸事件;isTransformedTouchPointInView()判断当前触摸事件是否在View 的范围内。

在获取到View 之后,就去获取其触摸对象。

如果当前触摸对象newTouchTarget不为null,表示当前的子View已经获取到一个触摸事件了,就直接结束当前的循环。
如果当前触摸对象newTouchTarget为null,就会调用resetCancelNextUpFlag(child)方法,此方法用于检查传入的View是否设置了暂时不会接受事件的标识,如果有就清除掉标识。
接下来,系统就会进行下一层的事件分发。

dispatchTransformedTouchEvent()方法描述了事件从ViewGroup到View是怎样过度的。


如果dispatchTransformedTouchEvent()返回为true的话,说明事件在子View中已经被消费了,当前ViewGroup就会结束掉循环。
到此为止,ViewGroup对子View查找过程就结束了。在查找完之后,会把子View的集合preorederedList清除掉。


接下来系统会判断mFirstTouchTarget是否为null,如果它恒等为null的话,表示到此为止依然没有子View来处理触摸事件。不为null的话,系统就会遍历目标列表。
最后会将handled返回。
学海无涯苦作舟
个人博客:http://www.coderlearning.cn/
