码迷,mamicode.com
首页 > 移动开发 > 详细

Android事件分发机制

时间:2017-07-18 10:13:54      阅读:274      评论:0      收藏:0      [点我收藏+]

标签:处理   data-   image   括号   技术   演示   article   包括   csdn   

【注】:这篇文章中的内容都以这张图来解说分发机制,当中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:不消费事件,向上层传递让上层处理。【注】假设发生了拦截。那么假设该层不处理则会继续向上传递,让上层处理。

假设过程中没有发生处理,则事件分发究竟层后将一直向上层传递至Activity。在Activity的onTouchEvent()中处理。

【注】假设在设置了setOnClickListener(…)的View或Viewgroup中,返回true则消费事件。会触发onClick事件。假设返回false,则不会触发onClick事件

这里借用网上的两张图片来添加理解:
1、在没有做不论什么处理,也即默认情况下,触摸屏幕发生的一系列事件分发过程:
技术分享
假设DOWN事件没有被消费。则兴许的MOVE/UP事件将不会传递过来。直接在Activity层处理

2、假设子View消费了事件,则事件的分发过程为:
技术分享

上面这三个方法就是负责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中仅仅有分发事件和处理事件这个两个方法

在默认情况下,我们点击屏幕上的Button

在此之前。要先说个概念,我们知道用户触摸到屏幕时候,会触发一系列事件。顺序为: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()消费事件,可能这里会有疑问?这里明明是默认情况下,没有发生拦截和onTouchEvent没有指定返回ture,怎么在Button那里就消费了呢?原因是像Button、ImageButton那样可点击的View在默认情况下onTouchEvent是返回ture的表示默认消费事件。而其他默认情况下不可点击的View或ViewGroup则返回false表示默认不消费事件。所以到达Button这层消费了事件就不会往上层传递了。

在B层中取消事件分发

即在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层后,事件就不能继续分发了,事件取消了

在B层拦截事件

在B层拦截事件后onTouchEvent()返回true,消费事件

-------------------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()方法中,同一时候消费该事件

在B层拦截事件后onTouchEvent()返回false。不消费事件,传递给上层处理

首先。由于刚刚在上面讲了,不管在哪个层发生了事件拦截。仅仅有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传递,这时候到达顶层了,没有谁能够传递了。就在Activity中消费该事件。同一时候也能够看到不管哪一层发生拦截后Up事件仅仅能停留在Activity层。

其他情况都默认,当事件到达Button层onTouchEvent()事件的处理

不消费事件。onTouchEvent()返回false

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事件分发过程中,假设View或ViewGroup没有对ACTION_DOWN事件进行消费,之后的其他事件也就不会传递过来了。

消费事件,onTouchEvent()返回true(默认情况下就会消费事件)

------------------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层消费了该事件后,将不再往上层传递

假设点击Button后。我们想让这个Down事件在C层处理完

由于我们想让这个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方法消费事件,假设事件分发过程中没有事件拦截且最底层也没有消费该事件,则事件会向上层传递,假设一直没有ViewGroup消费事件则会在Activity中onTouchEvent()方法中消费。

2、事件分发过程中有事件拦截,则在发生事件拦截的这一层会运行onTouchEvent方法。把事件交给它处理,假设返回true则消费事件。假设为false它不消费事件,则往上一层的onTouchEvent方法中传递,假设上一层也不消费。则继续向上传递。当到达最顶层Activity,会运行Activity的onTouchEvent方法消费事件。

3、假设View或ViewGroup没有对 ACTION_DOWN 进行消费。之后的其他事件不会传递过来(也就在Activity层处理)

4、对于底层的View来说。有一种方法能够阻止父层的View截获touch事件,就是调用getParent().requestDisallowInterceptTouchEvent(true);方法。

一旦底层View收到touch的action后调用这种方法那么父层View就不会再调用onInterceptTouchEvent了。也无法截获以后的action

5、像Button、ImageButton那样可点击的View在默认情况下onTouchEvent是返回ture的表示默认消费事件,而其他默认情况下不可点击的View或ViewGroup则返回false表示默认不消费事件


事件分发的流程:Activity——>ViewGroup1——>子ViewGroup2——>…——>子View
事件消费的流程
1、onTouchEvent返回true:在该层消费
2、onTouchEvent返回false:子View或子ViewGroup——>…——>ViewGroup1——>Activity


測试demo

Android事件分发机制

标签:处理   data-   image   括号   技术   演示   article   包括   csdn   

原文地址:http://www.cnblogs.com/jhcelue/p/7198534.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!