原创

Android事件分发机制(二)——ViewGroup的事件分发

一、ViewGroup事件分发的流程图

ViewGroup事件分发.png

以上是ViewGroup事件分发的整体流程,其中主要的方法有:

  • dispatchTouchEvent(MotionEvent ev)
  • onInterceptTouchEvent(MotionEvent ev)
  • onTouchEvent(MotionEvent ev)

二、ViewGroup事件分发源码解析

ViewGroup的dispatchTouchEvent方法主要做了以下三件事:

  1. 去判断是否需要拦截事件
  2. 在当前ViewGroup中找到用户真正点击的View
  3. 分发事件到View上

在ViewGroup的dispatchTouchEvent(ev)中有4个if判断:

ViewGroup01.png

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

ViewGroup03.png

ViewGroup02.png

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

ViewGroup05.png

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

安全策略源码.png

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

判断当前是否为ACTION_DOWN.png

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

判断是否拦截事件.png

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

onInterceptTouchEvent方法源码.png

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

接下来就会进入事件派发的逻辑:

事件派发过程.png

如果当前不是取消时间,并且不去拦截这个事件,那就就进行事件分发的逻辑中;

在事件分发逻辑中,首先会清空掉所有之前的触摸点的信息,然后获取到当前ViewGroup下所有子View的数量,如果newTouchTarget为空,并且子View的数量不为0的情况下,就会进入下面的逻辑中;

首先获取到当前触摸点的坐标值;获取到所有可以被分发的子View的集合;customOrder用于判断是否自定义了绘制顺序,接下来循环遍历子View的集合,通过getAndVerifyPreorderedIndex(childrenCount,i,customOrder)方法来获取到子View的索引值。

getAndVerifyPreorderedIndex源码.png

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

getAndVerifyPreorderedView的源码.png

现在为止,目标View就已经获取到了。接下来是对当前的View进行处理。

通过canViewReceivePointerEvents()方法和isTransformedTouchPointInView()来判断当前的View是否是处理该事件的View。

canViewReceivePointerEvents()用于检查当前的目标View是否可以接受到触摸事件;isTransformedTouchPointInView()判断当前触摸事件是否在View 的范围内。

判断当前的View是否是处理该事件的View.png

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

获取触摸对象.png

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

接下来,系统就会进行下一层的事件分发。

ViewGroup过度到View中.png

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

dispatchTransformedTouchEvent第一部分.png

dispatchTransformedTouchEvent第二部分.png

如果dispatchTransformedTouchEvent()返回为true的话,说明事件在子View中已经被消费了,当前ViewGroup就会结束掉循环。

到此为止,ViewGroup对子View查找过程就结束了。在查找完之后,会把子View的集合preorederedList清除掉。

循环遍历结束.png

分发触摸事件.png

接下来系统会判断mFirstTouchTarget是否为null,如果它恒等为null的话,表示到此为止依然没有子View来处理触摸事件。不为null的话,系统就会遍历目标列表。

最后会将handled返回。

学海无涯苦作舟

个人博客:http://www.coderlearning.cn/

我的微信公众号.jpg

读书笔记
  • 作者:HunterArley (联系作者)
  • 发表时间:2019-12-16 10:06
  • 版权声明:本网站部分内容转载于合作站点或其他站点,但都会注明作/译者和原出处。如有不妥之处,敬请指出。
  • 公众号转载:请在文末添加作者公众号二维码