前言
1. Android 事件分发流
⚠️:图分为三层,从上到下依次是 Activity、ViewGroup、View
事件从左上角那个白色箭头开始,由 Activity 的 dispatchTouchEvent 做分发
箭头的上面字代表方法返回值(return false、return true、return super.xxxx()),super 的意思是调用父类实现。
dispatchTouchEvent 和 onTouchEvent的框里有个【true---->消费】的字,表示的意思是如果方法返回true,那么代表事件就此消费,不会继续往别的地方传了,事件终止。
目前所有的图说明的事件是针对 ACTION_DOWN 的,对于 ACTION_MOVE 和 ACTION_UP ,当dispatchTouchEvent在进行事件分发的时候,只有前一个事件(如ACTION_DOWN)返回true,才会收到ACTION_MOVE和ACTION_UP的事件。
仔细看整个图,我们得出事件流 走向的几个结论:
- 如果事件不被中断,整个事件流向是一个类U型图,我们来看下这张图,可能更能理解U型图的意思。
所以如果我们没有对控件里面的方法进行重写或更改返回值,而直接用 super 调用父类的默认实现,那么整个事件流向应该是从Activity---->ViewGroup--->View 从上往下调用 dispatchTouchEvent 方法,一直到叶子节点(View)的时候,再由 View--->ViewGroup--->Activity 从下往上调用 onTouchEvent 方法。
- dispatchTouchEvent 和 onTouchEvent 一旦return true,事件就停止传递了(到达终点)。看下图中只要 return true 事件就没再继续传下去了,对于 return true 我们经常说事件被消费了,消费了的意思就是事件走到这里就是终点,不会往下传,没有谁能再收到这个事件了。
- dispatchTouchEvent 和 onTouchEvent return false 的时候事件都回传给父控件的 onTouchEvent 处理。
看上图深蓝色的线,对于返回 false 的情况,事件都是传给父控件 onTouchEvent 处理。
⚠️:对于 dispatchTouchEvent 返回 false 的含义应该是:事件停止往子 View 传递和分发同时开始往父控件回溯(父控件的 onTouchEvent 开始从下往上回传直到某个 onTouchEvent return true),事件分发机制就像递归,return false 的意义就是递归停止然后开始回溯。
对于 onTouchEvent return false 就比较简单了,它就是不消费事件,并让事件继续往父控件的方向从下往上流动。
- dispatchTouchEvent、onTouchEvent、onInterceptTouchEvent
ViewGroup 和 View 的这些方法的默认实现就是会让整个事件安装 U 型完整走完,所以 return super.xxxxxx() 就会让事件依照U型的方向的完整走完整个事件流动路径),中间不做任何改动,不回溯、不终止,每个环节都走到。
所以如果看到方法 return super.xxxxx() 那么事件的下一个流向就是走 U 型下一个目标,稍微记住上面这张图,你就能很快判断出下一个走向是哪个控件的哪个函数。
- onInterceptTouchEvent 的作用
Intercept 的意思就拦截,每个 ViewGroup 每次在做分发的时候,问一问拦截器要不要拦截(也就是问问自己这个事件要不要自己来处理)如果要自己处理那就在 onInterceptTouchEvent 方法中 return true 就会交给自己的 onTouchEvent 的处理,如果不拦截就是继续往子控件往下传。默认是不会去拦截的,因为子 View 也需要这个事件,所以 onInterceptTouchEvent 拦截器 return super.onInterceptTouchEvent() 和 return false 是一样的,是不会拦截的,事件会继续往子 View 的 dispatchTouchEvent 传递。
- ViewGroup 和 View 的 dispatchTouchEvent 方法返回 super.dispatchTouchEvent() 的时候事件流走向。
首先看下 ViewGroup 的 dispatchTouchEvent,之前说的 return true 是终结传递。return false 是回溯到父 View 的 onTouchEvent,然后 ViewGroup 怎样通过 dispatchTouchEvent 方法能把事件分发到自己的onTouchEvent处理呢?
return true 和 false 都不行,那么只能通过 Interceptor 把事件拦截下来给自己的 onTouchEvent,所以 ViewGroup dispatchTouchEvent 方法的 super 默认实现就是去调用 onInterceptTouchEvent,记住这一点。
那么对于 View 的 dispatchTouchEvent return super.dispatchTouchEvent() 的时候呢事件会传到哪里呢?
很遗憾 View 没有拦截器。但是同样的道理 return true 是终结。return false 是回溯会父类的 onTouchEvent,怎样把事件分发给自己的 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。
ViewGroup 和 View 的 dispatchTouchEvent 是做事件分发,那么这个事件可能分发出去的四个目标:
- 自己消费,终结传递。------->return true ;
- 给自己的 onTouchEvent 处理-------> 调用 super.dispatchTouchEvent() 系统默认会去调用 onInterceptTouchEvent,在 onInterceptTouchEvent return true 就会去把事件分给自己的 onTouchEvent 处理。
- 传给子 View------> 调用 super.dispatchTouchEvent() 默认实现会去调用 onInterceptTouchEvent 在 onInterceptTouchEvent return false,就会把事件传给子类。
- 不传给子 View,事件终止往下传递,事件开始回溯,从父 View 的 onTouchEvent 开始事件从下到上回归执行每个控件的 onTouchEvent------->return false;
⚠️ 注: 由于 View 没有子 View 所以不需要 onInterceptTouchEvent 来控件是否把事件传递给子 View 还是拦截,所以 View 的事件分发调用 super.dispatchTouchEvent() 的时候默认把事件传给自己的 onTouchEvent 处理(相当于拦截),对比 ViewGroup 的 dispatchTouchEvent 事件分发,View 的事件分发没有上面提到的 4 个目标的第 3 点。
申明:开始的图片来源网络,侵删