Android touch事件的分发

Android touch事件的分发分为几个层次:

  • 驱动层传递至framework的InputManagerService
  • WMS通过ViewRootImpl传递至目标窗口
  • touch事件到达DecorView后传递至子view
    对应用开发者来说,主要关注第三条,下面也主要touch事件在view中的分发逻辑

dispatchTouchEvent

这个是事件分发的核心接口,整个 View 之间的事件分发,实质上就是一个大的递归函数,而这个递归函数就是 dispatchTouchEvent 方法。在这个递归的过程中会适时调用 onInterceptTouchEvent (viewgroup)来拦截事件,或者调用 onTouchEvent 方法来处理事件。

整个分发逻辑可以这么概括:

  • 步骤 1:判断当前 ViewGroup 是否需要拦截此 touch 事件,如果拦截则此次 touch 事件不再会传递给子 View(或者以 CANCEL 的方式通知子 View)。

  • 步骤 2:如果没有拦截,则将事件分发给子 View 继续处理,如果子 View 将此次事件捕获,则将 mFirstTouchTarget 赋值给捕获 touch 事件的 View。

  • 步骤 3:根据 mFirstTouchTarget 重新分发事件。

touchEvent拦截

ViewGroup#dispatchTouchEvent.png

从源码可以看到,当action为ACTION_DOWN时或者没有mFirstTouchTarget 不为 null时(子ViewdispatchTouchEvent 返回true)才会调用onInterceptTouchEvent 判断是否拦截;

可以推导出,一次连续touchEvent事件(down ...move...up)当父View拦截了touchEvent,那么后续事件将不会再去判断是否拦截(交由该view处理)。

注意,如果不拦截down,在move事件中拦截,则子view会收到cancel事件,后续事件也不会再去判断是否拦截,直接交由父View处理;、

事件分发至子view

如果没有拦截,则将事件分发给子 View 继续处理,如果子 View 将此次事件捕获,则将 mFirstTouchTarget 赋值给捕获 touch 事件的 View。

源码内容过多,此处总结主要逻辑:

  • 当时间为Down,触发主动分发
  • 遍历所有子 View,判断事件坐标是否在子 View 坐标范围内,并且子 View 并没有处在动画状态;
  • 调用 dispatchTransformedTouchEvent 方法将事件分发给子 View,如果子 View 捕获事件成功,则将 mFirstTouchTarget 赋值给为 View。

根据mFirstTouchTarget再次分发

  • 如果此时 mFirstTouchTarget 为 null,说明在上述的事件分发中并没有子 View 对事件进行了捕获操作。这种情况下,直接调用 dispatchTransformedTouchEvent 方法,并传入 child 为 null,最终会调用 super.dispatchTouchEvent 方法。实际上最终会调用自身的 onTouchEvent 方法,进行处理 touch 事件。也就是说:如果没有子 View 捕获处理 touch 事件,ViewGroup 会通过自身的 onTouchEvent 方法进行处理。
  • mFirstTouchTarget 不为 null,说明在上面步骤 2 中有子 View 对 touch 事件进行了捕获,则直接将当前以及后续的事件交给 mFirstTouchTarget 指向的 View 进行处理。

总结

  • 判断是否需要拦截 —> 主要是根据 onInterceptTouchEvent 方法的返回值来决定是否拦截;

  • 在 DOWN 事件中将 touch 事件分发给子 View —> 这一过程如果有子 View 捕获消费了 touch 事件,会对 mFirstTouchTarget 进行赋值;

  • 最后一步,DOWN、MOVE、UP 事件都会根据 mFirstTouchTarget 是否为 null,决定是自己处理 touch 事件,还是再次分发给子 View。

你可能感兴趣的:(Android touch事件的分发)