android事件分发机制_具体的日志分析
事件分发在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返回的都是父类同名方法)
imageView、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的时候,代表要处理这个事件。这个事件就不会再回传,后续事件也将会交给他处理。可以分析下面红框的信息
image当ViewGroup的onTouchEvent返回true时的日志信息
下面红色框为子控件日志信息,事件分发给ViewGroup的子组件TestView,但是它没有处理,事件回传给ViewGroup(黄色框中的日志),它的onTouchEvent返回true,代表事件被他消耗了,后续的事件也将分发给他处理。
image
当onInterceptTouchEvent返回true的日志信息
该方法如果返回为true,表示拦截,事件将交给TestFrameLayout自身的onTouchEvent处理(也就是最后事件消耗或者不消耗,都要看ViewGroup的onTouchEvent怎么处理了)。从下的日志信息中我们看到TestView的onTouchEvent方法并没有调用可以看出。
image
dispatchTouchEvent 返回true日志
注:下面给出的是ViewGroup的日志信息,View的该返回返回的信息和这个差不多,他们两个区别是View将事件传递给ViewGroup的dispatchTouchEvent,而ViewGroup将事件传递给的是Activity的dispatchTouchEvent方法中。因此由于篇幅原因,这里就粘出View的日志信息了。
如果dispatchTouchEvent返回true则代表事件被消耗,事件将不会在向下分发。后续事件将交给他处理。(View同理)
image
dispatchTouchEvent 返回false
如果dispatchTouchEvent返回false,事件也将不会在向下分发,事件将会回传给他上级的onTouchEvent方法中。后续事件将交给他的上级处理处理。(View同理)
image终结
想要真正搞清楚事件分发,还是要亲自写个demo测试才能容易搞明白,这篇文章个人感觉写的还是有点乱(表达水平有限,嘿嘿)!如果看完了这篇文章不知道在说什么,可以看一看下面的两个链接,这两个链接算是我看过的讲事件分发的文章中写得计较好两个。推荐先看《图解 Android 事件分发机制》,一定要将作者画的流程图搞清楚。