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 的事件分发机制主要涉及 Activity
、ViewGroup
和 View
三者对触摸事件的传递和处理流程。该机制通过三个主要方法进行分发和拦截:
(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更适用于需要频繁刷新、刷新时数据处理量很大的页面
相关文章:

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()和onTouchEve…...

攻防世界GFSJ1229 Three
题目编号:GFSJ1229 解题过程 1. 附件下载是三个压缩包A.zip B.zip C.zip和一个python程序Three.py 2. A.zip可以直接解压出来,内容如下: 2022-08-27 20:16:04.246131 Func A0*X0B0 2022-08-27 20:16:05.116859 Read_Data A0.txt->A0(28829613228…...

2023 icpc杭州(M,J,D,G,H)
文章目录 [M. V-Diagram](https://codeforces.com/gym/104976/problem/M)[J. Mysterious Tree](https://codeforces.com/gym/104976/problem/J)[D.Operator Precedence](https://codeforces.com/gym/104976/problem/D)[G. Snake Move](https://codeforces.com/gym/104976/probl…...

在CentOS 7上安装Alist
在CentOS 7上安装Alist 的步骤如下: 1. 卸载旧版本 如果你之前安装过旧版本的Docker,可以先卸载它: sudo yum remove docker docker-common docker-snapshot docker-engine2. 安装依赖包 确保你的系统是最新的,并安装必要的依…...

【C/C++】memcpy函数的模拟实现
零.导言 上一篇博客我们学习了memcpy函数,不妨我们现在尝试模拟实现memcpy函数的功能。 一.实现memcpy函数的要点 memcpy函数是一种C语言内存函数,可以按字节拷贝任意类型的数组,因此我们自定义的模拟函数需要两个无类型的指针参数ÿ…...

嵌入式开发之线程互斥
目录 互斥锁初始化-pthread_mutex_init 申请锁-pthread_mutex_lock 释放锁-pthread_mutex_unlock 同步 VS 互斥 临界资源:一次只允许一个任务(进程、线程)访问的共享资源,不允许多个任务同时访问的。 临界区:访问临界区的代码 互斥机制:mutex互斥锁,任务访问临界资…...

JavaScript 变量作用域与函数调用机制:var 示例详解
JavaScript 变量作用域与函数调用机制:var 示例详解 在 JavaScript 中,作用域和闭包是理解变量生命周期和行为的核心概念。通过以下这段代码,我们将详细分析如何在不同的作用域内使用 var 关键字,并解释相关的变量访问规则 代码解…...

Linux(CentOS)安装 JDK
1、下载 JDK 官网:https://www.oracle.com/ 2、上传 JDK 文件到 CentOS,使用FinalShell远程登录工具,并且使用 root 用户登录 3、解压 JDK 创建目录 /export/server mkdir -p /export/server 解压到目录 /export/server tar -zxvf jdk-17…...

AI产品经理实战手册:策略、开发与商业化指南
通过《AI产品经理手册》,将可以了解不同类型的AI,如何将AI整合到产品或业务中,以及支持创建AI产品或将AI集成到现有产品所需的基础设施。熟悉实践管理AI产品开发流程、评估和优化AI模型,以及应对与AI产品相关的复杂伦理和法律问题…...

【大语言模型】ACL2024论文-06 探索思维链COT在多模态隐喻检测中的应用
【大语言模型】ACL2024论文-06 探索思维链COT在多模态隐喻检测中的应用 目录 文章目录 【大语言模型】ACL2024论文-06 探索思维链COT在多模态隐喻检测中的应用目录摘要研究背景问题与挑战如何解决创新点算法模型1. 知识总结模块(Knowledge Summarization Module&…...

Linux之初体验
目录 第1关:1-Linux初体验 第2关:1-Linux常用命令 第3关:1-Linux 查询命令帮助语句 第4关:2--查询命令-locate 第5关:2--查询命令-which/whereis 第6关:2--查询命令-find 第7关:3-Linux文…...

现代化水电管理:Spring Boot在大学城的实践
2相关技术 2.1 MYSQL数据库 MySQL是一个真正的多用户、多线程SQL数据库服务器。 是基于SQL的客户/服务器模式的关系数据库管理系统,它的有点有有功能强大、使用简单、管理方便、安全可靠性高、运行速度快、多线程、跨平台性、完全网络化、稳定性等,非常…...

黑马官网2024最新前端就业课V8.5笔记---HTML篇
Html 定义 HTML 超文本标记语言——HyperText Markup Language。 标签语法 标签成对出现,中间包裹内容<>里面放英文字母(标签名)结束标签比开始标签多 /拓展 : 双标签:成对出现的标签 单标签:只有开…...

GS-Blur数据集:首个基于3D场景合成的156,209对多样化真实感模糊图像数据集。
2024-10-31,由韩国首尔国立大学的研究团队创建的GS-Blur数据集,通过3D场景重建和相机视角移动合成了多样化的真实感模糊图像,为图像去模糊领域提供了一个大规模、高覆盖度的新工具,显著提升了去模糊算法在真实世界场景中的泛化能力…...

Linux下Java的多种方式安装
Linux下Java的多种方式安装 博客: www.lstar.icu 开源地址 Gitee 地址: https://gitee.com/lxwise/iris-blog_parent Github 地址: https://github.com/lxwise/iris-blog_parent 序言 Java是一门面向对象的编程语言,不仅吸收了…...

Android Studio:connect time out
参考:Android Studio:connect time out_android studio connection timed out-CSDN博客...

A014-基于Spring Boot的家电销售展示平台设计与实现
🙊作者简介:在校研究生,拥有计算机专业的研究生开发团队,分享技术代码帮助学生学习,独立完成自己的网站项目。 代码可以查看文章末尾⬇️联系方式获取,记得注明来意哦~🌹 赠送计算机毕业设计600…...

数学期望和联合概率密度
数学期望的定义 数学期望是描述随机变量平均趋势的一个重要统计量。根据随机变量的类型(离散或连续),数学期望的定义有所不同。 离散型随机变量的数学期望: 若离散型随机变量 X X X取值为 x 1 , x 2 , … , x n , … x_1,x_2,\do…...

萤石私有化设备视频平台EasyCVR视频融合平台如何构建农业综合监控监管系统?
现代农业的迅速发展中,集成监控管理系统已成为提高农业生产效率和优化管理的关键工具。萤石私有化设备视频平台EasyCVR,作为一个具有高度可扩展性、灵活的视频处理能力和便捷的部署方式的视频监控解决方案,为农业监控系统的建设提供了坚实的技…...

【MongoDB】Windows/Docker 下载安装,MongoDB Compass的基本使用、NoSQL、MongoDB的基础概念及基础用法(超详细)
文章目录 Windows下载MongoDB Compass使用NoSQL的基本概念MongoDB常用术语MongoDB与RDBMS区别MongoDB的CRUD 更多相关内容可查看 Docker安装MongoDB可查看:Docker-安装MongoDB Windows下载 官网下载地址:https://www.mongodb.com/try/download/communi…...

微信小程序-自定义导航栏
一.自定义导航栏 1.JSON文件中配置"navigationStyle": “custom” "navigationStyle": "custom"2.给导航栏设置轮播图 <swiper class"custom-swiper" indicator-dots autoplay interval"2000"> <swiper-item>…...

vue中强制更新视图
vue3 中强制更新视图 方式 通过 $forceUpdate 与 vue2 相似 import {getCurrentInstance} from vueconst internalInstance getCurrentInstance() //操作数据后更新视图 internalInstance.ctx.$forceUpdate()通过 key 值改变更新 <compName :key"key" />co…...

mqsql 场景函数整理
场景1:行数据取多字段,取到有值为止 解决方案: mysql coaleace函数 场景2:字符串拼接文本并换行 解决方案1: mysql concate() 和char(10) 场景3:获取单汉字首拼 解决方案1:单汉字获取首拼 解…...

【AI日记】24.11.05 向量数据库 weaviate、混合搜索、多语言搜索、明确自己的南京
【AI论文解读】【AI知识点】【AI小项目】【AI战略思考】【AI日记】 工作 工作1 内容:学习deeplearning.ai的课程课程:Vector Databases: from Embeddings to Applications时间:6小时评估:不错,完成收获:学…...

Scrapy入门
Scrapy 是用 Python 实现的一个为了爬取网站数据、提取结构性数据而编写的应用框架。 安装scrapy pip install scrapy2.5.0 1.新建 Scrapy项目 scrapy startproject mySpider # 项目名为mySpider 2.进入到spiders目录 cd mySpider/mySpider/spiders 3.创建爬虫 scrapy gensp…...

Ubantu/Linux 采用Repo或Git命令报错!!
简言: 遇事还是不要慌,出现这些问题,很正常的;如果那些你不需要,只是需要回到某一个版本,那么就是需要,方法可以尝试回退节点,也可以尝试强行merge合入冲突,或找到冲突文件解决,但这些方法都非常的繁杂且不实用。以下是研究出来的解决方案! 记得随时使用git statu…...

C++简单工厂模式
什么是简单工厂模式? 简单工厂模式属于创造型模式,而工厂就是负责生产和创造的,顾名思义。建立对象的类就如一个工厂,而需要被建立的对象就是一个个产品;在工厂中加工产品,使用产品的人,不用在…...

讲讲 kafka 维护消费状态跟踪的方法?
大家好,我是锋哥。今天分享关于【讲讲 kafka 维护消费状态跟踪的方法?】面试题?希望对大家有帮助; 讲讲 kafka 维护消费状态跟踪的方法? 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 在 Kafka 中&#x…...

MySQL 和 PostgreSQL 的对比概述
MySQL 和 PostgreSQL 是两种广泛使用的开源关系型数据库管理系统(RDBMS),它们各自有其特点和优缺点。以下将从多个方面对它们进行详细比较。 1. 介绍 MySQL: MySQL 由瑞典公司 MySQL AB 开发,2008 年被 Sun Microsyst…...

【Python单元测试】pytest框架单元测试 配置 命令行操作 测试报告 覆盖率
单元测试(unit test),简称UT。本文将介绍在Python项目中,pytest测试框架的安装,配置,执行,测试报告与覆盖率 pytest简介 pytest是一款流行的,简单易上手的单元测试框架,…...