Android触摸事件分发机制(一)

这篇具有很好参考价值的文章主要介绍了Android触摸事件分发机制(一)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1. 简介

本文主要分享事件分发中的基本概念。

介绍负责参与分发事件的主要方法。

从这些方法的核心逻辑中,总结事件分发的规律。

2. 被分发的对象


被分发的对象是那些?被分发的对象是用户触摸屏幕而产生的点击事件,事件主要包括:按
下、滑动、抬起与取消。这些事件被封装成 MotionEvent 对象。该对象中的主要事件如下表
所示:

Android触摸事件分发机制(一),android,android

按下、滑动、抬起、取消这几种事件组成了一个事件流。事件流以按下为开始,中间可能有
若干次滑动,以抬起或取消作为结束。
在安卓对事件分发的处理过程中,主要是对按下事件作分发,进而找到能够处理按下事件的
组件。对于事件流中后续的事件(如滑动、抬起等),则直接分发给能够处理按下事件的组
件。故本文讨论的内容则是主要针对按下事件的。

 3. 分发事件的组件

分发事件的组件,也称为分发事件者,包括 Activity、View 和 ViewGroup。它们三者的一般
结构为:

Android触摸事件分发机制(一),android,android

事件分发者结构
从上图中可以看出,Activity 包括了 ViewGroup,ViewGroup 又可以包含多个 View。 

Android触摸事件分发机制(一),android,android

4. 分发的核心方法 

负责对事件进行分发的方法主要有三个,分别是:
dispatchTouchEvent(
onTouchEvent()
onInterceptTouchEvent()。
它们并不存在于所有负责分发的组件中,其具体情况总结于下面的表格中:

Android触摸事件分发机制(一),android,android

从 表 格 中 看 , dispatchTouchEvent,onTouchEvent 方 法 存 在 于 上 文 的 三 个 组 件 中 。 而
onInterceptTouchEvent 为 ViewGroup 独有。这些方法的具体作用在下文作介绍。
ViewGroup 类中,实际是没有 onTouchEvent 方法的,但是由于 ViewGroup 继承自 View,而
View 拥有 onTouchEvent 方法,故 ViewGroup 的对象也是可以调用 onTouchEvent 方法的。故在表格中表明 ViewGroup 中存在 onTouchEvent 方法的。 

5. 事件分发过程

这一小节是本文的核心内容,会从整体上对事件的分发过程作介绍。
对于事件分发过程从,笔者认为网上的一些教程中的观点是有误的。
网上部分教程认为事件是从内部(如 Button)开始分发的,这是有误的。
网上部分教程常使用’向上‘、’向下‘传播等描述,但又未对‘何为上’、‘何为下’作解释。
网上部分教程将 Java 的子类对象调用父类方法(向上转型)的过程也称为‘向上’传播,即将
事件在组件之间的传播与程序语言多态特性混为一谈,让初学者费解。
子类在覆写的方法中调用父类的同名方法,被称为’向上传播‘,这也是不对的。
为此在介绍分发过程之前,先对一些概念作定义:
向下传播:Activity 包括 Layout,事件从 Activity 向 Layout 传播被称作’向下传播‘。Layout 包
含若干 View,事件从 Layout 向其子 View 传播,也被称为’向下传播‘。
向上传播:与’向下传播‘相反。
’向上转型‘不能称为传播,即子类对象调用父类方法,或在覆写的方法中调用父类方法,都
不能称为传播。不能将面向对象程序语言中的概念与布局层次中的上下传播混为一谈。
分发方法 dispatchTouchEvent
从方法的名称中可以看出该方法主要是负责分发,是安卓事件分发过程中的核心。事件是如
何传递的,主要就是看该方法,理解了这个方法,也就理解了安卓事件分发机制。
在了解该方法的核心机制之前,需要知道一个结论:
如果某个组件的该方法返回 TRUE,则表示该组件已经对事件进行了处理,不用继续调用其余
组件的分发方法,即停止分发。
如果某个组件的该方法返回 FALSE,则表示该组件不能对该事件进行处理,需要按照规则继续
分发事件。在不复写该方法的情况下,除了一些特殊的组件,其余组件都是默认返回 False
的。后续有例子说明。
为何返回 TRUE 就不用继续分发,而返回 FALSE 就停止分发呢?为了解决这个疑问,需要看
一看该方法的具体分发逻辑。为了便于理解,下面对 dispatchTouchEvent 方法进行简化,只
保留最核心的逻辑。
Activity 的 dispatchTouchEvent 方法

// Activity 中该方法的核心部分伪代码
public boolean dispatchTouchEvent(MotionEvent ev) {
if (child.dispatchTouchEvent(ev)) {
    return true; //如果子 View 消费了该事件,则返回 TRUE,让调用者知道该事件已
被消费
} else {
    return onTouchEvent(ev); // 如 果 子 View 没 有 消 费 该 事 件 , 则 调 用 自 身 的
    onTouchEvent 尝试处理。
    }
}

首先,从核心逻辑中看出,当事件传递给 Activity 后,它先将事件分发给子 View 处理。
如果经过子 View 层层传递或处理后,该事件被消费了(即返回了 TRUE),则 Activity 的分
发方法也返回 TRUE,同样也表示该事件已经被消费了。
如果经过子 View 层层传递或处理后,该事件没有被消费(即返回了 FALSE),则 Activity 的
分发方法就不会返回 TRUE 了,而是调用 onTouchEvent()去处理,看其实际的处理情况。
如果 onTouchEvent 消费了事件,那依然能返回 TRUE(表示已消费事件),这个 TRUE 作为
dispatchTouchEvent 的返回值,让调用它的对象知道该 Activity 已经消费了事件。
如果 onTouchEvent 没有消费该事件,那就返回 FALSE(表示未消费事件),这个 FALSE 作为
dispatchTouchEvent 的返回值,让调用它的对象知道该 Activity 没有消费事件,需要继续处理。
ViewGroup 的 dispatchTouchEvent 方法

// ViewGroup 中该方法的核心部分伪代码
public boolean dispatchTouchEvent(MotionEvent ev) {
    if (!onInterceptTouchEvent(ev)) {
        return child.dispatchTouchEvent(ev); //不拦截,则传给子 View 进行分发处理
    } else {
        return onTouchEvent(ev); //拦截事件,交由自身对象的 onTouchEvent 方法处理
    }
}

ViewGroup 的该方法与 Activity 的类似,只是新添了一个 onInterceptTouchEvent 方法。当事
件传入时,首先会调用 onInterceptTouchEvent。
如果该方法返回了 FALSE(表示不拦截),则交给子 View 去调用 dispatchTouchEvent()方

如果该方法返回了 TRUE(表示拦截),则直接交给该 ViewGroup 对象的 onTouchEvent(ev)
方法处理,具体是否能处理以 onTouchEvent()的实际情况为准。
实 际 上 , 在 onInterceptTouchEvent 返 回 TURE 表 示 拦 截 时 , 实 际 调 用 的 是
super.dispatchTouchEvent 方法,即 View 的该方法,进而由该方法调用 onTouchEvent.
View 的 dispatchTouchEvent 方法

// View 中该方法的核心部分伪代码
public boolean dispatchTouchEvent(MotionEvent ev) {
//如果该对象的监听成员变量不为空,则会调用其 onTouch 方法,
    if (mOnTouchListener != null && mOnTouchListener.onTouch(this, event)) {
        return true; // 若 onTouch 方 法 返 回 TRUE , 则 表 示 消 费 了 该 事 件 , 则
        dispachtouTouchEvent 返回 TRUE,让其调用者知道该事件已被消费。
    }
    return onTouchEvent(ev); //若监听成员为空或 onTouch 没有消费该事件,则调用对象自身的        onTouchEvent 方法处理。
}

从该方法的核心逻辑中可以看到,事件传递进来后,首先会对 mOnTouchListener 判空,如
果之前 Set 了 Listener,则会调用其 onTouch 方法。
若 onTouch 方法返回 TRUE,则 dispatchTouchEvent 也会返回 TRUE,表示消费该事件。
若 onTouch 方法返回 FALSE,或者 mOnTouchListener 本来就是空,则调用自身的 onTouchEvent()
来处理,是否消费事件,可以由其返回值判断。
实际上,在 View 的 onTouchEvent 方法中,如果设置了 onClickListener 监听对象,则会调用
其 onClick 方法。
在 同 时 设 置 了 onTouchListener 与 onClickListener 对 象 的 情 况 下 , 正 是 由 于 View 的
dispacthTouchEvent 方法会先调用 mOnTouchListener 的 onTouch,才会调用 onTouchEvent 方法,
所以 onTouchListener 对象的 onTouch 方法是优先于 onClickListener 对象的 onClick 方法调用
的。这里只简单描述结论,具体源码请查看本文对应的高级篇内容。
小节:dispatchTouchEvent 方法
回顾上面 Activity、ViewGroup 和 View 中的 dispatchTouchEvent 方法,它们大体都可以分为
两部分,前一部分是交由子 View 的 dispatchTouchEvent 方法或 onTouch 方法进行处理,后一
部分是交给自身的 onTouchEvent 方法处理。这样理解的话,就非常便于记忆了。
为了便于记忆和理解,可以将各组件的 dispatchTouchEvent 方法分为两部分:
子 View 的 dispatchTouchEvent 或 onTouch 方法
自身的 onTouchEvent 方法
这个结构有点类似于递归的过程,就是组件的 dispatchTouchEvent 会自用子组件的同名方法,
子组件一样会调用子子组件的同名方法,直到递归到底,然后在从递归底部返回上层,直到
返回到最上层,整个过程结束。或者在这个过程中,事件传递到某个子 View,该子 View 决
定处理该事件,则事件交给其自身的 onTouchEvent 方法处理,如果 onTouchEvent 方法处理
不了,再交由父组件的同名方法处理,直到向上传递到顶层结束。
于是,就有了很多教程里的 U 型图。

Android触摸事件分发机制(一),android,android

安卓分发事件 U 型图
从 U 型图中可以发现,其实安卓事件分发的主体思路非常简单,即由父组件不断向子组件
分发,若子组件能够处理,则立刻返回。若子组件都不处理,那传递到底层的子组件,再返
回回来。这个过程类似上面说的递归的过程。
这 里 对 这 个 U 型 图 做 一 下 说 明 , 先 看 图 中 左 上 角 , 事 件 传 到 Activity , 首 先 调 用 其
dispatchTouchEvent 方法,其会传递给子 View 处理,该子 View(在图中是 ViewGroup)会调
用其 dispatchTouchEvent 方法,如果该方法被覆写直接返回 TRUE,则立即返回 Activity,表示
已 经 消 费 事 件 。 如 果 该 方 法 没 有 被 覆 写 或 调 用 了 super 的 同 名 方 法 , 则 会 调 用
onInterceptTouchEvent 方法,如果该方法返回 TRUE 拦截事件,则交给自身的 onTouchEvent
处 理 , 如 果 该 方 法 返 回 FALSE 不 拦 截 , 则 继 续 传 给 子 子 View ( 图 中 是 View ) 的
dispatchTouchEvent 方法处理。此时,再看看这个 U 型图,该递归调用已经到底了,若在该
方法中的 onTouchListener 方法不处理,则调用自身的 onTouchEvent 处理。若还是处理不了,
则从递归底部向上返回,依次调用 ViewGroup 的、Activity 的 onTouchEvent 方法。
实 际 上 , 用 这 个 U 型 图 来 描 述 安 卓 的 事 件 分 发 机 制 并 不 一 定 准 确 , 因 为 同 一 对 象 的
dispatchTouchEvent 方法实际是包含了另外几个方法的(Activity 与 View 只包含 onTouchEvent),
但是在这个图中,却是将几个方法分别画在不同的框中。所以通过该 U 型图来理解事件分
发机智是不准确的。但是对于部分读者可能会有所帮助。要准确理解事件调用机制,还是应
该回到上面,查看三个核心方法的核心逻辑,就能够准确理解。
强调说明,安卓事件分发的‘向上’与‘向下‘传播,不要与面向对象程序语言中基类与子类关系,
或子类向上调用父类方法等概念搞混淆。对于安卓事件分发的‘向上’与‘向下‘传播,这里的上
与下,是指在’递归‘调用过程中的上与下(也体现到 U 型图里的上与下)。这个概念,体现到布局中,就是外与内。即这里所说的事件’向下‘传播,等同于在布局上,由外向内传播,
而’向上’传播,等同于在布局上,由内向外传播。
在面向对象程序语言中,对于子类覆盖父类方法,或子类调用父类方法,这些‘上’与‘下’的关
系,在布局层面上并没有跨越布局层次,不要与事件传播的方向概念相混淆。
拦截方法 onInterceptTouchEvent
该方法是 ViewGroup 类对象所独有的,用于对事件进行提前拦截。在一般情况下,该方法是
默认返回 FALSE 的,即不拦截。
如果自定义的 ViewGroup 希望拦截事件,不希望事件继续往子 View 传播,可以覆写该方法,
返回 TRUE,即可阻止向下的传播过程。
实际上,从上面的核心逻辑的伪代码中可以看出,在 ViewGroup 调用 dispatchTouchEvent 后,
肯定会调用该方法,根据该方法的返回值来确定如何处理。若该方法返回 True,则会将事件
拦截掉,就给自身的 onTouchEvent 处理。如果返回 False,则继续传递给 child 执行分发流程。
处理方法 onTouchEvent
该方法主要对事件进行处理,若返回 True 表示已经处理了事件,若返回 False 则表示没有对
事件进行处理,需要继续传递事件。一般情况下,默认为 FALSE。在 View 的 onTouchEvent
方法中,如果设置了 onClickListener 监听对象,则会调用其 onClick 方法。

6. 总结

本文在介绍了事件分发基本概念的基础上,介绍了负责参与事件分发的核心方法,包括
dispatchTouchEvent()、onInterceptTouchEvent 与 onTouchEvent 方法。通过伪代码的形式介绍
了这些方法的核心逻辑,重点分析了在 Activity、ViewGroup 与 View 中的 dispatchTouchEvent
方法。它们三者中的该方法结构类似,都是先调用子 View 的同名方法或者 listener 方法,然
后再调用自身的 onTouchEvent 方法。
这些方法在调用关系中体现了一个类似‘递归’的调用过程,通过 dispatchTouchEvent 将事件
传 递 下 去 , 又 通 过 onTouchEvent 将 事 件 传 递 上 来 。 中 间 的 这 一 过 程 可 以 通 过 让
onInterceptTouchEvent 方法(对于 ViewGroup),或者另外的负责分发的方法返回 TRUE,均
可以提前终止这一类似’递归‘的调用过程,进而让事件的处理符合我们的预期。

 Android触摸事件分发机制(一),android,android

 文章来源地址https://www.toymoban.com/news/detail-610424.html

到了这里,关于Android触摸事件分发机制(一)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 高级UI之Android事件分发机制原理及源码分析

    在 Android 中, 事件分发机制是一块很重要的知识点, 掌握这个机制能帮你在平时的开发中解决掉很多的 View 事件冲突问题,这个问题也是面试中问的比较多的一个问题了, 本篇就来总结下这个知识点。 Android 中页面上的 View 是以树型结构显示的,View 会重叠在一起,当我们

    2024年02月08日
    浏览(43)
  • Flutter中为控件添加交互,带你一起探究Android事件分发机制

    ), ); } } 代码运行效果如图: 2.父widget管理widget的状态 对于父widget来说,管理状态并告诉其子widget何时更新通常是最有意义的。 例如,IconButton允许您将图标视为可点按的按钮。 IconButton是一个无状态的小部件,因为我们认为父widget需要知道该按钮是否被点击来采取相应的处理

    2024年04月11日
    浏览(44)
  • Android 事件分发

    本篇文章主要简单介绍下Android中的事件分发,和大家一起学习,进步,有问题也希望大家及时指证修改. 1: onClick和OnTouch 首先我们在单独的activity中添加个按钮button.增加点击事件setOnClickListener: 接着添加OnTouch: 我们执行下点击事件.可以看到输出如下: 2024-04-09 20:54:11.219 17770-17770/?

    2024年04月11日
    浏览(51)
  • Android 事件分发介绍

    目录 一、目的 二、环境 三、相关概念 3.1 事件分发 四、详细设计 4.1应用布局 4.1.1 应用布局结构 4.1.2 LayoutInspector 4.2 关键View方法 4.2.1 相关View 4.2.2 相关方法 4.2.3 View与方法关系 4.3 事件分发概念图 4.3.1 事件分发类图 4.3.2 事件分发模型图 4.4 Activity组件 4.4.1 Activity-dispatchTouch

    2024年02月03日
    浏览(40)
  • Android事件分发-基础原理和场景分析

    作者:京东零售 郭旭锋 和其他平台类似,Android 中 View 的布局是一个树形结构,各个 ViewGroup 和 View 是按树形结构嵌套布局的,从而会出现用户触摸的位置坐标可能会落在多个 View 的范围内,这样就不知道哪个 View 来响应这个事件,为了解决这一问题,就出现了事件分发机制

    2023年04月21日
    浏览(42)
  • Android 11 定制系统全局监听触摸事件接口

    1.定义创建aidl接口(由于需要回调这里优先需要增加一个回调接口 ) frameworksbasecorejavaandroidappIOnTouchListener.aidl package android.app; oneway interface IOnTouchListener {      void onTouchEvent( int action); }   2.新增调用接口 在 base/core/java/android/view/IWindowManager.aidl 修改如下: import android.ap

    2023年04月08日
    浏览(55)
  • Android进阶 View事件体系(二):从源码解析View的事件分发

    本篇文章为总结View事件体系的第二篇文章,前一篇文章的在这里:Android进阶 View事件体系(一):概要介绍和实现View的滑动 本篇文章将专注于介绍View的点击事件的分发,介绍的内容主要有: 点击事件的传递原则 解析Activity的构成 源码解析View的事件分发 源码解析View对点击

    2024年02月06日
    浏览(46)
  • systemserver的inputdispatcher直接产生CANCEL事件原理分析-讨厌的android触摸面试题

    上一个blog已经重点讲解了app层面自己产生的Cancel触摸事件,大概产生的原理如下: 上一个blog地址:https://blog.csdn.net/learnframework/article/details/124086882 即可以看出来,在服务端systemserver其实传递的触摸事件依然是move,只是move事件到了app端后,由于app端自己的业务把这个传递的

    2024年02月09日
    浏览(43)
  • Android 关于View事件分发(onTouch、onTouchEvent、onClick、onLongClick的关系及原理)(一)

    事件 含义 ACTION_DOWN 手指初次碰到屏幕时触发 ACTION_MOVE 手指在屏幕上滑动时触发(ps:会多次触发,看源码时同一块代码应该看多次去理解) ACTION_UP 手指离开屏幕时触发 ACTION_CANCEL 事件被上层拦截时触发   关于ACTION_MOVE事件,手指在屏幕上滑动时会触发多次,对于这个点,看源码

    2024年01月24日
    浏览(40)
  • 【Android】Touch 事件分发逻辑梳理和避坑逻辑(上层设置了setOnTouchListener的事件监听但是没有起作用的原因)

    在项目中发现我明明在最上层的 activity 中的一个 DrawerLayout 对象设置了如下代码: 该代码的作用的在点击的时候显示一下 TopBar 一个自定义的UI组件。 但是发现点击超级白板(你可以理解为一个画板组件)部分上述代码就没有触发。这是怎么回事呢? 首先针对Touch事件的分发机

    2024年02月11日
    浏览(37)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包