Android事件分发机制三:事件分发工作流程

onTouchEvent

该方法是消费事件的主要方法,存在于view中,viewGroup默认并没有重写该方法。方法返回true表示消费事件,返回false表示不消费事件。

viewGroup分发事件时,如果没有一个子view消费事件,那么会调用自身的onTouchEvent方法来处理事件。View的dispatchTouchEvent方法中,并不是直接调用onTouchEvent方法来消费事件,而是先调用onTouchListener判断是否消费;如果onTouchListener没有消费事件,才会调用onTouchEvent来处理事件。

我们为view设置的onClickListener与onLongClickListener都是在View的dispatchTouchEvent方法中,根据具体的触摸情况被调用。

重要规则


事件分发有一个很重要的原则:一个触控点的事件序列只能给一个view消费,除非发生异常情况如被viewGroup拦截 。具体到代码实现就是:消费了一个触控点事件序列的down事件的view,将持续消费该触控点事件序列接下来的所有的事件 。举个栗子:

当我手指按下屏幕时产生了一个down事件,只有一个view消费了这个down事件,那么接下来我的手指滑动屏幕产生的move事件会且仅会给这个view消费。而当我手机抬起,再按下时,这时候又会产生新的down事件,那么这个时候就会再一次去寻找消费down事件的view。所以,事件分发,是以事件序列为单位的 。

因此下面的工作流程中都是指down事件的分发 ,而不是ACTION_MOVE或ACTION_UP的分发。因为消费了down事件,意味着接下来的move和up事件都会给这个view处理,也就无所谓分发了。但同时注意事件序列是可以被viewGroup的onInterceptTouchEvent中断的,这些就属于其他的情况了。

细心的读者还会发现事件分发中包含了多点触控。在多点触控的情况下,ACTION_POINTER_DOWN与ACTION_DOWN的分发规则是不同的,具体可前往第二篇文章了解详细。ACTION_POINTER_DOWN在ACTION_DOWN的分发模型上稍作了一些修改而已,后面会详细解析,

工作流程模型


工作流程模型,本质上就是不同的控件对象,viewGroup和view之间事件分发方法的关系。需要注意的是,这里讨论的是viewGroup和view的默认方法实现,不涉及其他实现类如DecorView的重写方法。

下面用一段伪代码来表示三个事件分发方法之间的关系( 这里再次强调,这里的事件分发模型分发的事件类型是ACTION_DOWN且都是默认的方法,没有经过重写,这点很重要 ):

public boolean dispatchTouchEvent(MotionEvent event){

// 先判断是否拦截

if (onInterceptTouchEvent()){

// 如果拦截调用自身的onTouchEvent方法判断是否消费事件

return onTouchEvent(event);

}

// 否则调用子view的分发方法判断是否处理事件

if (childView.dispatchTouchEvent(event)){

return true;

}else{

return onTouchEvent(event);

}

}

这段代码非常好的展示了三个方法之间的关系:在viewGroup收到触摸事件时,会先去调用 onInterceptTouchEvent 方法判断是否拦截,如果拦截则调用自己的 onTouchEvent 方法处理事件,否则调用子view的 dispatchTouchEvent 方法来分发事件。因为子view也有可能是一个viewGroup,这样就形成了一个类似递归的关系。

这里我再补上view分发逻辑的简化伪代码:

public boolean dispatchTouchEvent(MotionEvent event){

// 先判断是否存在onTouchListener且返回值为true

if (mOnTouchListener!=null && mOnTouchListener.onTouch(event)){

// 如果成功消费则返回true

return true;

}else{

// 否则调用onTouchEvent消费事件

return onTouchEvent(event);

}

}

view与viewGroup不同的是他不需要分发事件,所以也就没有必要拦截事件。view会先检查是否有onTouchListener且返回值是否为true,如果是true则直接返回,否则调用onTouchEvent方法来处理事件。

基于上述的关系,可以得到下面的工作流程图:

这里为了展示类递归关系使用了画了两个viewGroup,只需看中间一个即可,下面对这个图进行解析:

  • viewGroup
  1. viewGroup的dispatchTouchEvent方法接收到事件消息,首先会去调用onInterceptTouchEvent判断是否拦截事件
  • 如果拦截,则调用自身的onTouchEvent方法

  • 如果不拦截则调用子view的dispatchTouchEvent方法

  1. 子view没有消费事件,那么会调用viewGroup本身的onTouchEvent

  2. 上面1、2步的处理结果为viewGroup的dispatchTouchEvent方法的处理结果,并返回给上一层的onTouchEvent处理

  • view
  1. view的dispatchTouchEvent默认情况下会调用onTouchEvent来处理事件,返回true表示消费事件,返回false表示没有消费事件

  2. 第1步的结果就是dispatchTouchEvent方法的处理结果,成功消费则返回true,没有消费则返回false并交给上一层的onTouchEvent处理

可以看到整个工作流程就是一个“U”型结构,在不拦截的情况下,会一层层向下寻找消费事件的view。而如果当前view不处理事件,那么就一层层向上抛,寻找处理的viewGroup。

上述的工作流程模型并不是完整的,还有其他的特殊情况没有考虑。下面讨论几种特殊的情况:

事件序列被中断

我们知道,当一个view接收了down事件之后,该触控点接下来的事件都会被这个view消费。但是,viewGroup是可以在中途掐断事件流的,因为每一个需要分发给子view的事件都需要经过拦截方法:onInterceptTouchEvent (当然,这里不讨论子view设置不拦截标志的情况)。那么当viewGroup掐断事件流之后,事件的走向又是如何的呢?参看下图:(注意,这里不讨论多指操作的情况,仅讨论单指操作的move或up事件被viewGroup拦截的情况

  1. 当viewGroup拦截子view的move或up事件之后,会将当前事件改为cancel事件并发送给子view

  2. 如果当前事件序列还未结束,那些接下来的事件都会交给viewGroup的ouTouchEvent处理

  3. 此时不管是viewGroup还是view的onTouchEvent返回了false,那么将导致整个控件树的dispatchTouchEvent方法返回false

  • 秉承着一个事件序列只能给一个view消费的原则,如果一个view消耗了down事件却在接下来的move或up事件返回了false,那么此事件不会给上层的viewGroup处理,而是直接返回false。

多点触控情况

上面讨论的所有情况,都是不包含多点触控情况的。多点触控的情况,在原有的事件分发流程上,新增了一些特殊情况。这里就不再画图,而是把一些特殊情况描述一下,读者了解一下就可以了。

默认情况下,viewGroup是支持多点触控的分发,但view是不支持多点触控的,需要自己去重写 dispatchTouchEvent 方法来支持多点触控。

多点触控的分发规则如下:

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
img

最后

我坚信,坚持学习,每天进步一点,滴水穿石,我们离成功都很近!
以下是总结出来的字节经典面试题目,包含:计算机网络,Kotlin,数据结构与算法,Framework源码,微信小程序,NDK音视频开发,计算机网络等。

字节高级Android经典面试题和答案


领取方法:

所有资料获取方式:评论666+点赞即可咨询资料免费领取方式!

直达领取链接:【Android高级架构师】文件夹下载!

高级Android经典面试题和答案**

[外链图片转存中…(img-GwVJIrTR-1710817705874)]
[外链图片转存中…(img-ZUM0OHuF-1710817705875)]

领取方法:

所有资料获取方式:评论666+点赞即可咨询资料免费领取方式!

直达领取链接:【Android高级架构师】文件夹下载!

  • 20
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值