概述
View的Touch事件分发大致分为两个过程:
- 从上向下的分发过程,调用的是dispatchTouchEvent()。
- 从下向上的处理过程,调用的是onTouchEvent()。
相关概念
Touch事件分发涉及到几个相关概念
事件链
这里涉及到事件链的概念,事件链是指从MotionEvent.ACTION_DOWN
开始,到MotionEvent.ACTION_UP
之间的一系列Touch事件,ACTION_DOWN
和ACTION_UP
之间可能没有其他事件,也可能包含ACTION_MOVE
,ACTION_CANCEL
,ACTION_POINTER_DOWN
,ACTION_POINTER_UP
等。
事件消费
如果一个View对某个Touch事件感兴趣,并且它的onTouchEvent()返回了true,则称为该View消费了此次Touch事件。并且,一次Touch事件只会被一个View消费。事件消费与ACTION_DOWN
联系紧密,在一个事件链中,只有消费了ACTION_DOWN
事件的View才会有机会在onTouchEvent()
中响应后续的事件。
详细流程
主要关心三个函数:
/* 从最顶层的ViewGroup开始调用,发起Touch事件的分发过程,返回true表示该ViewGroup处理了此次事件 */
boolean dispatchTouchEvent(MotionEvent ev);
/* 判断是否拦截此次Touch,返回true表示拦截,即不再将此次事件像子View传递 */
boolean onInterceptTouchEvent(MotionEvent ev);
/* 对Touch事件的处理业务,返回true表示消费此次事件 */
boolean onTouchEvent(MotionEvent ev);
处理流程:
- 所有事件链都是从
ACTION_DOWN
开始的,ViewGroup
收到ACTION_DOWN
后,会触发dispatchTouchEvent()
发起分发过程。这会调用onInterceptTouchEvent()
,如果后者返回值为false
,即不拦截,则接下来依次调用子View
的dispatchTouchEvent()
方法;如果返回值为true
,则跳过遍历子View
的步骤。 - 如果没有被拦截,dispatch会一直进行到最下面一层的
View
,然后沿着刚刚dispatch的顺序,反向依次调用各个View
的onTouchEvent()
;如果中途被拦截,即某个View
的dispatchTouchEvent()
返回了false
,则从拦截的View
开始,沿着dispatch的顺序,反向依次调用各个View
的onTouchEvent()
。 - 依次调用
onTouchEvent()
的过程中, 如果某个View
的onTouchEvent()
返回true
,即该View
对此次Touch事件感兴趣,则该View
会被记录到此次事件链的mFirstTouch
中,并且结束此次Touch事件的处理,不再继续调用父View
的onTouchEvent()
。如果所有View的onTouchEvent()都返回了false,则结束事件处理时mFirstTouch
为null
。 - 前面3步讲的都是
ACTION_DOWN
,下面开始处理事件链的后续Touch事件。当后续事件到来时(可能是除了ACTION_DOWN
以外的任何事件),依然触发dispatchTouchEvent()
,但处理逻辑不同,ViewGroup会判断mFirstTouchTarget
是否为null
,也就是判断以自己为根的View树中是否有人消费了之前的ACTION_DOWN
,如果有人消费,则依次向下dispatch,直到找到消费了ACTION_DOWN
事件的View
,调用它的onTouchEvent()
处理。如果没人消费,直接返回,结束此次分发。
参考文献
http://wugengxin.cn/download/pdf/android/PRE_andevcon_mastering-the-android-touch-system.pdf