一个最简单的屏幕触摸动作,理解事件分发中的3个方法

前言

视图系统主要包括 排版渲染事件分发 这两大类工作。

其中 客户端 运行在用户进程中,负责 可视化内容的排版向服务端输出,以及 接收来自服务端的事件并在视图树中分发

而 视图服务 运行在系统进程中,负责 对排版内容的渲染,以及 传递触控事件给客户端

android中的事件类型分为按键事件和屏幕触摸事件,Touch事件是屏幕触摸事件的基础事件。

一次屏幕触摸会发生什么?

触发了一系列Touch事件:ACTION_DOWN->ACTION_MOVE->ACTION_MOVE->ACTION_MOVE…->ACTION_MOVE->ACTION_UP。到底有多少个MOVE呢?这取决于你点击屏幕时手接触多久屏幕。

当屏幕中包含一个ViewGroup,而这个ViewGroup又包含一个子view,这个时候android系统如何处理Touch事件呢?到底是ViewGroup来处理Touch事件,还是子view来处理Touch事件呢?onTouchEvent和OnClickListener是如何执行的?先留几个疑问

解决疑问前,先要理清楚ViewGroup这个类中,和TouchEvent处理密切相关的3个方法:

  • public boolean dispatchTouchEvent(MotionEvent ev) 这个方法用来分发TouchEvent
  • public boolean onInterceptTouchEvent(MotionEvent ev) 这个方法用来拦截TouchEvent(View类中没有这个方法)
  • public boolean onTouchEvent(MotionEvent ev) 这个方法用来处理TouchEvent

接下来我们来理一下逻辑
时间传递
一次没有人工干预的事件是这样的:

当TouchEvent发生时,首先交互界面(Activity或者Fragment)最顶层的ViewGroup先获取到。TouchEvent最先到达最顶层 viewGroup的 dispatchTouchEvent 进行事件分发,此时dispatchTouchEvent 调用父类的同名方法super.dispatchTouchEvent(event)。接着到达 onInterceptTouchEvent,也是调用父类同名方法super,默认是不拦截,继续传递到下一个View或者ViewGroup。如果传到了View,先到达dispatchTouchEvent,这时候不往下传了,因为View的事件分发super方法和ViewGroup的不一样,他会直接交给自己的onTouchEvent处理。

人工干预的情况,也就是改写了上述出现的3个方法,手动返回true或者false而不是调用super方法。

总结

  • dispatchTouchEvent方法
    • View和ViewGroup不同在于当调用super时,ViewGroup会进行分发,而View会交给他的onTouchEvent处理。
    • View和ViewGroup返回true、返回false的处理逻辑都是一样的。返回true则消费,返回false则回传给父View的onTouchEvent处理。注意:返回false后,此时后面的事件都接收不到了,因为会往上传,最后由哪个View处理,以后的所有事件都交由它来处理(怎么理解这句话呢,好比我有A、B2个ViewGroup,C是View,我重写C里的dispatchTouchEvent方法,让他返回false,那么点击一次屏幕,AB的分发和拦截方法被调用,到C这分发返回false,直接往上传:→B(onTouchEvent)→A(onTouchEvent)→窗口层级(onTouchEvent),此时在ABC里MOVE和UP的事件打印见不到了,因为后面的MOVE或者UP的事件都交给了窗口层级处理了,不信你可以在Activity层级的onTouchEvent和dispatchTouchEvent打印一下是有这2个事件的;如果你不想让DecorView这样的窗口层级处理,只需要在A中重写onTouchEvent,返回true,那么虽然BC后面的事件收不到,但是在A里面有MOVE和UP的打印)。
  • onInterceptTouchEvent方法
    • 因为只有ViewGroup有,所以默认的super方法和返回false都是表示不拦截,返回true则交给自己的onTouchEvent处理。
  • onTouchEvent方法
    • View和ViewGroup返回true、返回false的处理逻辑都是一样的。不同的地方在于super方法,View是直接消费,ViewGroup则是回传给父View的onTouchEvent处理,一直往上传直到传到某个ViewGroup返回true消费掉。值得注意的是,事件触发是先触发onTouch,再触发onClick,如果onTouch方法返回tue,表示消费掉该事件,不在继续进行事件传递,onClick也不会被调用。
补充:View的onTouch和onTouchEvent联系

在这里插入图片描述

①如果onTouch()方法返回值是true(事件被消费)时,则onTouchEvent()方法将不会被执行;

②只有当onTouch()方法返回值是false(事件未被消费,向下传递)时,onTouchEvent方法才被执行。

③平时我们使用的OnClickListener,其优先级最低,即处于事件传递的尾端。(实现都基于onTouchEvent)

④给View设置监听OnTouchListener,重写onTouch()方法。其优先级比onTouchEvent()高。如果不返回false,那么设置的点击监听等也就意味着失效了。

看一道小米的面试题

题目

问题解答
ACTION_DOWN在3个界面传递;ACTION_MOVE在A、B界面传递,B拦截了并调用onTouchEvent处理了。ACTION_UP在A、B界面传递。

问题分析
ACTION_DOWN没有限制;ACTION_MOVE到B的时候由于onInterceptTouchEvent拦截了,直接被B的onTouchEvent处理,onInterceptTouchEvent返回true之后,看Log知道,之后onInterceptTouchEvent都不会调用了,后面的ACTION_MOVE和ACTION_UP事件都不会往下传了,都是在B的onTouchEvent处理。具体可以看下面的日志。

打印日志

A

12-06 10:52:41.980 20891-20891/com.phz.testtouchevent E/ALayout: dispatchTouchEvent:ACTION_DOWN
12-06 10:52:41.980 20891-20891/com.phz.testtouchevent E/ALayout: onInterceptTouchEvent:ACTION_DOWN
12-06 10:52:42.006 20891-20891/com.phz.testtouchevent E/ALayout: dispatchTouchEvent:ACTION_MOVE
12-06 10:52:42.006 20891-20891/com.phz.testtouchevent E/ALayout: onInterceptTouchEvent:ACTION_MOVE
12-06 10:52:42.023 20891-20891/com.phz.testtouchevent E/ALayout: dispatchTouchEvent:ACTION_MOVE
12-06 10:52:42.023 20891-20891/com.phz.testtouchevent E/ALayout: onInterceptTouchEvent:ACTION_MOVE
12-06 10:52:42.029 20891-20891/com.phz.testtouchevent E/ALayout: dispatchTouchEvent:ACTION_MOVE
12-06 10:52:42.029 20891-20891/com.phz.testtouchevent E/ALayout: onInterceptTouchEvent:ACTION_MOVE
12-06 10:52:42.029 20891-20891/com.phz.testtouchevent E/ALayout: dispatchTouchEvent:ACTION_UP
12-06 10:52:42.029 20891-20891/com.phz.testtouchevent E/ALayout: onInterceptTouchEvent:ACTION_UP

B

12-06 10:52:41.980 20891-20891/com.phz.testtouchevent E/BLayout: dispatchTouchEvent:ACTION_DOWN
12-06 10:52:41.980 20891-20891/com.phz.testtouchevent E/BLayout: onInterceptTouchEvent:ACTION_DOWN
12-06 10:52:42.006 20891-20891/com.phz.testtouchevent E/BLayout: dispatchTouchEvent:ACTION_MOVE
12-06 10:52:42.006 20891-20891/com.phz.testtouchevent E/BLayout: onInterceptTouchEvent:ACTION_MOVE
12-06 10:52:42.023 20891-20891/com.phz.testtouchevent E/BLayout: dispatchTouchEvent:ACTION_MOVE
12-06 10:52:42.023 20891-20891/com.phz.testtouchevent E/BLayout: onTouchEvent:ACTION_MOVE
12-06 10:52:42.029 20891-20891/com.phz.testtouchevent E/BLayout: dispatchTouchEvent:ACTION_MOVE
12-06 10:52:42.029 20891-20891/com.phz.testtouchevent E/BLayout: onTouchEvent:ACTION_MOVE
12-06 10:52:42.029 20891-20891/com.phz.testtouchevent E/BLayout: dispatchTouchEvent:ACTION_UP
12-06 10:52:42.029 20891-20891/com.phz.testtouchevent E/BLayout: onTouchEvent:ACTION_UP

C

12-06 10:52:41.980 20891-20891/com.phz.testtouchevent E/CLayout: dispatchTouchEvent:ACTION_DOWN
12-06 10:52:41.980 20891-20891/com.phz.testtouchevent E/CLayout: onTouchEvent:ACTION_DOWN
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值