文档开发者中心
我的

【转载】Android事件分发机制


事件分发机制

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


大多介绍事件分发机制的文章都只叙述了 View 与 ViewGroup 的事件分发机制,我这里将从 Activity 开始介绍。


一般来说,事件传递的顺序是:Activity -> ViewGroup -> View。


那么我们先说 Activity。


Activity 中的事件分发机制


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


//事件分发

public boolean dispatchTouchEvent(MotionEvent ev);

//事件消费

public boolean onTouchEvent(MotionEvent event);


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


我这里只介绍 Activity 中的事件分发机制,其它的有兴趣可以自行阅读源码。

首先,Activity 中的事件同样起源于 dispatchTouchEvent 方法:


public boolean dispatchTouchEvent(MotionEvent ev) {

if (ev.getAction() == MotionEvent.ACTION_DOWN) {

onUserInteraction();

}

if (getWindow().superDispatchTouchEvent(ev)) {

return true;

}

return onTouchEvent(ev);

}


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


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


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


@Override

public boolean superDispatchTouchEvent(MotionEvent event) {

return mDecor.superDispatchTouchEvent(event);

}


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


public boolean superDispatchTouchEvent(MotionEvent event) {

return super.dispatchTouchEvent(event);

}


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


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



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


ViewGroup 中的事件分发机制


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


//事件分发

public boolean dispatchTouchEvent(MotionEvent ev);

//事件拦截

public boolean onInterceptTouchEvent(MotionEvent ev);

//事件消费

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 方法。

我们可以拦截但不消费某个事件,同样,可以也消费但不拦截某个事件。


其分发流程如下图所示:


View 中的事件分发机制


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


//事件分发

public boolean dispatchTouchEvent(MotionEvent event);

//事件消费

public boolean onTouchEvent(MotionEvent event);


ViewGroup 会将事件传递给 dispatchTouchEvent 方法,由于不是 ViewGroup,不需要考虑向下传递事件的逻辑,所以 View 中的事件处理流程很简单。

抛开 dispatchTouchEvent 中其它的代码来看,其中主要是用来调用 onTouchEvent 方法,并返回 onTouchEvent 的返回值,流程如下:



上述便是 Android 事件机制的完整流程,实际上事件分发机制还有很多要处理的东西,我这里只是介绍了一些重点及关键点。

下面我来用一张图总结一下:




其他


另外还有一些需要注意点的点。

除了上面说的这几个事件分发相关的方法外,我们经常会调用 View#setOnTouchListener 来处理事件,例如下面这样:


view.setOnTouchListener(new View.OnTouchListener() {

@Override

public boolean onTouch(View v, MotionEvent event) {

return false;

}

});


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


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


Custom view has setOnTouchListener called on it but does not override performClick


提示我们没有重写 performClick 方法,那我重写之后还是有警告,不过这次变成了这样:


onTouchEvent should call performClick when a click detected


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


@Override

public boolean onTouchEvent(MotionEvent event) {

Log.i(TAG, 'ViewGroup->onTouchEvent:' + event.toString());

if(event.getAction() == MotionEvent.ACTION_DOWN){

return performClick();

}

return super.onTouchEvent(event);

}


@Override

public boolean performClick() {

return super.performClick();

}


如果觉得还不错的话,欢迎关注我的个人公众号,我会不定期发一些干货文章~


---------------------

作者:张可_

来源:CSDN

原文:https://blog.csdn.net/u013872857/article/details/91049670

版权声明:本文为博主原创文章,转载请附上博文链接!


点赞
收藏
回复
1
分享
举报
浏览7465 编辑于2019-07-09 07:50未知归属地
全部评论
最多点赞
最新发布
最早发布

前排沙发~~~~~~

1楼回复于2019-07-09 08:47 未知归属地
写回答
新增插入模板功能
一键使用模板,快速填写内容,轻松发帖~
知道了
  • 为了保障您的信息安全,请勿上传您的敏感个人信息(如您的密码等信息)和您的敏感资产信息(如关键源代码、签名私钥、调试安装包、业务日志等信息),且您需自行承担由此产生的信息泄露等安全风险。
  • 如您发布的内容为转载内容,请注明内容来源。

我要发帖子

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