Android 事件分发机制

Android 事件分发机制一直让人头痛,之前也是面向 GitHub 编程得过且过。今天下定决心了解一下,以便后面自己定制 View 效果。Android 触摸事件有三个基本类型:ACTION_DOWN, ACTION_MOVE, ACTION_UP,后两者的传递顺序取决于 DOWN 的传递结果,所以就从 ACTION_DOWN 开始分析。

ACTION_DOWN

全景

借用一张下面参考文章里的全景图片,注意这里指的仅仅是 ACTION_DOWN 事件的传递。先解释一下:

  • 白色箭头表示事件传递(函数调用)
  • 箭头上的标注表示调用前提。(supper 表示上一级直接调用,false 表示若上级返回 false 则系统继续向下调用)
  • 白色方块内的消费箭头表示若此函数返回对应值,则事件终止传递(也称作被消费了)

以左上角事件入口为例,首先 Activity 收到事件触发 dispatchTouchEvent,不论返回 true 还是 false 事件均终止,任何组件的任何函数均不会再被调用(包括 activity 自己的 onTouchEvent),只有 return super.dispatchTouchEvent() 也就是调用了 super 才会继续传递到下一级。

对于下一级 ViewGroup 的 dispatchTouchEvent 来讲,返回 true 同样消费事件立即终止传递。返回 false 则会回溯到上一层的 onTouchEvent。调用 super 则继续向下传递。

全程传递

我们假设事件没有被拦截、消费,那么整个传输流程类似 U 型:

不难看出,整个流程分为左右两部分,我们暂且叫做分派回溯。分派是自顶到底的,主要用于事件的传递。回溯是从底到顶的,主要用于事件的处理。所有方法的默认实现就是 return super.xxx() 因此事件默认情况下可以走完整个流程。

拦截器

ViewGroup

细心的同学应该注意到了,在分派过程中除了整齐的 dispatchTouchEvent 方法外,乱入了一个 onInterceptEvent 方法,可以称之为拦截器。顾名思义拦截器的作用就是拦截此事件供自己使用(就像大理 gov 一样对待口罩那样)。不难看出 dispatchTouchEvent 方法调用后根据内部处理的不同有三个后果,分别是 ①终止传递 ②向下传递 ③向上回溯,而前面提到过,一般来讲处理具体的处理会放在 onTouchEvent 中。那么问题来了,终止传递后我居然自己无法处理事件?(参考 U 型图中的 ViewGroup 层,无论 dispatchTouchEvent 作何响应都无法调用自己的 onTouchEvent

这时候就需要拦截器登场啦。在 onInterceptEvent 方法中若返回 true 则表示拦截此事件,传递给自己的 onTouchEvent 继续处理并决定是否回溯。一定要与 dispatchTouchEvent 区分开,它返回 true 会终止整个流程,更别提回溯了;而拦截器返回 true 后只是停止向下分派,会从自己开始向上回溯。

所以拦截器的作用就是拦截向下分派、自己处理事件并决定是否继续向上回溯。

View

那么 View 怎么办?拦截器方法只有 ViewGroup 中才有,如果 View 想自己处理事件呢?如果理解了拦截器的作用那么这个问题就非常简单了。因为 View 已经是底层组件,它不需要继续向下传递事件,因此它在 onInterceptEvent 直接调用 super 就可以触发自身的 onTouchEvent也就是说 View 的 super 实现了 ViewGroup 的拦截器功能。

总结

到此为止 ACTION_DOWN 的传递流程基本上分析完了,最后总结一下在不同的阶段要达到不同的目的应该执行什么操作。

分派阶段

期望行为 操作
继续分派 调用 super
消费事件,终止整个流程 dispatch return true
终止分派,向上回溯 dispatch return false
终止分派,交给自己处理 ViewGroup: 拦截器返回 true; View: 调用 super

回溯阶段

期望行为 操作
继续回溯 调用 super 或 return false
消费事件,终止回溯 return true

ACTION_MOVE/UP

不同情况

DOWN 事件不同,其他的都属于后续事件,只有消费了它的上一个事件(例如DOWN)的控件,及其上层的控件,才能接受到后续事件。这么说太抽象了,画个图看看(下图中红色表示 DOWN 事件,蓝色表示后续事件)

1. ViewGroup2 dispatchTouchEvent 消费事件(return true

此时对于后续事件来讲,ViewGroup2 消费了上一个事件,而 Activity, ViewGroup1 都是它的上层控件,因此他们都能收到后续事件。

2. View onTouchEvent 消费事件

和上一个情形类似,不再赘述了。

3. ViewGroup1 onTouchEvent 消费事件

这次稍微有点不同,因为 ViewGroup1 消费了事件,因此只有它的上层控件才能收到后续事件,也就是只有 Activity 和它自己。

4. View dispatchTouchEvent 返回 false,ViewGroup onTouchEvent 消费事件

这次我们在 View 的 dispatchTouchEvent 返回了 false,也就是会直接回溯到 ViewGroup2. 通过这个案例可以看出,后续事件的传递仅与消费之前事件的控件及其上层控件有关,与之前事件在消费控件下层的传递路径无关。

5. ViewGroup onInterceptEvent 返回 true 拦截事件,ViewGroup onTouchEvent 消费事件

再次印证了上一个情况得出的结论。

总结

总结后续事件的传递路径,就是一直传递到消费前一个事件的控件,并传递到消费前一个事件的方法。注意,onInterceptEvent 只能拦截事件不能消费事件。

参考

图解 Android 事件分发机制 注意博主有个别图标错了,本文已更正。

发表评论

电子邮件地址不会被公开。 必填项已用*标注