1 完整的事件传递流程
下沉
事件从上到下(即从父级到子级),依次通过各个控件的dispatchTouchEvent方法:
- 在dispatchTouchEvent方法中会调用onInterceptTouchEvent方法判断是否要拦截,如果拦截(onInterceptTouchEvent返回true),则将事件交给同一控件的onTouchEvent方法,如果不拦截,则将事件继续传递给子控件的dispatchTouchEvent。
- 如果所有的父控件都没有拦截,则事件会一路往下传递直到最底层控件的dispatchTouchEvent,此dispatchTouchEvent也会先将事件传递给onInterceptTouchEvent(假设最底层控件是一个ViewGroup),但不管onInterceptTouchEvent是否拦截,事件都会被传递给同一控件的onTouchEvent,因为此控件是最底层的控件,往下已没有子控件了。(如果最底层控件是一个View,因为View没有onInterceptTouchEvent方法,事件会被直接传递给onTouchEvent)。
- 这一过程可以形象地称之为“下沉”。
上浮
一旦某一控件的onTouchEvent获取到事件(不管是因为此控件是最底层的控件而获取到,还是因为事件被同一控件的onInterceptTouchEvent拦截而获取到),那么事件从上到下传递的流程就结束了,接下来,事件会从此onTouchEvent方法开始,从下往上依次传递给每个父控件的onTouchEvent方法————除非事件在某个onTouchEvent中被消耗掉了(onTouchEvent返回true)。这一过程可以形象地称之为“上浮”。
由前面的知识我们知道,dispatchTouchEvent是负责事件分发的方法,事件的下沉就是通过调用dispatchTouchEvent方法来实现的,因此,事件的下沉过程中所经历的每一个控件,其dispatchTouchEvent方法都会被调用;而在事件上浮的过程中,任何控件的dispatchTouchEvent都不会被调用。在下面的论述中,为了简便,我们会省略对dispatchTouchEvent调用的描述。
2 简化的事件传递流程
将事件分为两种:down事件和后续事件(主要指move、up)。只有down事件会走上述的完整事件传递流程,后续事件走的都是简化的事件传递流程。
我们将“事件只向下传递到控件M为止,而不会继续往下传递”的现象称为“事件在M处被截断”。
假设有几个控件从上到下依次为(这里及以后的“上下”都是指的“父子”关系,而并非位置的上下关系):
...
A
B
C
...
结论1
对于某控件B,onTouchEvent获取到down事件后(不管此down事件是B的onInterceptTouchEvent拦截到的,还是因其子控件未消耗而上浮上来的,或是因为B是最底层的控件),只要B消耗了down事件(onTouchEvent返回true),那么同一事件序列的后续事件(move和up)都将在B处被截断(即事件下沉过程截止到B,不会再继续往下传递),此外,后续事件也不再经过B的onInterceptTouchEvent。后续事件的下沉流程变为:从上到下依次经过B的各层父控件的onInterceptTouchEvent方法,如果父控件均不拦截,则将事件交给B的onTouchEvent。(但如果在B的dispatchTouchEvent中调用了requestDisallowInterceptTouchEvent(true),则后续事件的下沉流程将跳过所有父控件的onInterceptTouchEvent,直接传递给B的onTouchEvent。另外需要注意:requestDisallowInterceptTouchEvent(true)不影响down事件下沉,只影响后续事件下沉。)
对于传递给B的onTouchEvent方法的后续事件,无论B是否消耗后续事件,后续事件的下沉流程都是不变的,而对于后续事件的上浮:
1.如果B消耗了某个后续事件,那么此后续事件就没有了,自然也就不会传递给其他任何控件。
2.如果某个后续事件没有被B消耗掉,那么此后续事件经过B的onTouchEvent后,会直接传递给Activity的onTouchEvent,而不会再经过B的任何父控件的onTouchEvent。
在如上所述的后续事件下沉流程中,如果B的父控件A突然拦截了某个后续事件,则无论A的onTouchEvent是否消耗后续事件,后续事件都将在A处被截断,并且后续事件也不会再经过A的onInterceptTouchEvent。后续事件的下沉流程变成:从上到下依次判断A的各层父控件是否拦截事件,如果父控件均不拦截,则直接将事件交给A的onTouchEvent。
结论2
如果在down事件的下沉和上浮过程中,任何控件都没有消耗down事件,那么down事件最终会上浮到Activity的onTouchEvent中。无论Activity的onTouchEvent是否消耗了down事件,此事件序列的后续事件都会直接传递给Activity的onTouchEvent,即事件在Activity处被截断了。
推论1
**由结论1、2很容易知道,影响后续事件下沉流程(或者说,后续事件在何处被截断)的因素只有两个:1.down事件在哪里被消耗;2.后续事件在哪里被拦截。而后续事件是否被消耗,在哪里被消耗,都不会影响后续事件的下沉流程。**具体来说:
- down事件:某个控件消耗了down事件(onTouchEvent返回true),则后续事件在此控件处被截断,并且此控件的onInterceptTouchEvent也不会再被调用。如果没有任何控件消耗down事件(其实最底层的控件通常默认都会消耗down),则后续事件将在Activity处被截断。
- 后续事件:如果某个控件拦截了后续事件(onInterceptTouchEvent返回true),则后续事件将在此控件处被截断(与此控件onTouchEvent的返回值无关),并且此控件的onInterceptTouchEvent也不会再被调用。
推论2(了解)
**由结论1、2很容易知道:如果B的onTouchEvent接收到down事件却没有消耗它,那么同一事件序列中的后续事件都不会再传递给B及B的子控件了。**因为,down事件必然:1.被B的某一级父控件给消耗掉了 或 2.被传递给了Activity,这必然导致后续事件在B的上方被截断。
推论3(了解)
无论B拦截了down事件还是后续事件(onInterceptTouchEvent返回true),则B的onInterceptTouchEvent都将无法再接收到此事件序列的后续事件:
B拦截了down事件:
- 若B的onTouchEvent消耗了down,则后续事件将只经过B的父控件的onInterceptTouchEvent和B的onTouchEvent。
- 若B的onTouchEvent没有消耗down,那么后续事件必然在B上方的某处被截断。
B拦截了后续事件:由结论1可知,后续事件将在B处被截断,并且B的onInterceptTouchEvent不会再被调用。