文档开发者中心
我的

Android事件分发机制(转载)

事件分发机制


事件分发机制是 Android 开发者的必备技能,关于这方面的博客也有很多,最近准备夯实一下自己的基础知识,整理一下零散的知识点,为突破做准备,所以再来梳理一下事件分发机制。

大多介绍事件分发机制的文章都只叙述了 View 与 ViewGroup 的事件分发机制,我这里将从 Activity 开始介绍。
一般来说,事件传递的顺序是:Activity -> ViewGroup -> View。

那么我们先说 Activity。

Activity 中的事件分发机制

Activity 中包含两个事件分发与处理的方法:

1 //事件分发
2 public boolean dispatchTouchEvent(MotionEvent ev);
3 //事件消费
4 public boolean onTouchEvent(MotionEvent event);

我们知道,事件最先是传到 Activity 中,然后在其内部分发之后再传递给ViewGroup 。也就是说 ViewGroup 中的事件上游其实是 Activity,但实际上,ViewGroup 事件的上游不仅仅可以是 Activity,还可以是 Dialog,更进一步的说:ViewGroup 事件的上游是一个 Window。

我这里只介绍 Activity 中的事件分发机制,其它的有兴趣可以自行阅读源码。
首先,Activity 中的事件同样起源于 dispatchTouchEvent 方法:

1 public boolean dispatchTouchEvent(MotionEvent ev) {
2 if (ev.getAction() == MotionEvent.ACTION_DOWN) {
3 onUserInteraction();
4 }
5 if (getWindow().superDispatchTouchEvent(ev)) {
6 return true;
7 }
8 return onTouchEvent(ev)
9 }

第一步是判断事件如果是点击事件,则调用 onUserInteraction 方法,该方法的方法体为空,我们可以重写这个方法,用于处理一些交互问题。

第二步是调用了 Window 的 superDispatchTouchEvent 方法,如果返回 true,则表示此事件已被消费,结束此次分发流程,false 则继续调用该 Activity 的 onTouchEvent 方法处理该事件。

再来说 superDispatchTouchEvent 这个方法是什么,我们知道 Window 是个抽象类,具体实现为 PhoneWindow,我们看看 PhoneWindow 中的这个方法的实现:

1 @Override
2 public boolean superDispatchTouchEvent(MotionEvent event) {
3 return mDecor.superDispatchTouchEvent(event);
4 }

其中的 MDecor 指的是 DecorView, DecorView 是整个视图树的根节点视图,也就是说 Activity 的事件被传到了视图树的根节点,我们来看看 DecorView 中的 s uperDispatchTouchEvent 方法:

1 public boolean superDispatchTouchEvent(MotionEvent event) {
2 return super.dispatchTouchEvent(event);
3 }

只是调用了父类的 dispatchTouchEvent 方法,它的父类是 FrameLayout,此时的事件已经传递到了视图树,开始通过 ViewGroup 的逻辑进行分发,事已至此,一切都很明确了,Activity 已经将事件传递给了 ViewGroup。

总结一下,事件进入 Activity#dispatchTouchEvent 开始分发,首先会将该事件传递给该页面的 ViewGroup,ViewGroup 如果消费了该事件,则分发结束,未消费则继续调用 Activity#onTouchEvent 方法处理事件,流程如下图:

微信图片_20190725142116.png

现在再来看 ViewGroup 的事件分发机制。


ViewGroup 中的事件分发机制


ViewGroup 中包含三个事件分发相关的方法:

1 //事件分发
2 public boolean dispatchTouchEvent(MotionEvent ev);
3 //事件拦截
4 public boolean onInterceptTouchEvent(MotionEvent ev);
5 //事件消费
6 public boolean onTouchEvent(MotionEvent event);

事件首先会进入 dispatchTouchEvent 方法开始分发,该方法通过调用 onInterceptTouchEvent 方法判断是否需要拦截事件。
如果需要拦截(方法返回 true),则表示当前 ViewGroup 希望处理该事件,或者不希望子 View 处理该事件,此时将直接调用 onTouchEvent 方法。
如果不需要拦截,则将该事件传递给子 View 的 onTouchEvent 方法,或者子 ViewGroup 的 dispatchTouchEvent 方法。

现在再来说 onTouchEvent 方法, 该方法用于消费事件,返回值表示是否已消费,在两种情况下会被调用:onInterceptTouchEvent 方法确认需要拦截该事件以及子 View/ViewGroup 未消费该事件。
如果该方法返回 true,则表示事件已消费,此次事件分发就成结束;
如果返回 false,则表示未消费该事件,会继续调用父 ViewGroup 或 Activity 的 onTouchEvent 方法。

对于 ViewGroup 来说,事件拦截与事件消费是两个概念。
事件拦截对应 onInterceptTouchEvent 方法,返回 true 表示拦截该事件。
事件消费对应 onTouchEvent 方法,返回 true 表示消费此事件。
拦截事件表示该事件将不会向下传递,也就是说不会传递给子 View。
消费事件表示该事件将不会向上传递,也就是说不会传递给父 ViewGroup 的 onTouchEvent 方法。
我们可以拦截但不消费某个事件,同样,可以也消费但不拦截某个事件。

其分发流程如下图所示:

微信图片_201907251421161.pngView 中的事件分发机制


View 中包含如下两个用于处理事件分发相关的方法:

1 //事件分发
2 public boolean dispatchTouchEvent(MotionEvent event);
3 //事件消费
4 public boolean onTouchEvent(MotionEvent event);

ViewGroup 会将事件传递给 dispatchTouchEvent 方法,由于不是 ViewGroup,不需要考虑向下传递事件的逻辑,所以 View 中的事件处理流程很简单。
抛开 dispatchTouchEvent 中其它的代码来看,其中主要是用来调用 onTouchEvent 方法,并返回 onTouchEvent 的返回值,流程如下:

微信图片_201907251421162.png上述便是 Android 事件机制的完整流程,实际上事件分发机制还有很多要处理的东西,我这里只是介绍了一些重点及关键点。
下面我来用一张图总结一下:

其他


另外还有一些需要注意点的点。
除了上面说的这几个事件分发相关的方法外,我们经常会调用 View#setOnTouchListener 来处理事件,例如下面这样:

1 view.setOnTouchListener(new View.OnTouchListener() {
2 @Override
3 public boolean onTouch(View v, MotionEvent event) {
4 return false;
5 }
6 });


当我们调用了这个方法后,dispatchTouchEvent 会优先调用 OnTouchListener#onTouch 方法判断是否消费该事件,不消费才会继续调用 onTouchEvent 方法。

还有一点,我们设置 OnTouchListener 时 IDE 会提示一个警告,或者我们直接重写 View#OnTouchEvent 方法时同样会提示这个警告:

1 Custom view has setOnTouchListener called on it but does not override performClick
提示我们没有重写 performClick 方法,那我重写之后还是有警告,不过这次变成了这样:

onTouchEvent should call performClick when a click detected
1 意思就是由于我们重写了 onTouchEvent 方法,可能会导致设置 setOnClickListener 方法失效,我们需要在合适的时候去调用 performClick 方法,我们可以这样:

1 @Override
2 public boolean onTouchEvent(MotionEvent event) {
3 Log.i(TAG, 'ViewGroup->onTouchEvent:' + event.toString());
4 if(event.getAction() == MotionEvent.ACTION_DOWN){
5 return performClick();
6 }
7 return super.onTouchEvent(event);
8 }
9
10 @Override
11 public boolean performClick() {
12 return super.performClick();
13 }

如果觉得还不错的话,欢迎关注我的个人公众号,我会不定期发一些干货文章~
---------------------
作者:张可_
来源:CSDN
原文:
版权声明:本文为博主原创文章,转载请附上博文链接!

点赞
收藏
回复
分享
举报
浏览1538 发布于2019-07-25 06:23未知归属地
全部评论
最多点赞
最新发布
最早发布
暂无评论
暂无评论
写回答
新增插入模板功能
一键使用模板,快速填写内容,轻松发帖~
知道了
  • 为了保障您的信息安全,请勿上传您的敏感个人信息(如您的密码等信息)和您的敏感资产信息(如关键源代码、签名私钥、调试安装包、业务日志等信息),且您需自行承担由此产生的信息泄露等安全风险。
  • 如您发布的内容为转载内容,请注明内容来源。

我要发帖子

了解社区公约,与您携手共创和谐专业的开发者社区。