拖了那么久,终于要把Android事件分发机制整理一遍了。
【注】:这篇文章中的内容都以这张图来讲解分发机制,其中A、B、C都是ViewGroup,它们的层次关系为:A为根布局,B为二级子布局,C为三级子布局,其中C布局中包含一个Button按钮,即A包含B,B包含C,C包含Button。
好了,废话少说。先来讲下今天的三位主角吧。
1、dispatchTouchEvent - 分发事件,默认为false。true:取消事件,不继续向下分发,false:向下分发事件
2、onInterceptTouchEvent - 拦截事件,默认为false。true:拦截事件,自身的onTouchEvent()方法消费,false:事件继续向下传递
3、onTouchEvent - 处理事件,默认为false,true:消费事件,false:不消费事件,向上层传递让上层处理。【注】如果发生了拦截,那么如果该层不处理则会继续向上传递,让上层处理,如果过程中没有发生拦截,则事件将到达底部那层,这时将返回true由底部那层处理、消费事件,也就是说在整个过程中如果没有发生拦截,则到达底部或者顶部时候,将强制最后的一层处理该事件。【注】如果在设置了setOnClickListener(…)的View或Viewgroup中,返回true则消费事件,不会触发onClick事件,如果返回false,则会触发onClick事件
上面这三个方法就是负责Android中当用户触摸屏幕时事件的分发与处理。在Android中,事件的分发是遵循这样一套机制的:当用户触摸到屏幕时,也就是触摸到Activity界面,当Activity中的dispatchTouchEvent()方法允许分发时,这时这个触摸事件就会先出现在根布局这个ViewGroup中,然后再向里层的ViewGroup或View传递,也就是Activity->RootView->子ViewGroup->…..->View。
如图:
下面我们通过几个示例来看看事件分发到底是怎样的?
我们按最上面那个图布局好后,然后分别实现A、B、C中的dispatchTouchEvent()、onInterceptTouchEvent()、onTouchEvent()三个方法,然后实现Button中的dispatchTouchEvent()、onTouchEvent()两个方法,再用Log分别在各自的方法以这种形式打印出来: Log.v(“zxy”,”C—–>onInterceptTouchEvent”);**
【注】:只有ViewGroup中才有onInterceptTouchEvent()拦截事件方法,View中只有分发事件和处理事件这个两个方法**。
在此之前,要先说个概念,我们知道用户触摸到屏幕时候,会触发一系列事件,顺序为:Down->(Move….)->Up,这里为什么要给Move打括号呢?因为在此过程中,有可能用户只点击然后抬起手指,有可能点击后又移动了多次,所以Move的次数是不确定的,但是只要触摸到屏幕,一定会有Down和Up事件,这里我们假设在点击过程中不移动手指,所以只产生Down和Up两个事件。
所以触摸一次既然有两个事件Down->Up:
1、Up的分发也是取决于Down是否分发,如果Down事件在B层的dispatchTouchEvent()方法返回true,也即取消事件停止分发,那么后续的Move事件、Up事件也只能到达B层,并在B层取消分发。假如我们在A层中的dispatchTouchEvent()方法返回true,也就是取消事件停止分发,那么Down事件会在A层中取消事件,Up事件也会在A层中取消事件(假设我们手指不移动只产生down和up两个事件),所以Log只有Down和Up两个事件并都在A层取消分发,Log如下:
-------------------Down
08-17 21:39:01.447 2661-2661/? V/zxy﹕ Activity----->dispatchTouchEvent
08-17 21:39:01.447 2661-2661/? V/zxy﹕ A----->dispatchTouchEvent
-------------------Up
08-17 21:39:01.540 2661-2661/? V/zxy﹕ Activity----->dispatchTouchEvent
08-17 21:39:01.540 2661-2661/? V/zxy﹕ A----->dispatchTouchEvent
2、Down事件无论在哪一层中被拦截即onInterceptTouchEvent()返回true,那么后续的Move事件、Up事件都不会被下发,都只能在Activity层处理。假如我们在A层中onInterceptTouchEvent()返回true,我们可以看打印的Log中Down事件的分发过程,其余事件都停留在Activity层,Log如下:
-------------------Down
08-17 21:28:28.510 2557-2557/? V/zxy﹕ Activity----->dispatchTouchEvent
08-17 21:28:28.510 2557-2557/? V/zxy﹕ A----->dispatchTouchEvent
08-17 21:28:28.510 2557-2557/? V/zxy﹕ A----->onInterceptTouchEvent
08-17 21:28:28.510 2557-2557/? V/zxy﹕ A----->onTouchEvent
08-17 21:28:28.510 2557-2557/? V/zxy﹕ Activity----->onTouchEvent
------------------Up
08-17 21:28:28.596 2557-2557/? V/zxy﹕ Activity----->dispatchTouchEvent
08-17 21:28:28.597 2557-2557/? V/zxy﹕ Activity----->onTouchEvent
所以,我们清楚上面的概念后,下面的示例就比较好懂了
在默认情况下,我们分别在那几个方法中用Log打印,看看点击Button后事件传递的流程和各个方法执行的情况(假设手指没有移动),Log如下:
-------------------Down
08-17 21:58:03.585 2810-2810/? V/zxy﹕ Activity----->dispatchTouchEvent
08-17 21:58:03.585 2810-2810/? V/zxy﹕ A----->dispatchTouchEvent
08-17 21:58:03.585 2810-2810/? V/zxy﹕ A----->onInterceptTouchEvent
08-17 21:58:03.585 2810-2810/? V/zxy﹕ B----->dispatchTouchEvent
08-17 21:58:03.585 2810-2810/? V/zxy﹕ B----->onInterceptTouchEvent
08-17 21:58:03.585 2810-2810/? V/zxy﹕ C----->dispatchTouchEvent
08-17 21:58:03.585 2810-2810/? V/zxy﹕ C----->onInterceptTouchEvent
08-17 21:58:03.585 2810-2810/? V/zxy﹕ Button----->dispatchTouchEvent
08-17 21:58:03.585 2810-2810/? V/zxy﹕ Button----->onTouchEvent
-------------------Up
08-17 21:58:03.677 2810-2810/? V/zxy﹕ Activity----->dispatchTouchEvent
08-17 21:58:03.677 2810-2810/? V/zxy﹕ A----->dispatchTouchEvent
08-17 21:58:03.677 2810-2810/? V/zxy﹕ A----->onInterceptTouchEvent
08-17 21:58:03.677 2810-2810/? V/zxy﹕ B----->dispatchTouchEvent
08-17 21:58:03.677 2810-2810/? V/zxy﹕ B----->onInterceptTouchEvent
08-17 21:58:03.677 2810-2810/? V/zxy﹕ C----->dispatchTouchEvent
08-17 21:58:03.677 2810-2810/? V/zxy﹕ C----->onInterceptTouchEvent
08-17 21:58:03.677 2810-2810/? V/zxy﹕ Button----->dispatchTouchEvent
08-17 21:58:03.677 2810-2810/? V/zxy﹕ Button----->onTouchEvent
我可以看出,点击中间的Button后,首先是调用Activity中的dispatchTouchEvent()方法,因为这个方法默认返回值为false,所以往下分发,然后到了ViewGroup-A,在A中又往下分发到达B,在B中又往下分发到达C,在C中又往下分发到达Button,然后Button中的onTouchEvent()默认情况下是处理事件消费事件,就不会往上层传递交给上层处理。
即在B层的dispatchTouchEvent()方法返回true
打印的Log为:
-------------------Down
08-17 22:10:16.872 2923-2923/? V/zxy﹕ Activity----->dispatchTouchEvent
08-17 22:10:16.873 2923-2923/? V/zxy﹕ A----->dispatchTouchEvent
08-17 22:10:16.873 2923-2923/? V/zxy﹕ A----->onInterceptTouchEvent
08-17 22:10:16.873 2923-2923/? V/zxy﹕ B----->dispatchTouchEvent
-------------------Up
08-17 22:10:16.974 2923-2923/? V/zxy﹕ Activity----->dispatchTouchEvent
08-17 22:10:16.974 2923-2923/? V/zxy﹕ A----->dispatchTouchEvent
08-17 22:10:16.974 2923-2923/? V/zxy﹕ A----->onInterceptTouchEvent
08-17 22:10:16.974 2923-2923/? V/zxy﹕ B----->dispatchTouchEvent
可以看到,在事件传递到B层后,事件就不能继续分发了,事件取消了
-------------------Down
08-17 22:22:29.376 3039-3039/? V/zxy﹕ Activity----->dispatchTouchEvent
08-17 22:22:29.377 3039-3039/? V/zxy﹕ A----->dispatchTouchEvent
08-17 22:22:29.377 3039-3039/? V/zxy﹕ A----->onInterceptTouchEvent
08-17 22:22:29.377 3039-3039/? V/zxy﹕ B----->dispatchTouchEvent
08-17 22:22:29.377 3039-3039/? V/zxy﹕ B----->onInterceptTouchEvent
08-17 22:22:29.377 3039-3039/? V/zxy﹕ B----->onTouchEvent
-------------------Up
08-17 22:22:30.309 3039-3039/? V/zxy﹕ Activity----->dispatchTouchEvent
08-17 22:22:30.309 3039-3039/? V/zxy﹕ A----->dispatchTouchEvent
08-17 22:22:30.309 3039-3039/? V/zxy﹕ A----->onInterceptTouchEvent
08-17 22:22:30.309 3039-3039/? V/zxy﹕ B----->dispatchTouchEvent
08-17 22:22:30.309 3039-3039/? V/zxy﹕ B----->onTouchEvent
可以看到,在B层如果拦截后,那么事件就到B层的onTouchEvent()方法中,同时消费该事件
首先,因为刚刚在上面讲了,无论在哪个层发生了事件拦截,只有Down事件才会到达该层,而Move、Up事件则将停留在Activity层处理。
又因为该层发生了拦截,所以onTouchEvent()默认返回为false,所以Log如下:
08-17 22:29:01.714 3211-3211/? V/zxy﹕ Activity----->dispatchTouchEvent
08-17 22:29:01.715 3211-3211/? V/zxy﹕ A----->dispatchTouchEvent
08-17 22:29:01.715 3211-3211/? V/zxy﹕ A----->onInterceptTouchEvent
08-17 22:29:01.715 3211-3211/? V/zxy﹕ B----->dispatchTouchEvent
08-17 22:29:01.715 3211-3211/? V/zxy﹕ B----->onInterceptTouchEvent
08-17 22:29:01.715 3211-3211/? V/zxy﹕ B----->onTouchEvent
08-17 22:29:01.715 3211-3211/? V/zxy﹕ A----->onTouchEvent
08-17 22:29:01.715 3211-3211/? V/zxy﹕ Activity----->onTouchEvent
------------------Up
08-17 22:29:01.802 3211-3211/? V/zxy﹕ Activity----->dispatchTouchEvent
08-17 22:29:01.802 3211-3211/? V/zxy﹕ Activity----->onTouchEvent
从Log中可以看出,在B发生拦截后,该层如果不处理该事件,则会向上一层A传递,同时A默认返回为false,即不处理,再继续向上一层Activity传递,这时候到达顶层了,没有谁可以传递了,就强制在顶层消费该事件。同时也可以看到无论哪一层发生拦截后Up事件只能停留在Activity层。
Log为:
因为Button层的onTouchEvent()方法不消费该事件,所以将往上层的父ViewGroup传递,直到消费事件为止
------------------Down
08-17 23:12:31.004 3659-3659/? V/zxy﹕ Activity----->dispatchTouchEvent
08-17 23:12:31.004 3659-3659/? V/zxy﹕ A----->dispatchTouchEvent
08-17 23:12:31.004 3659-3659/? V/zxy﹕ A----->onInterceptTouchEvent
08-17 23:12:31.004 3659-3659/? V/zxy﹕ B----->dispatchTouchEvent
08-17 23:12:31.004 3659-3659/? V/zxy﹕ B----->onInterceptTouchEvent
08-17 23:12:31.004 3659-3659/? V/zxy﹕ C----->dispatchTouchEvent
08-17 23:12:31.004 3659-3659/? V/zxy﹕ C----->onInterceptTouchEvent
08-17 23:12:31.004 3659-3659/? V/zxy﹕ Button----->dispatchTouchEvent
08-17 23:12:31.004 3659-3659/? V/zxy﹕ Button----->onTouchEvent
08-17 23:12:31.004 3659-3659/? V/zxy﹕ C----->onTouchEvent
08-17 23:12:31.004 3659-3659/? V/zxy﹕ B----->onTouchEvent
08-17 23:12:31.004 3659-3659/? V/zxy﹕ A----->onTouchEvent
08-17 23:12:31.004 3659-3659/? V/zxy﹕ Activity----->onTouchEvent
------------------Up
08-17 23:12:31.137 3659-3659/? V/zxy﹕ Activity----->dispatchTouchEvent
08-17 23:12:31.137 3659-3659/? V/zxy﹕ Activity----->onTouchEvent
这里为什么Up事件一直在Activity层呢?因为Down事件分发过程中,最终事件是在Activity层处理,所以,Down后续的事件Move、Up也将在Activity中处理。
------------------Down
08-17 23:18:07.619 3783-3783/? V/zxy﹕ Activity----->dispatchTouchEvent
08-17 23:18:07.620 3783-3783/? V/zxy﹕ A----->dispatchTouchEvent
08-17 23:18:07.620 3783-3783/? V/zxy﹕ A----->onInterceptTouchEvent
08-17 23:18:07.620 3783-3783/? V/zxy﹕ B----->dispatchTouchEvent
08-17 23:18:07.620 3783-3783/? V/zxy﹕ B----->onInterceptTouchEvent
08-17 23:18:07.620 3783-3783/? V/zxy﹕ C----->dispatchTouchEvent
08-17 23:18:07.620 3783-3783/? V/zxy﹕ C----->onInterceptTouchEvent
08-17 23:18:07.620 3783-3783/? V/zxy﹕ Button----->dispatchTouchEvent
08-17 23:18:07.620 3783-3783/? V/zxy﹕ Button----->onTouchEvent
------------------Up
08-17 23:18:07.728 3783-3783/? V/zxy﹕ Activity----->dispatchTouchEvent
08-17 23:18:07.728 3783-3783/? V/zxy﹕ A----->dispatchTouchEvent
08-17 23:18:07.728 3783-3783/? V/zxy﹕ A----->onInterceptTouchEvent
08-17 23:18:07.728 3783-3783/? V/zxy﹕ B----->dispatchTouchEvent
08-17 23:18:07.728 3783-3783/? V/zxy﹕ B----->onInterceptTouchEvent
08-17 23:18:07.729 3783-3783/? V/zxy﹕ C----->dispatchTouchEvent
08-17 23:18:07.729 3783-3783/? V/zxy﹕ C----->onInterceptTouchEvent
08-17 23:18:07.729 3783-3783/? V/zxy﹕ Button----->dispatchTouchEvent
08-17 23:18:07.729 3783-3783/? V/zxy﹕ Button----->onTouchEvent
从Log中可以看出,在Button层消费了该事件后,将不再往上层传递
因为我们想让这个Down事件在C层就处理完,所以在C层我们就拦截该事件设置onInterceptTouchEvent()返回值为true,并在C层onTouchEvent()中返回true消费事件。Log为:
-------------------Down
08-17 23:29:06.605 3979-3979/? V/zxy﹕ Activity----->dispatchTouchEvent
08-17 23:29:06.606 3979-3979/? V/zxy﹕ A----->dispatchTouchEvent
08-17 23:29:06.606 3979-3979/? V/zxy﹕ A----->onInterceptTouchEvent
08-17 23:29:06.606 3979-3979/? V/zxy﹕ B----->dispatchTouchEvent
08-17 23:29:06.606 3979-3979/? V/zxy﹕ B----->onInterceptTouchEvent
08-17 23:29:06.606 3979-3979/? V/zxy﹕ C----->dispatchTouchEvent
08-17 23:29:06.606 3979-3979/? V/zxy﹕ C----->onInterceptTouchEvent
08-17 23:29:06.606 3979-3979/? V/zxy﹕ C----->onTouchEvent
------------------Up
08-17 23:29:06.705 3979-3979/? V/zxy﹕ Activity----->dispatchTouchEvent
08-17 23:29:06.706 3979-3979/? V/zxy﹕ A----->dispatchTouchEvent
08-17 23:29:06.706 3979-3979/? V/zxy﹕ A----->onInterceptTouchEvent
08-17 23:29:06.706 3979-3979/? V/zxy﹕ B----->dispatchTouchEvent
08-17 23:29:06.706 3979-3979/? V/zxy﹕ B----->onInterceptTouchEvent
08-17 23:29:06.706 3979-3979/? V/zxy﹕ C----->dispatchTouchEvent
08-17 23:29:06.706 3979-3979/? V/zxy﹕ C----->onTouchEvent
可以看到Down事件和Up事件在C层消费掉了,假如我们也设置了Button的onClickListener(…)方法,那么该onClick方法将不执行,因为事件在C层已经消费掉了
好了,Android的事件分发机制就讲完了,下面来梳理一下整个过程:
【注】:事件分发中,处理消费事件的方法onTouchEvent()只执行在两种情况下:
1、事件分发过程中没有事件拦截,到达最底层执行onTouchEvent方法消费事件。
2、事件分发过程中有事件拦截,则在发生事件拦截的这一层会执行onTouchEvent方法,把事件交给它处理,如果返回true则消费事件,如果为false它不消费事件,则往上一层的onTouchEvent方法中传递,如果上一层也不消费,则继续向上传递,当到达最顶层Activity,会执行Activity的onTouchEvent方法消费事件。
事件分发的流程:Activity——>ViewGroup1——>子ViewGroup2——>…——>子View
事件消费的流程:
1、onTouchEvent返回true:在该层消费
2、onTouchEvent返回false:子View或子ViewGroup——>…——>ViewGroup1——>Activity
版权声明:本文为博主原创文章,未经博主允许不得转载。转载注明出处:http://blog.csdn.net/u010687392
原文地址:http://blog.csdn.net/u010687392/article/details/47730955