View事件分发

1. 基础知识

(1) 所有 Touch事件都被封装成 MotionEvent 对象,包括 Touch的位置、时间、历史记录以及第几个手指(多指触摸)等。

(2) 事件类型分为: ACTION_DOWN,ACTION_UP,ACTION_MOVE,ACTION_POINTER_DOWN,
ACTION_POINTER_UP,ACTION_CANCEL,每个事件都是以ACTION_DOWN开始,ACTION_UP结束的。
(3) 对事件的处理包括三类:

  • 传递——dispatchTouchEvent()
  • 拦截——onInterceptTouchEvent()
  • 消费——onTouchEvent()onTouchListener()onTouchListener()优先于 onTouchEvent()对事件进行消费。

2. 图解Android 事件分发机制

1. Android 事件分发流

8c3c87fa4a51390188c96941df33e440.png

注意:

  • 仔细看的话,图分为3层,从上往下依次是Activity、ViewGroup、View
  • 事件从左上角那个白色箭头开始,由Activity的dispatchTouchEvent做分发
  • 箭头的上面字代表方法返回值,(return true、return false、return super.xxxxx()),super 的意思是调用父类实现。
  • dispatchTouchEventonTouchEvent的框里有个【true—->消费】的字,表示的意思是如果方法返回true,那么代表事件就此消费,不会继续往别的地方传了,事件终止。
  • Activity 的dispatchTouchEvent 无论返回什么,都会调用ViewGroupdispatchTouchEvent(自行看源码)

2. 详细分析

1. 如果事件不被中断,整个事件流向是一个类U型图

8c3c87fa4a51390188c96941df33e440.png

所以如果我们没有对控件里面的方法进行重写或更改返回值,而直接用super调用父类的默认实现,那么整个事件流向应该是从Activity—->ViewGroup—>View 从上往下调用dispatchTouchEvent方法,一直到叶子节点(View)的时候,再由View—>ViewGroup—>Activity从下往上调用onTouchEvent方法

2. dispatchTouchEventonTouchEvent 一旦return true,事件就停止传递了(到达终点)(没有谁能再收到这个事件)

看下图中只要return true事件就没再继续传下去了,对于return true我们经常说事件被消费了,消费了的意思就是事件走到这里就是终点,不会往下传,没有谁能再收到这个事件了。

20160718094855_87.png

3. dispatchTouchEventonTouchEvent return false 的时候事件都回传给父控件的onTouchEvent处理。

e942905c28c693d853cbf22ccd758a1f.jpg

看上图深蓝色的线,对于返回false的情况,事件都是传给父控件onTouchEvent处理。

  • 对于dispatchTouchEvent return false含义应该是:事件停止往子View传递和分发同时开始往父控件回溯(父控件的onTouchEvent开始从下往上回传直到某个onTouchEvent return true),事件分发机制就像递归,return false 的意义就是递归停止然后开始回溯
  • 对于onTouchEvent return false 就比较简单了,它就是不消费事件,并让事件继续往父控件的方向从下往上流动。

4. onInterceptTouchEvent 的作用

20160718094856_363.png

Intercept 的意思就拦截,每个ViewGroup每次在做分发的时候,问一问拦截器要不要拦截(也就是问问自己这个事件要不要自己来处理)

  • 如果要自己处理那就在onInterceptTouchEvent()方法中 return true 就会交给自己的onTouchEvent()的处理
  • 如果不拦截就是继续往子控件往下传。默认是不会去拦截的,因为子View也需要这个事件,所以onInterceptTouchEvent()拦截器return super.onInterceptTouchEvent()return false是一样的,是不会拦截的,事件会继续往子View的dispatchTouchEvent()传递。

5. ViewGroup 和View 的dispatchTouchEvent方法返回super.dispatchTouchEvent()的时候事件流走向。

ViewGroup 的dispatchTouchEvent():

  • return true 是终结传递。
  • return false 是回溯到父View的 onTouchEvent()
  • ViewGroup 通过 super() 方法调用 onInterceptTouchEvent(),从而把事件拦截下来给自己的onTouchEvent()

View 的 dispatchTouchEvent()

  • return true 是终结
  • return false 是回溯会父类的onTouchEvent()
  • return super.dispatchTouchEvent(),View类的dispatchTouchEvent()方法默认实现就是能帮你调用View自己的onTouchEvent()方法的。

总结一下

  • 对于 dispatchTouchEvent()onTouchEvent()return true 是终结事件传递。return false 是回溯到父View的onTouchEvent()方法。
  • ViewGroup 想把自己分发给自己的onTouchEvent(),需要拦截器onInterceptTouchEvent()方法return true 把事件拦截下来。
  • ViewGroup 的拦截器onInterceptTouchEvent() 默认是不拦截的,所以return super.onInterceptTouchEvent()=return false
  • View 没有拦截器,为了让View可以把事件分发给自己的onTouchEvent(),View的dispatchTouchEvent()默认实现(super)就是把事件分发给自己的onTouchEvent()

3. ACTION_DOWN 事件传递总结

  1. ViewGroup和View 的dispatchTouchEvent 是做事件分发,那么这个事件可能分发出去的四个目标

    注:——> 后面代表事件目标需要怎么做。

    1. 自己消费,终结传递。——->return true ;
    2. 给自己的onTouchEvent处理——-> 调用super.dispatchTouchEvent()系统默认会去调用 onInterceptTouchEvent,在onInterceptTouchEvent return true就会去把事件分给自己的onTouchEvent处理。
    3. 传给子View——>调用super.dispatchTouchEvent()默认实现会去调用 onInterceptTouchEvent 在onInterceptTouchEvent return false,就会把事件传给子类。
    4. 不传给子View,事件终止往下传递,事件开始回溯,从父View的onTouchEvent开始事件从下到上回归执行每个控件的onTouchEvent——->return false;

      注: 由于View没有子View所以不需要onInterceptTouchEvent 来控件是否把事件传递给子View还是拦截,所以View的事件分发调用super.dispatchTouchEvent()的时候默认把事件传给自己的onTouchEvent处理(相当于拦截),对比ViewGroup的dispatchTouchEvent 事件分发,View的事件分发没有上面提到的4个目标的第3点。

  2. ViewGroup和View的onTouchEvent方法是做事件处理的,那么这个事件只能有两个处理方式:

    1. 自己消费掉,事件终结,不再传给谁—–>return true;
    2. 继续从下往上传,不消费事件,让父View也能收到到这个事件—–>return false;View的默认实现是不消费的。所以super==false。
  3. ViewGroup的onInterceptTouchEvent方法对于事件有两种情况:

    1. 拦截下来,给自己的onTouchEvent处理—>return true;
    2. 不拦截,把事件往下传给子View—->return false,ViewGroup默认是不拦截的,所以super==false;

4. 关于 ACTION_MOVE 和 ACTION_UP

  1. 简单的说,就是当dispatchTouchEvent()在进行事件分发的时候,只有前一个事件(如ACTION_DOWN)返回true,才会收到ACTION_MOVE和ACTION_UP的事件。
  2. 如果在某个控件的dispatchTouchEvent() 返回true消费终结事件,那么收到ACTION_DOWN 的dispatchTouchEvent()函数也能收到 ACTION_MOVE和ACTION_UP。
  3. 对于在onTouchEvent消费事件的情况:在哪个View的onTouchEvent 返回true,那么ACTION_MOVE和ACTION_UP的事件从上往下传到这个View后就不再往下传递了,而直接传给自己的onTouchEvent 并结束本次事件传递过程。

[参考文献]