Android事件分发之流程分析

2,605 阅读5分钟

原文首发于微信公众号:躬行之(jzman-blog),欢迎关注交流!

上篇文章中主要针对 Activity、ViewGroup 以及 View 中的相关事件方法进行了基本概述,下面主要以案例的方式对 Android 中的事件传递进行归纳。

  1. 默认的事件分发流程
  2. 事件分发
  3. 事件处理
  4. 事件拦截
  5. 总结

阅读本篇文章之前请先阅读:

默认的事件分发流程

Android 事件分发的正常流程,也就是默认情况下事件是如何分发的,首先自定义 ViewGroup 和 自定义 View, 如继承 LinearLayout 自定义 ViewGroup 为 MLinearLayout,继承 TextView 自定义 View 为 MTextView,然后分别重写各自与事件分发相关的事件方法,Activity 也重写自己的事件方法,根据日志观察正常的事件分发流程,布局如下图所示:

通过触发 MTextView 的 onTouch 事件,对各个事件分发方法都默认处理,执行流程如下图所示:

显然,事件分发的起点是 Activity 的 dispatchTouchEvebt() 方法,然后通过一系列动作事件向 ViewGroup 分发,具体表现就是调用 ViewGroup 的 dispatchTouchEvebt() 方法,ViewGroup 的 onInterceptTouchEvent() 方法默认返回 false,也就是默认不拦截事件向子 View(ViewGroup 也是子 View,如 MRelativeLayout),然后,一直向下分发直到最内层的子 View 为止.

当然,事件分发至子 View , 也就是调用子 View 的 dispatchTouchEvebt() 方法,其事件分发方法内部调用了 onTouchEvent() 方法,onTouchEvent() 默认返回 false,表示该 View 不消费事件,反之返回 true 则消费该事件,下面是根据上述执行结果绘制的默认的事件分发流程,如下图所示:

通过上面的验证,至少能够了解各个事件相关方法的执行顺序咯。

事件分发

dispatchTouchEvent() 方法负责事件的分发,决定当前事件是自身消费还是继续向子 View 分发,返回 true,表示事件已经被消费,后续的事件也会继续执行,如 ACTION_MOVE、ACTION_UP等事件,返回 false 则继续向子 View 分发事件,如果是子 View 是 ViewGroup,则先将事件分发给 onInterceptTouchEvent() 方法,从而决定是否拦截该事件。按照上面的案例将 MRelativeLayout 的 dispatchTouchEvent() 方法返回 true ,事件消费且接受之后的一系列事件,日志截图如下图所示:

显然,当 MRelativeLayout 的 dispatchTouchEvent() 方法返回 true 的时候,事件没有向下传递,这里为了更清楚的观察各个事件方法的执行,具体的事件(如 ACTION_DOWN 等)没有输出在日志中,所以,当 dispatchTouchEvent() 方法返回 true 时表示事件已消费。

事件拦截

onInterceptTouchEvent() 方法负责事件的拦截,其返回值决定是否对当前事件进行拦截,返回值为 true,表示拦截当前事件,反之不拦截事件,也就是默认实现继续调用子 View 的 dispatchTouchEvent() 方法进行事件的分发,按照上面的案列将 MRelativeLayout 的 onInterceptTouchEvent() 方法返回值设为 true,拦截当前事件,使事件不在向下传递,日志截图如下图所示:

话说事件拦截之后,那就要进行事件处理了,当然事件处理归 onTouchEvent() 方法负责,onTouchEvent() 可以处理也可以不处理,如果 onTouchEvent() 返回 true 意味着事件处理完成,返回 false 则事件交由父 View 的 onTouchEvent() 方法进行处理,如图示情况,MRelativeLayout 拦截了事件,但是没有处理事件,最终交由 Activity 来处理咯。

事件处理

onTouchEvent() 方法负责事件的处理,其返回值决定是否消费该事件,返回 true 表示当前 View 消费事件,返回 false 表示不消费事件,事件向复 View 的方向传递,按照上面的案例将 MRelativeLayout 的 onTouchEvent() 方法返回值设为 true,日志截图如下图所示:

上面截图中省略了若干 ACTION_MOVE 事件,当某一 View 事件拦截成功之后,ACTION_DOWN 之后的一系列事件将直接由该 View 处理。

总结

  1. 事件分发由父 View 向子 View 进行事件的分发,在分发过程中如果没有被拦截和处理,当分发至最深层次的 View 时,将向上回传事件,最终交由 Activity 进行处理,后续事件也将不会接受;如果中途某个 View 拦截了事件并进行了处理,那么由拦截事件的 View 的 onTouchEvent() 方法进行处理;如果中途某个 View 拦截了事件并且不进行处理,则事件停止向子 View 分发,然后向父 View 的方向交由父 View 的 onTouchEvent() 方法进行处理;
  2. 事件拦截成功之后则要对事件进行处理,事件的处理主要由拦截事件的 View 的 onTouchEvent() 方法或是其父 View 的 onTouchEvent() 来处理,是否处理具体由相应的 onTouchEvent() 方法的返回值决定;
  3. 事件的处理当然是 onTouchEvent() 方法了,返回 true 表示事件就此处理,反之事件未被处理,继续向上回传事件直至被处理。

个人微信公众号:躬行之 可以一起交流学习。