事件分发在android开发中是一个让人头疼的问题,也是在面试过程中问的比较多的一个问题。同样如果能了解事件分发的过程,将有利于我们处理在开发过程中遇到的组件间滑动问题。所以这个也是在复习的时候重点要看的知识点。在准备这篇文章资料的时候我也在草稿纸上画了几次流程,但是总感觉没有《图解 Android 事件分发机制》作者的流程图画的好,所以文章中的流程图就抄了这位作者的流程图(汗颜)。

目录

  • 事件类型
  • 事件分发过程及顺序
  • 分发的三个阶段
  • 日志分析



1. 事件类型

当我们的手接触与屏幕接触得时候会产生一系列事件,我们可以通过MotionEvent这个类来得到事件的类型。常用的事件类型分为三种:

  • ACTION_DOWNN: 手指刚触摸屏幕,标志着触摸事件的开始
  • ACTION_MOVE: 手指在屏幕上移动
  • ACTION_UP: 手指从屏幕上抬起,标志着事件的结束

在正常的情况下,我们和手机交互会触发一些列的事件发生,例如点击事件会触发ACTION_DOWNN-->ACTION_MOVE,还有滑动事件会触发ACTION_DOWNN-->ACTION_MOVE-->ACTION_UP

2. 事件分发过程及顺序

事件分发过程: 事件的传递我们可以从点击事件来分析,点击事件触发后,系统把这个事件一层层分发,最终传递到具体的view中处理,这个事件的传递过程就是事件的分发过程。

事件的传递顺序是: Activity-->ViewGroup-->View

3. 分发的三个阶段

在研究事件分发的时候可结合上面的流程图

dispatchTouchEvent

分发事件阶段

这个事件可以分几种情况处理:

Activity中的处理

  • Activity中该方法不管返回true或者false事件都会被消耗,事件将不会在传播。
  • 返回父类同名方法:即super.dispatchTouchEvent(ev),那么事件将会继续传播

ViewGroup的处理

  • 返回true:事件被消耗,将不再被传播。
  • 返回false,事件将会回传,调用Activity的onTouchEvent方法。
  • 父类同名方法:将事件分发给自己的onInterceptTouchEvent方法

View中的处理

  • 返回的是false或者父类的同名方法,事件将会回传。事件会回传给给自己的父容器的onTouchEvent来处理。
  • 返回true:事件被消耗,不再传播。
onInterceptTouchEvent

拦截事件,只有ViewGroup才有这个事件。

  • 返回是false或者父类的同名方法,都代表不拦截这个事件,事件将传递给自己的子View中
  • 返回true:表示拦截该事件。调用自己的onTouchEvent来处理这个事件。
onTouchEvent

消耗事件阶段

这个事件也分下面几种情况处理

Activity中的处理

  • 不管这个方法返回的是false、true、或者父类的同名方法,事件都将在这个方法中被处理。事件能传播道这里,就说明这个窗口的ViewGroup和View都没有处理这个事件。

ViewGroup中的处理

  • 返回为false或者父类同名方法:代表它也不处理,事件将会回传给Activity的onTouchEvent来处理。后续的事件也将不再传递给他。
  • 返回true:事件被消耗不会再回传。后续事件将交给他处理,不再传递给它的子View

View中的处理

  • 返回为false或者父类同名方法:代表它不处理,事件将会回传给它的父容器的onTouchEvent来处理。后续的事件也将不再传递给他。
  • 返回true:事件被消耗不会再回传,后续事件将会继续传递给它处理。

4. 日志分析:

下面的所有日志信息可在GitHub的测试项目doc文件夹中的Android事件分发日志信息文件中查看到。

正常情况下日志信息(Activity、ViewGroup、View返回的都是父类同名方法)

View、ViewGroup以及Activity的onTouchEvent返回同名父类或者false的日志信息和下面的相同(Activity返回true也相同),所以就不在文章中粘出来了。



从上面的日志信息我们可以看到,当我们点击屏幕的个View的时候,事件先从EventActivity的dispatchTouchEvent分发给TestFrameLayout的dispatchTouchEvent然后在分发到TestView的dispatchTouchEvent中(提前是onInterceptTouchEvent没有拦截)最终交给了TestView的onTouchEvent方法。前文提到过,如果View的onTouch返回为false或者是父类的同名方法,将代表其不处理这个事件,那么事件将会回传。从上面的日志信息我们也能看出来,DOWN事件有被回传给TestFrameLayout的onTouchEvent处理。TestFrameLayout也是返回父类的同名方法,事件将继续回传最后回传到了EventActivity的onTouchEvent中吃力。另外我们从上面的信息中也可以看出,后面的UP事件也没有再向下分发,而是继续交给了Activity的onTouchEvent处理了。

View的onTouchEvent返回true的日志信息

当View的onTouch返回true的时候,代表要处理这个事件。这个事件就不会再回传,后续事件也将会交给他处理。可以分析下面红框的信息



当ViewGroup的onTouchEvent返回true时的日志信息

下面红色框为子控件日志信息,事件分发给ViewGroup的子组件TestView,但是它没有处理,事件回传给ViewGroup(黄色框中的日志),它的onTouchEvent返回true,代表事件被他消耗了,后续的事件也将分发给他处理。



当onInterceptTouchEvent返回true的日志信息

该方法如果返回为true,表示拦截,事件将交给TestFrameLayout自身的onTouchEvent处理(也就是最后事件消耗或者不消耗,都要看ViewGroup的onTouchEvent怎么处理了)。从下的日志信息中我们看到TestView的onTouchEvent方法并没有调用可以看出。



dispatchTouchEvent 返回true日志

注:下面给出的是ViewGroup的日志信息,View的该返回返回的信息和这个差不多,他们两个区别是View将事件传递给ViewGroup的dispatchTouchEvent,而ViewGroup将事件传递给的是Activity的dispatchTouchEvent方法中。因此由于篇幅原因,这里就粘出View的日志信息了。

如果dispatchTouchEvent返回true则代表事件被消耗,事件将不会在向下分发。后续事件将交给他处理。(View同理)



dispatchTouchEvent 返回false

如果dispatchTouchEvent返回false,事件也将不会在向下分发,事件将会回传给他上级的onTouchEvent方法中。后续事件将交给他的上级处理处理。(View同理)



终结

想要真正搞清楚事件分发,还是要亲自写个demo测试才能容易搞明白,这篇文章个人感觉写的还是有点乱(表达水平有限,嘿嘿)!如果看完了这篇文章不知道在说什么,可以看一看下面的两个链接,这两个链接算是我看过的讲事件分发的文章中写得计较好两个。推荐先看《图解 Android 事件分发机制》,一定要将作者画的流程图搞清楚。

参考:

图解 Android 事件分发机制

Android事件分发机制详解:史上最全面、最易懂