关于Android触摸事件分发的原理详析

 更新时间:2022年01月09日 12:12:05   作者:灵剑山真人  
触摸事件分发机制一直以来都是Android中比较重要的一大块,自定义view,各种复杂的自定义手势交互都与触摸事件分发机制关系密,下面这篇文章主要给大家介绍了关于Android触摸事件分发原理的相关资料,需要的朋友可以参考下

一:前言

最近在学Android的触摸事件分发,我觉得网上说的太杂太乱,而且有很多博客都有明显的错误。什么自顶向下分发,自下向顶分发,什么拦截又一直消费什么什么之类,非常难懂。为了自己将来回顾可以更好的理解这块知识,也为了后来之人可以更好的学习,我写下这篇博客。

二:说在前面的知识

  • 点击,滑动,松手都是由MotionEvent这个类来表示。
  • 屏幕上的一个事件序列是指以一个MotionEvent.action_down按下开始,以若干个MotionEvent.action_move移动事件在中间,再以一个MotionEvent.action_up作为结束的事件流。
  • view group是view的子类。view group和view都有dispatchTouchEvent方法;view group有onTnterceptTouchEvent和onTouchEvent方法,view 只有onTouchEvent方法。

三:整体流程

1:activity

我们点击屏幕的所有事件,都会被第一个接收。

public boolean dispatchTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            onUserInteraction();//是一个空方法,如果想知道按下了屏幕,可以重写这个方法打印日志
        }
        if (getWindow().superDispatchTouchEvent(ev)) {//把这个事件传给window属性
            return true;
        }
        return onTouchEvent(ev);
    }

2:window就是PhoneWindow

每一个activity都会对应一个PhoneWindow(在onCreate方法之前、activity内部的attach方法中创建)。PhoneWindow含有一个decor view属性(setContentView中创建),phone window把事件传给decor view。 decor view继承于view group。点击事件现在传到decor view这里,就开始view group的事件分发逻辑了。

3:view group

view group收到点击事件, 进入dispatchTouchEvent, 如果满足以下二个条件中的任何一个条件:

  • 事件为down事件
  • 有一个子view或子view group在处理着事件流了
mFirstTouchTarget !=null

就进入判断,如果没有被禁用拦截(子view调用parent.requestDisallowed....)就执行, onInterceptTouchEvent代码。

如果决定拦截,后面还会把mFirstTouchTarget置为null,这样,之后就不会在调用onInterceptTouchEvent了。而且之后的事件流都会由这个view group的dispatchTouchEvent处理

如果不决定拦截,就遍历子view、子view group,挨个调用它们的dispatchTouchEvent如果没有人接收,那就调用自己的super.dispatchTouchEventview group的super.dispatchTouchEvent就是自己view那部分 的 dispatchTouchEvent

4:view

在view这一层,对于down事件,返回true就表示消费这个down事件之后的序列。具体看图。

 view调用setOnTouchLIstener可以设置OnTouchListener,重写onTouch方法。从源码中可以看出,若onTouch返回true,将不再回调onTouchEvent方法。不回调onTouchEvent的话,那onClickListener也不能回调了。

四:一些关键点

即使有view消费着一组事件,事件流由底向上传递时,依然会调用每一个view group的intercept拦截方法判断是否拦截。当一个view group遍历它所有的子view没有一个接收时,就会进入view模式,调用自己继承于view的那一个dispatchTouchEvent方法。如果自己不接收,那会交给调用自己的dispatchTouchEvent的那个父view.

事件流没有什么自上而下,就是自下而上的。

ViewGroup的实现负责将触摸事件沿着控件树向子控件进行派发,而View的实现则主要用于事件接收与处理工作。当view group没有子view接收时,view group作为一个“view”去处理。

五:从源码看触摸事件分发

由于专栏关注自定义控件,所以关于系统如何从硬件获取触摸事件以及传递到Activity的dispatchTouchEvent就不详细分解,下面将从Activity的dispatchTouchEvent方法来一步步看事件是如何被分发传递的:

Activity中的dispatchTouchEvent:

    public boolean dispatchTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            onUserInteraction();
        }
        if (getWindow().superDispatchTouchEvent(ev)) {
            return true;
        }
        return onTouchEvent(ev);
    }

其中onUserInteraction();是一个空实现,是系统留给我们的一个修改事件分发的一个方法,这里可以忽略。

所以实际上Activity的dispatchTouchEvent方法是调用的PhoneWindow的superDispatchTouchEvent方法,如果superDispatchTouchEvent返回false,没有消费掉事件,那么才会再交给activity的onTouchEvent方法去处理,从这个角度来讲,如果所有地方都没有消费掉事件,最后接收事件的会是Activity的onTouchEvent方法。

那么下面我们来看看PhoneWindow中的superDispatchTouchEvent方法:

    @Override
    public boolean superDispatchTouchEvent(MotionEvent event) {
        return mDecor.superDispatchTouchEvent(event);
    }

发现实际上调用的是DecorView对象mDecor的superDispatchTouchEvent方法,来看看DecorView的superDispatchTouchEvent方法:

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

调用的super.dispatchTouchEvent,而再来看看这个DecorView的继承关系:

private final class DecorView extends FrameLayout implements RootViewSurfaceTaker

所以调用的是FrameLayout中的dispatchTouchEvent方法,而FrameLayout并没有重写dispatchTouchEvent方法,所以实际调用的是FrameLayout的父类 ---> ViewGroup中的dispatchTouchEvent方法,下面这个图描述了从系统得到MotionEvent实际到传递给DecorView的super.dispatchTouchEvent的过程:

总结

到此这篇关于Android触摸事件分发原理的文章就介绍到这了,更多相关Android触摸事件分发原理内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 使用Java如何将图片转成Base64编码,并压缩至40k

    使用Java如何将图片转成Base64编码,并压缩至40k

    这篇文章主要介绍了使用Java如何将图片转成Base64编码,并压缩至40k问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-06-06
  • java实现一致性hash算法实例代码

    java实现一致性hash算法实例代码

    这篇文章主要给大家介绍了关于java实现一致性hash算法的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-12-12
  • SpringBoot使用JDBC获取相关的数据方法

    SpringBoot使用JDBC获取相关的数据方法

    这篇文章主要介绍了SpringBoot使用JDBC获取相关的数据方法,JDBC与数据库建立连接、发送 操作数据库的语句并处理结果,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2019-03-03
  • 详解java中的Collections类

    详解java中的Collections类

    这篇文章主要为大家详细介绍了java中的Collections类,感兴趣的小伙伴们可以参考一下
    2016-07-07
  • Java使用@Validated注解进行参数验证的方法

    Java使用@Validated注解进行参数验证的方法

    这篇文章主要介绍了Java使用@Validated注解进行参数验证的方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-08-08
  • SpringBoot项目整合拦截器详解

    SpringBoot项目整合拦截器详解

    这篇文章主要介绍了SpringBoot项目整合拦截器详解,java里的拦截器是动态拦截Action调用的对象,它提供了一种机制可以使开发者在一个Action执行的前后执行一段代码,拦截器用于在某个方法或者字段被访问之前进行拦截,然后再之前或者之后加入某些操作,需要的朋友可以参考下
    2023-10-10
  • JavaWeb工程中集成YMP框架快速上手(二)

    JavaWeb工程中集成YMP框架快速上手(二)

    YMP是一个非常简单、易用的一套轻量级JAVA应用开发框架,设计原则主要侧重于简化工作任务、规范开发流程、提高开发效率。对YMP框架感兴趣的小伙伴们可以参考一下
    2016-02-02
  • Java RocketMQ 路由注册与删除的实现

    Java RocketMQ 路由注册与删除的实现

    这篇文章主要介绍了Java RocketMQ 路由注册与删除的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-11-11
  • 查看SpringBoot和JDK版本对应关系的方法

    查看SpringBoot和JDK版本对应关系的方法

    在进行一些自主学习的时候,发现使用maven方式创建的SpringBoot项目启动失败,最终发现是SpringBoot版本和JDK版本不对应导致的,所以本文就给大家介绍了如何查看SpringBoot和JDK版本的对应关系,需要的朋友可以参考下
    2024-03-03
  • java的poi技术读取和导入Excel实例

    java的poi技术读取和导入Excel实例

    本篇文章主要介绍了java的poi技术读取和导入Excel实例,报表输出是Java应用开发中经常涉及的内容,有需要的可以了解一下。
    2016-11-11

最新评论