Android View事件分发

目录

1.什么是View事件分发?

2.事件的类型

3.事件的发生

4.事件分发的方法

4.1 dispatchTouchEvent()

4.2 onTouchEvent()

4.3 onInterceptTouchEvent()

5.滑动冲突

5.1 外部拦截法

5.2内部拦截法

6.onTouch的执行高于onClick

7. onTouch()和onTouchEvent()的区别

8.实现View的滑动

9.SurfaceView和View


个人对于Android事件分发的知识点总结,相对零散,源码了解的不够多,就没有展开说明。主要总结关于Android中的

View事件是如何分发的?如何传递的?如何消耗的?

1.什么是View事件分发?

将点击事件(MotionEvent)传递到某个具体的View或者ViewGroup处理的过程。事件分发是向下传递的,也就是父到子的顺序,当用户触摸屏幕时(View或ViewGroup派生的控件),将产生点击事件(Touch事件)。Touch事件相关细节(发生触摸的位置、时间、历史记录、手势动作等)被封装成MotionEvent对象。

2.事件的类型

这里的事件指的就是点击事件(MotionEvent),主要分为以下几个类型:

(1)ACTION_DOWN:手指刚碰到屏幕的时候;

(2)ACTION_UP:手指离开屏幕的时候;

(3)ACTION_CANCEL:结束事件的时候(子View处理事件的过程中,父View拦截事件,收回处理权,此时的子View会收到这个事件);

(4)ACTION_MOVE:手指在屏幕上进行滑动的时候;

3.事件的发生

  • 在一次屏幕触摸事件中,ACTION_DOWN和ACTION_UP者两个事件是必须的,而ACTION_MOVE视情况而定,如果用户仅是点击了一下屏幕,那么可能只会监测到按下和抬起的动作。
  • 通过MotionEvent可以获得事件发生的x和y坐标,getX和getY返回的是相当于当前View左上角的X和Y坐标,getRawX和getRawY返回的是相当于手机屏幕左上角的X和Y坐标
  • 事件分发的本质就收将点击事件(MotionEvent)向某个View进行传递并最终得到处理,即当一个点击事件发生后,系统需要将这个事件传递给一个具体的View去处理。这个事件传递的过程就是分发过程。
  • Android事件分发机制的本质是要解决,点击事件由哪个对象发出,经过哪些对象,最终达到哪个对象并最终得到处理。
  • 事件分发流程:Native层 --> ViewRootImpl层 --> DecorView层 --> Activity层 --> ViewGroup层 --> View层
  • 当一个点击事件发生时,调用顺序如下
    • 1.事件最先传到Activity的dispatchTouchEvent()进行事件分发
    • 2.调用Window类实现类PhoneWindow的superDispatchTouchEvent()
    • 3.调用DecorView的superDispatchTouchEvent()
    • 4.最终调用DecorView父类的dispatchTouchEvent(),即ViewGroup的dispatchTouchEvent(),再由ViewGroup传递到View
    • 5.在ViewGroup中通过onInterceptTouchEvent()对事件传递是否进行拦截

4.事件分发的方法

Android 的事件分发机制主要涉及 ActivityViewGroupView 三者对触摸事件的传递和处理流程。该机制通过三个主要方法进行分发和拦截:

(1)dispatchTouchEvent():分发点击事件;

(2)onTouchEvent():处理点击事件;

(3)onInterceptTouchEvent():拦截某个事件;

4.1 dispatchTouchEvent()

作用对象:Activity,ViewGroup和View 里面的源码有兴趣可以去看看

它会返回一个布尔值。

  • 返回true
    • 消费事件
    • 事件不会往下传递
    • 后续事件(Move、Up)会继续分发到该View
  • 返回false
    • 不消费事件
    • 事件往下传递
    • 将事件回传给父控件的onTouchEvent()处理
      • Activity例外:返回false=消费事件
    • 后续事件(Move、Up)会继续分发到该View(与onTouchEvent()区别)
4.2 onTouchEvent()

作用对象:Activity,ViewGroup和View

作用:在dispatchTouchEvent()内部调用,用于处理点击事件。它会返回一个布尔值。

  • 返回true
    • 自己处理(消费)该事情
    • 事件停止传递
    • 该事件序列的后续事件(Move、Up)让其处理;
  • 返回false(同默认实现:调用父类onTouchEvent())
    • 不处理(消费)该事件
    • 事件往上传递给父控件的onTouchEvent()处理
    • 当前View不再接受此事件列的其他事件(Move、Up);
4.3 onInterceptTouchEvent()

该方法表示是否对事件进行拦截,只有在ViewGroup里才有此方法,代表这个事件是否往下传递

  • true-当前ViewGroup希望该事件不再传递给其child,而是希望自己处理。
  • false-当前ViewGroup不准备拦截该事件,事件正常向下分发给其child。

当用户进行触摸/点击事件后,他们的执行顺序

dispatchTouchEvent()--->onInterceptTouchEvent()--->onTouchEvent()

一般来说事件是这样传递的:

  • 从Activity A---->ViewGroup B--->View C,从上往下调用dispatchTouchEvent()
  • 再由View C--->ViewGroup B --->Activity A,从下往上调用onTouchEvent()

5.滑动冲突

当父容器与子 View 都可以滑动时,就会产生滑动冲突。解决 View 之间的滑动冲突的方法分为两种,分别是外部拦截法内部拦截法

5.1 外部拦截法

即在父容器进行选择性拦截操作,即使用onInterceptTouchEvent方法。

例如对指定方向的滑动进行拦截,当满足条件时,返回true,表示此事件被拦截,不进行事件下发

tips:1.ACTION_DOWN 事件需要返回 false,父容器不能进行拦截,否则根据 View 的事件分发机制,后续的 ACTION_MOVE 与 ACTION_UP 事件都将默认交由父容器进行处理。

2.原则上 ACTION_UP 事件也需要返回 false,如果返回 true,那么子 View 将接收不到 ACTION_UP 事件,子 View 的onClick 事件也无法触发。

例子:实现一个自定义ScrollView

class CustomScrollView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null
) : ScrollView(context, attrs) {private var lastX = 0fprivate var lastY = 0foverride fun onInterceptTouchEvent(ev: MotionEvent): Boolean {when (ev.action) {MotionEvent.ACTION_DOWN -> {lastX = ev.xlastY = ev.y// 不拦截,让子视图接收事件return false}MotionEvent.ACTION_MOVE -> {val deltaX = Math.abs(ev.x - lastX)val deltaY = Math.abs(ev.y - lastY)// 如果 Y 轴移动距离大于 X 轴,则父容器拦截return deltaY > deltaX}}return super.onInterceptTouchEvent(ev)}
}

在此实现中,onInterceptTouchEvent 方法会根据手指滑动的方向来决定是否拦截事件。如果纵向滑动距离更大,则 ScrollView 会拦截事件。

5.2内部拦截法

内部拦截法在子视图(如 RecyclerView)中处理,通过对子视图的 onTouchEvent 进行控制,让子视图自己决定是否请求父容器不要拦截。

例子:自定义一个RecycleView

class CustomRecyclerView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null
) : RecyclerView(context, attrs) {private var lastX = 0fprivate var lastY = 0foverride fun onTouchEvent(ev: MotionEvent): Boolean {when (ev.action) {MotionEvent.ACTION_DOWN -> {lastX = ev.xlastY = ev.y// 请求父容器不要拦截事件parent.requestDisallowInterceptTouchEvent(true)}MotionEvent.ACTION_MOVE -> {val deltaX = Math.abs(ev.x - lastX)val deltaY = Math.abs(ev.y - lastY)// 当检测到横向滑动时,允许父容器拦截if (deltaX > deltaY) {parent.requestDisallowInterceptTouchEvent(false)} else {parent.requestDisallowInterceptTouchEvent(true)}}}return super.onTouchEvent(ev)}
}

在这个示例中,RecyclerView 会在接收到横向滑动时调用 parent.requestDisallowInterceptTouchEvent(false),使父容器可以拦截事件;而在纵向滑动时调用 parent.requestDisallowInterceptTouchEvent(true),让父容器不拦截。

总结

  • 外部拦截法适合在父容器中根据滑动方向决定是否拦截事件。
  • 内部拦截法让子视图根据滑动方向来请求父容器是否拦截事件。

6.onTouch的执行高于onClick

因为每当控件被点击时:

  • 如果在回调onTouch()里返回false,就会让dispatchTouchEvent方法返回false,那么就会执行onTouchEvent();如果回调了setOnClickListener()来给控件注册点击事件的话,最后会在performClick()方法里回调onClick()。
    • onTouch()返回false(该事件没被onTouch()消费掉) = 执行onTouchEvent() = 执行OnClick()
  • 如果在回调onTouch()里返回true,就会让dispatchTouchEvent方法返回true,那么将不会执行onTouchEvent(),即onClick()也不会执行;
    • onTouch()返回true(该事件被onTouch()消费掉) = dispatchTouchEvent()返回true(不会再继续向下传递) = 不会执行onTouchEvent() = 不会执行OnClick()
  • onTouch->onTouchEvent->onClick

7. onTouch()和onTouchEvent()的区别

  • 这两个方法都是在View的dispatchTouchEvent中调用,但onTouch优先于onTouchEvent执行。
  • 如果在onTouch方法中返回true将事件消费掉,onTouchEvent()将不会再执行。

8.实现View的滑动

  • layout:对View进行重新布局定位。在onTouchEvent()方法中获得控件滑动前后的偏移。然后通过layout方法重新设置。
  • offsetLeftAndRight和offsetTopAndBottom:系统提供上下/左右同时偏移的API。onTouchEvent()中调用
  • LayoutParams: 更改自身布局参数(设置margin或者父容器的padding等等)
  • scrollTo/scrollBy: 本质是移动View的内容,需要通过父容器的该方法来滑动当前View
  • Scroller: 平滑滑动,通过重载computeScroll(),使用scrollTo/scrollBy完成滑动效果。
  • 属性动画: 动画对View进行滑动

9.SurfaceView和View

SurfaceView是从View基类中派生出来的显示类,他和View的区别有:

  • View需要在UI线程对画面进行刷新,而SurfaceView可在子线程进行页面的刷新
  • View适用于主动更新的情况,而SurfaceView适用于被动更新,如频繁刷新,这是因为如果使用View频繁刷新会阻塞主线程,导致界面卡顿
  • SurfaceView在底层已实现双缓冲机制,而View没有,因此SurfaceView更适用于需要频繁刷新、刷新时数据处理量很大的页面

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/466669.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

uniapp 实现瀑布流

效果演示 组件下载 瀑布流布局-waterfall - DCloud 插件市场

6.qsqlquerymodel源码分析

目录 继承关系入口浅析qsqlquery刷新数据 扩展列或者移除列以及取别名读取数据与增减行读取数据 下一章节:如何使用qsqlquerymodel 与 qtableview实现自定义表格 继承关系 qsqlquerymodel 继承与qabstracttablemodel 入口 负责填充数据 void QSqlQueryModel::s…

Vue3中使用LogicFlow实现简单流程图

实现结果 实现功能&#xff1a; 拖拽创建节点自定义节点/边自定义快捷键人员选择弹窗右侧动态配置组件配置项获取/回显必填项验证历史记录&#xff08;撤销/恢复&#xff09; 自定义节点与拖拽创建节点 拖拽节点面板node-panel.vue <template><div class"node-…

Devops业务价值流:软件研发最佳实践

在当今快速迭代的软件开发环境中&#xff0c;DevOps业务价值流已成为推动软件研发高效与质量并重的关键实践。软件研发阶段作为产品生命周期的核心环节&#xff0c;其每一步都承载着将创意转化为现实的重要使命。在历经需求澄清的精准定位、架构设计的宏观规划以及项目初始化的…

wireshark工具使用

复制数据 1.右键展开整帧数据 2.复制“所有可见项目” mark标记数据 标记&#xff1a; 跳转&#xff1a; 保存成文件&#xff1a; 文件–>导出特定分组—>Marked packets only

管理 Elasticsearch 变得更容易了,非常容易!

作者&#xff1a;来自 Elastic Ken Exner Elasticsearch 用户&#xff0c;我们听到了你的心声。管理 Elasticsearch 有时会变得很复杂&#xff0c;面临的挑战包括性能调整、问题检测和资源优化。我们一直致力于简化你的体验。今天&#xff0c;我们宣布了自收购 Opster 以来的一…

深度洞察| 超6亿银发精准流量,40+泛银发群体参与消费三大变化

作者 | NewAgingPro团队 前言 9月24日&#xff0c;AgeClub成立银发流量及场景联盟&#xff08;简称&#xff1a;AgeMCN&#xff09;&#xff0c;助力银发经济高质量发展。 10月11日&#xff0c;AgeClub发布《2024银发流量全景洞察报告》&#xff0c;探索银发流量发展新模式…

Spring Boot——日志介绍和配置

1. 日志的介绍 在前面的学习中&#xff0c;控制台上打印出来的一大堆内容就是日志&#xff0c;可以帮助我们发现问题&#xff0c;分析问题&#xff0c;定位问题&#xff0c;除此之外&#xff0c;日志还可以进行系统的监控&#xff0c;数据采集等 2. 日志的使用 在程序中获取日…

Redis 组网方式入门

文章目录 一、组网方式1. 单实例模式描述优点缺点适用场景 2. 主从复制模式&#xff08;Master-Slave Replication&#xff09;描述优点缺点适用场景基于docker的redis主从复制1. 配置主节点2. 配置从节点3. 查看节点状态4. 验证主从数据同步5. 查看同步进度 3. 哨兵模式&#…

信号-2-信号捕捉

相关概念&#xff1a;递达 未决 / 阻塞 忽略 阻塞 vs 忽略 阻塞&#xff1a; 如果指定信号信号被阻塞&#xff0c; block期间该信号不能被递达&#xff0c;一直在pending表中。知道block被撤销后&#xff0c; 该信号才能递达&#xff0c;递达后对应pending位置置零。 忽…

(蓝桥杯C/C++)——基础算法(下)

目录 一、时空复杂度 1.时间复杂度 2.空间复杂度 3.分析技巧 4.代码示例 二、递归 1.递归的介绍 2.递归如何实现 3.递归和循环的比较 4.代码示例 三、差分 1.差分的原理和特点 2.差分的实现 3.例题讲解 四、枚举 1.枚举算法介绍 2.解空间的类型 3. 循环枚举解…

【极限编程(XP)】

极限编程&#xff08;XP&#xff09;简介 定义与核心价值观&#xff1a;极限编程&#xff08;Extreme Programming&#xff0c;XP&#xff09;是一种轻量级、敏捷的软件开发方法。它强调团队合作、客户参与、持续测试和快速反馈等价值观&#xff0c;旨在提高软件开发的效率和质…

如何编写安全的 Go 代码

原文&#xff1a;Jakub Jarosz - 2024.11.02 在编写 Go 代码时&#xff0c;如何时刻考虑安全性&#xff1f;要在一篇简短的文章中回答这个问题似乎不太可能。因此&#xff0c;我们将把范围缩小到一些具体做法上。 这些实践如果持续应用&#xff0c;将有助于我们编写健壮、安全…

Go八股(Ⅳ)***slice,string,defer***

***slice&#xff0c;string&#xff0c;defer*** 1.slice和arry的区别 arry&#xff1a; Go语言中arry即为数据的一种集合&#xff0c;需要在声明时指定容量和初值&#xff0c;且一旦声明就长度固定&#xff0c;访问时按照索引访问。通过内置函数len可以获取数组中的元素个…

使用 Mac 数据恢复从 iPhoto 图库中恢复照片

我们每个人都会遇到这种情况&#xff1a;在意识到我们不想丢失照片之前&#xff0c;我们会永久删除 iPhoto 图库中的一些照片。永久删除这些照片后&#xff0c;是否可以从 iPhoto 图库中恢复照片&#xff1f;本文将指导您使用免费的 Mac 数据恢复软件从 iPhoto 中恢复照片。 i…

Spark 的介绍与搭建:从理论到实践

目录 一、分布式的思想 &#xff08;一&#xff09;存储 &#xff08;二&#xff09;计算 二、Spark 简介 &#xff08;一&#xff09;发展历程 &#xff08;二&#xff09;Spark 能做什么&#xff1f; &#xff08;三&#xff09;spark 的组成部分 &#xff08;四&…

Spring Boot2(Spring Boot 的Web开发 springMVC 请求处理 参数绑定 常用注解 数据传递 文件上传)

SpringBoot的web开发 静态资源映射规则 总结&#xff1a;只要静态资源放在类路径下&#xff1a; called /static (or /public or /resources or //METAINF/resources 一启动服务器就能访问到静态资源文件 springboot只需要将图片放在 static 下 就可以被访问到了 总结&…

Vue2中使用firefox的pdfjs进行文件文件流预览

文章目录 1.使用场景2. 使用方式1. npm 包下载,[点击查看](https://www.npmjs.com/package/pdfjs-dist)2. 官网下载1. 放到public文件夹下面2. 官网下载地址[点我,进入官网](https://github.com/mozilla/pdf.js/tags?afterv3.3.122) 3. 代码演示4. 图片预览5. 如果遇到跨域或者…

2024软件测试面试热点问题

&#x1f345; 点击文末小卡片 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 大厂面试热点问题 1、测试人员需要何时参加需求分析&#xff1f; 如果条件循序 原则上来说 是越早介入需求分析越好 因为测试人员对需求理解越深刻 对测试工…

C语言 | Leetcode C语言题解之第542题01矩阵

题目&#xff1a; 题解&#xff1a; /*** Return an array of arrays of size *returnSize.* The sizes of the arrays are returned as *returnColumnSizes array.* Note: Both returned array and *columnSizes array must be malloced, assume caller calls free().*/ type…