Android View事件分发与传递
在Android中,人们主要通过手指与系统交互。Android把所有的touch事件都被封装成MotionEvent来进行处理,其中包括了手指点击的位置,时间等信息。其事件类型主要包括:ACTION_DOWN,ACTION_UP,ACTION_MOVE, ACTION_POINTER_DOWN, ACTION_POINTER_UP,ACTION_CANCEL。
这些事件是有触发顺序的,下面举两个栗子:
- 只有一个手指点击在view范围内,依次触发的事件:手指按下ACTION_DOWN→手指移动ACTION_MOVE→手指抬起ACTION_UP
- 多个手指点击在view范围内,依次触发的事件:第一根手指按下ACTION_DOWN→随后的手指按下ACTION_POINTER_DOWN→有手指抬起ACTION_POINTER_UP→最后一根手指抬起ACTION_UP。
为了处理这些事件,Android定义了三种方法:
- dispatchTouchEvent()函数:负责分发传递事件,通常不把逻辑处理放在该方法中,所以一般都return super.dispatchTouchEvent().
- onInterceptTouchEvent()函数:负责拦截事件
- onTouchEvent()函数和 OnTouchListener监听:负责消费和处理事件
事件传递流程
当一个事件被触发,事件的传递从Activity.dispatchTouchEvent()开始,一直从最外层的父view开始向里面的子view传递,直到被拦截。在传递过程中,view可以通过onInterceptTouchEvent()函数进行拦截,一旦父view拦截了该事件,则不再向下传递。
如果被触发的事件被传递至最内层的view,一直未被拦截消费,则会反向向外传递,这时候父view可以通过onTouchEvent函数对事件进行消费,直到activity。另外,如果被触发的ACTION_DOWN事件在某一层未被消费,那么接下来的事件是无法被传递进来的。
下面我们来看几个例子:
Case 1 手指点击在view上,父View(ViewGroup)和View都不消费事件,最终返回给activity消费。
以ACTION_DOWN事件为例,ACTION_DOWN事件沿着图中黑色箭头逐层传递。在开发过程中,我们通常会为view或者viewGroup设置监听器来捕获view事件,listener的onTouch方法会在onTouchEvent之前执行。在分发传递过程中,无论哪个方法返回true,都表示传递停止;如果返回false,则表示继续传递下去。
从图上可以看出,ACTION_DOWN事件经过activity—>ViewGroup—>View—>ViewGroup—>activity一直未被ViewGroup和View消费处理,又回到了activity中。那么随后的ACTION_MOVE和ACTION_UP事件只会沿着绿色箭头传递,不再沿着ACTION_DOWN事件的路线传递了。
Case 2 手指点击在父View和子View之间的空隙中,并未点击在子View上。同上一个例子一样,ViewGroup中并未消费该事件,返回给了activity。
这里用户并未点击到ViewGroup中的view,而是点击在了ViewGroup和View之间的空隙处。同上一个例子相同,ViewGroup并未处理ACTION_DOWN事件,而是返回给了activity处理。
Case 3 单击view后,在view中处理消费了ACTION_DOWN事件。
从图上可以看到,ACTION_DOWN事件沿着黑色箭头方向逐层向内部传递,直到在view的onTouchEvent中消费了该事件,并返回true表示不再向下传递。随后的事件ACTION_MOVE和ACTION_UP将会沿着绿色箭头传递,直到到达view的onTouchEvent方法。
Case 4 单击view,但是并不在view中处理ACTION_DOWN事件,而是让它传递到ViewGroup中处理。
ACTION_DOWN事件经过ViewGroup,ViewGroup并未拦截而是传递给View。View并未消费该事件而是回传给了ViewGroup,在ViewGroup中消费该事件。
Case 5 单击view,在ViewGroup中拦截ACTION_DOWN事件并消费
ACTION_DOWN事件沿着黑色箭头分发传递,在ViewGroup中对它进行拦截(返回true),所以就不再继续向view传递ACTION_DOWN事件了。然后在ViewGroup的onTouchEvent方法中消费了该事件,并返回true表示事件已消费。随后的ACTION_MOVE和ACTION_UP事件将沿着绿色箭头传递,直抵最后的消费方法onTouchEvent,而并不经过拦截事件。