Android 事件分发机制

事件分发机制在android中非常常见,比如:手势滑动,自定义View,多点触控都有它的身影。事件分发的顺序是:Activity->GroupView->View,GrounpView包括五大布局,ListView和GridView等,View包括TextView,ImageView等。我们今天就来搞一下。

首先我们的手指和屏幕接触的MotionEvent对象会产生一系列事件,它有四种状态:

MotionEvent对象的四种状态:

  • MotionEvent.ACTION_DOWN:手指按下屏幕的瞬间
  • MotionEvent.ACTION_MOVE:手指在屏幕上移动
  • MotionEvent.ACTION_UP:手指离开屏幕瞬间
  • MotionEvent.ACTION_CANCEL :取消手势,一般由程序产生

事件分发所涉及的主要方法:

1.1 dispatchTouchEvent

用来进行事件分发和传递的,返回true的时候一定是自己去消费,返回false有可能是自己消费也有可能是传递给上一级的OnTouchEvent方法, super就传递给其他的view。该方法也是触摸事件第一个执行的方法。

1.2 onInterceptTouchEvent

是viewGroup特有的,用来做触摸事件拦截的,默认返回false:如果false或者super表示不拦截,事件继续向下传递。事件所经过的每一层的viewGroup都会去调用该方法来询问是否拦截。如果返回true,则代表拦截该事件,停止传递给子view,会走自己的onTouchEvent事件 事件被拦截后,子view会接收到一个cancel事件,来恢复之前的状态,结束当前事件流。

1.3 requestDisallowInterceptTouchEvent

是viewGroup专有的方法,也是事件拦截用的,和onInterceptTouchEvent类似。不过一般是在子view中来调用的。

1.4 onTouch

是触摸事件,当一个触摸事件被分发到一个view的时候。

1.5 onTouchEvent

真正用来处理触摸事件的最后调用的方法, onTouchEvent是否拦截取决于down事件。

1.6 onClick

是一个点击事件,是在onTouchEvent的up事件里面执行的,它的优先级是最低,如果在onTouchEvent或OnTouchListener的onTouch方法如果返回true,则不响应onClick方法。

onTouch,onTouchEvent和onClick的执行顺序:onTouch—>onTouchEvent—>onClick,注意:onClick直接消费掉了事件,不会再向上回溯事件了。 我们来一个表格了解一下Activity和ViewGroup和View各自拥有的方法:

类型

拥有方法

Activity

dispatchTouchEvent,onTouchEvent

GroupView

dispatchTouchEvent,onTouchEvent ,onInterceptTouchEvent,requestDisallowInterceptTouchEvent

View

dispatchTouchEvent,onTouchEvent ,onTouch,onClick

在来一张图了解一下其分发流程(自己写的,体谅一下):

image.png

一共分有三层,Activity——>ViewGroup——>View,其中ViewGroup比其他两个多了一个onInterceptTouchEvent方法,箭头代表了事件流的走向,箭头上的值表示该方法的返回值,消费框表示事件被消费掉,不会向下或者向上传递,接下来我们写代码运行一下看效果。

测试部分

1.首先先自定义一个view(TextView)和一个ViewGroup(ReleativeLayout),然后重新其dispatchTouchEvent,onTouchEvent等方法,布局关系基本是这样的:

image.png

已ViewGroup为例,部分代码是这样的:

image.png

我们先啥也不干,就正常点击一下,打印看看:

image.png

由于我们只是点击然后迅速抬起,所以没有Action_Move这个动作,所以正常的事件流程应该是这样的: 事件依次从Activity的dispatchTouchEvent——>ViewGroup的dispatchTouchEvent——>ViewGroup的onInterceptTouchEvent——>View的dispatchTouchEvent——>View的onTouchEvent——>ViewGroup的onTouchEvent——>Activity的onTouchEvent流转完成,由于没有事件没有被消费,所以事件一路传递下来,又一路回溯回去,所以最终事件就被Activity消费了(就是Activity的UP事件)。

2.我们刚才是正常的事件分发流程图,没有给View即TextView设置click事件,那我们现在给TextView设置点击事件看看,事件是怎么分发的?

image.png

可以看到如果给View设置了click事件,那么事件会最终分发给view,被view消费,这就是我们平时最常用的事件分发流程

3.其他的你们都可以在个个阶段的事件分发中,返回不同的值去测试验证,我们再说一个viewGroup的onInterceptTouchEvent()返回true和onTouchEvent也返回true看一下:

image.png

可以看到被拦截之后,后续move,up事件都交给自己处理,并且不再调用onIntercepetTouchEvent,而且事件也不再传递到子View

4.那如果我们不想让TextView的点击事件响应怎么办,按照上面的andorid事件分发流程图,方法多了,我们可以在不同的阶段进行控制,不让事件向下分发,但我们试试onTouch()这个方法,此方法默认返回false,我们现在让它返回true试试。测试结果是可以的,也就是说onTouch()方法返回true也是自己消费了,不会在向下传递到onTouchEvent()了,更不会传递到onClick()了,如图:

image.png

image.png

ACTION_CANCEL的出现时机

ACTION_CANCEL的出现场景为:手指点击屏幕停顿,让系统以为view的onTouchEvent要消费此事件的时候滑动,在onInterceptTouchEvent()中拦截,交给ViewGroup来处理此次事件,这个时候就会额外触发一个ACTION_CANCEL来传递给子view来恢复子view的状态. 那么我们怎么做呢?

  • 先让系统以为view的onTouchEvent要消费事件:在view的onTouchEvent中return true,结果如下:

image.png

此时事件是被view的onTouchEvent消费了。

  • 在viewGrounp的onInterceptTouchEvent方法中对ACTION_MOVE动作进行拦截return true,把属于view的事件给转移到ViewGroup消费,结果如下:

image.png

ACTION_CANCEL这个动作目前是被触发了,但大家有没有发现后续move没有再经过ViewGroup,明明是在ViewGroup的ACTION_MOVE中return true消费了呀? 原因是:还需要在ViewGroup的onTouchEvent返回true才行,不然viewGroup又会默认super,把ACTION_MOVE等传递给了Activity消费而不是自己消费,那我们现在改一下,运行如下:

image.png

这样才对嘛。 项目已上传github.

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏张高兴的博客

张高兴的 Xamarin.Android 学习笔记:(三)活动生命周期

381110
来自专栏everhad

札记:android手势识别,MotionEvent

摘要 本文是手势识别输入事件处理的完整学习记录。内容包括输入事件InputEvent响应方式,触摸事件MotionEvent的概念和使用,触摸事件的动作分类、多...

38170
来自专栏Android干货

Android项目实战(三十四):蓝牙4.0 BLE 多设备连接

1.1K60
来自专栏Android干货

Android项目实战(六):JazzyGridView和JazzyListView的使用

23540
来自专栏Android相关

Android的NestedScroll机制

这种事件传递的结果就会导致以下问题: 一个View把Touch事件消费之后,其他View就无法接收到该事件,也就无法根据这个事件完成与用户的交互

21830
来自专栏jianhuicode

Fragment 学习笔记(1)

网上关于Fragment相关的博客资料很多,写关于这个知识笔记是加深记忆,大神略过; 0x01 了解Fragment 当然看官方文档(http://www.an...

20250
来自专栏编程之路

羊皮书APP(Android版)开发系列(二十三)10分钟单例模式实战-Glide图片加载库封装工具类

12050
来自专栏腾讯Bugly的专栏

Android APP 快速 Pad 化实现

如何能在最快的时间内,实现一个最新版本 Android app 的 pad 化呢?从拿到一个大型手机 app 代码开始开发到第一个其全新 pad 版本的发布,我...

65060
来自专栏jianhuicode

事件分发机制学习

0x01 事件分发机制中三个重要的函数  View事件机制有哪几个重要函数?dispatchTouchEvent(),onInterceptTouchEvent...

190100
来自专栏Android干货园

【PageLayout】非常简单的一键切换加载-空数据-错误页,支持自定义

Android中经常使用一个空白页和网络错误页用来提高用户体验,给用户一个较好的感官,如果获取到的数据为空,那么会显示一个空白数据页,如果在获取数据的过程中网络...

13030

扫码关注云+社区

领取腾讯云代金券