一、事件分发机制核心概念

1. 事件分发三要素

要素作用关键方法
事件(Event)用户触摸动作的封装MotionEvent
分发者负责将事件传递给下级dispatchTouchEvent()
拦截者决定是否截断事件传递(仅ViewGroup)onInterceptTouchEvent()
消费者最终处理事件的组件onTouchEvent()

2. 事件序列组成

二、事件分发流程全景图

1. 事件传递层级

2. 核心方法调用链

// Activity
public boolean dispatchTouchEvent(MotionEvent ev) {if (getWindow().superDispatchTouchEvent(ev)) {return true; // 事件被消费}return onTouchEvent(ev); // 默认处理
}// PhoneWindow
public boolean superDispatchTouchEvent(MotionEvent event) {return mDecor.superDispatchTouchEvent(event);
}// ViewGroup
public boolean dispatchTouchEvent(MotionEvent ev) {// 1. 检查拦截if (onInterceptTouchEvent(ev)) {return onTouchEvent(ev); // 拦截事件}// 2. 分发子Viewfor (View child : children) {if (child.dispatchTouchEvent(ev)) {return true; // 子View消费}}// 3. 自身处理return onTouchEvent(ev);
}// View
public boolean dispatchTouchEvent(MotionEvent event) {if (mOnTouchListener != null && mOnTouchListener.onTouch(this, event)) {return true; // 优先回调OnTouchListener}return onTouchEvent(event); // 默认处理
}

三、ViewGroup 的事件分发机制

1. 拦截决策流程

public boolean onInterceptTouchEvent(MotionEvent ev) {// 默认实现:不拦截return false;
}

2. 分发优先级规则

  1. Z轴顺序:后添加的子View优先(可通过setElevation()调整)

  2. 可见性:GONE状态的View不参与分发

  3. 点击区域:仅分发到触摸区域内的子View

  4. 拦截标志:一旦拦截,整个事件序列不再检查拦截

3. 事件分发伪代码

boolean dispatchTouchEvent(MotionEvent ev) {boolean handled = false;// 1. ACTION_DOWN时重置状态if (action == ACTION_DOWN) {resetTouchState();}// 2. 检查拦截final boolean intercepted;if (action == ACTION_DOWN || mFirstTouchTarget != null) {intercepted = onInterceptTouchEvent(ev);} else {intercepted = true; // 后续事件默认拦截}// 3. 未拦截时分发子Viewif (!intercepted) {for (View child : reverseChildren) {if (child.isInTouchArea(ev)) {if (child.dispatchTouchEvent(ev)) {mFirstTouchTarget = child; // 记录消费目标handled = true;break;}}}}// 4. 自身处理if (mFirstTouchTarget == null) {handled = onTouchEvent(ev);}return handled;
}

四、View 的事件处理机制

1. 事件处理优先级

2. onTouchEvent 核心逻辑

public boolean onTouchEvent(MotionEvent event) {// 1. 检查是否可用if (!isEnabled()) {return clickable; // 不可用时仍返回clickable状态}// 2. 处理不同事件类型switch (event.getAction()) {case MotionEvent.ACTION_DOWN:setPressed(true); // 设置按压状态break;case MotionEvent.ACTION_MOVE:if (!pointInView(event)) {removeTapCallback(); // 移出视图时取消点击}break;case MotionEvent.ACTION_UP:if (mHasPerformedLongPress) {break; // 长按已处理}performClick(); // 执行点击break;case MotionEvent.ACTION_CANCEL:setPressed(false); // 重置状态break;}return true; // 始终消费事件(如果可点击)
}

五、事件分发的核心规则

1. 事件序列连续性原则

  • 消费权绑定:消费ACTION_DOWN的View将接收整个事件序列

  • 拦截时机

    • ACTION_DOWN:可自由决定是否拦截

    • 后续事件:若未拦截DOWN,仍可拦截MOVE/UP

  • 状态一致性:View应在DOWN时初始化触摸状态

2. 返回值含义表

方法返回true返回false
dispatchTouchEvent()事件已消费事件未消费,继续传递
onInterceptTouchEvent()拦截事件,不再传递子View不拦截,继续传递子View
onTouchEvent()事件已处理事件未处理,回传给父View

六、滑动冲突解决方案

1. 冲突类型分类

类型示例场景解决方案
同方向冲突ScrollView嵌套ListView外部拦截法
不同方向冲突ViewPager内嵌横向RecyclerView内部拦截法
嵌套冲突多层嵌套的复杂布局定制分发策略

2. 外部拦截法(推荐)

public class ParentView extends ViewGroup {private float mLastX, mLastY;@Overridepublic boolean onInterceptTouchEvent(MotionEvent ev) {boolean intercepted = false;float x = ev.getX();float y = ev.getY();switch (ev.getAction()) {case MotionEvent.ACTION_DOWN:intercepted = false; // DOWN必须不拦截break;case MotionEvent.ACTION_MOVE:float dx = Math.abs(x - mLastX);float dy = Math.abs(y - mLastY);if (dx > dy && dx > touchSlop) {intercepted = true; // 横向滑动时拦截}break;case MotionEvent.ACTION_UP:intercepted = false;break;}mLastX = x;mLastY = y;return intercepted;}
}

3. 内部拦截法

public class ChildView extends View {@Overridepublic boolean dispatchTouchEvent(MotionEvent event) {float x = event.getX();float y = event.getY();switch (event.getAction()) {case MotionEvent.ACTION_DOWN:getParent().requestDisallowInterceptTouchEvent(true); // 禁止父容器拦截break;case MotionEvent.ACTION_MOVE:if (needParentIntercept()) {getParent().requestDisallowInterceptTouchEvent(false); // 允许父容器拦截}break;}return super.dispatchTouchEvent(event);}
}

七、核心要点

1. 高频问题清单

  1. 事件分发流程是怎样的?

    • 答:Activity -> Window -> DecorView -> ViewGroup -> View

    • 每个层级通过dispatchTouchEvent()向下传递

  2. onTouch和onTouchEvent的区别?

    • onTouch是View.OnTouchListener接口方法

    • onTouchEvent是View自身的处理方法

    • onTouch优先级高于onTouchEvent

  3. ACTION_CANCEL何时触发?

    • 当父容器拦截事件时发送

    • 用于重置View的触摸状态

  4. 如何解决滑动冲突?

    • 外部拦截法:重写父容器onInterceptTouchEvent()

    • 内部拦截法:子View调用requestDisallowInterceptTouchEvent()

  5. 为什么ACTION_DOWN特殊处理?

    • 它决定整个事件序列的接收者

    • 父容器在DOWN时必须给子View机会

2. 高级问题解析

Q:requestDisallowInterceptTouchEvent()原理?

// View.java
public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {if (mParent != null) {mParent.requestDisallowInterceptTouchEvent(disallowIntercept); // 递归向上}
}// ViewGroup.java
public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {mGroupFlags |= FLAG_DISALLOW_INTERCEPT; // 设置标志位if (mParent != null) {mParent.requestDisallowInterceptTouchEvent(disallowIntercept);}
}// 在ViewGroup的dispatchTouchEvent中
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {intercepted = onInterceptTouchEvent(ev); // 检查拦截
} else {intercepted = false; // 被子View禁止拦截
}

Q:事件分发中的设计模式?

  • 责任链模式:事件沿视图树传递,直到被处理

  • 模板方法模式:dispatchTouchEvent()定义处理框架

  • 观察者模式:OnTouchListener回调机制

Q:如何优化事件处理性能?

  1. 避免在事件方法中创建对象

  2. 使用getActionMasked()替代getAction()

  3. 对复杂手势使用GestureDetector

  4. 减少不必要的触摸状态更新

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

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

相关文章

从威胁检测需求看两类安全监测平台差异

在网络安全领域,针对不同场景的威胁检测需求,衍生处了多种技术架构的安全监测平台。尽管它们的目标均为“识别异常行为、阻断潜在威胁”,但根据其核心引擎的配置的技术侧重点,可大致分为两类:聚焦基础入侵检测的平台与…

useContext:React 跨组件数据共享的优雅解决方案

关键点 useContext:React 提供的 Hook,用于在组件树中共享全局状态,简化跨组件数据传递。应用场景:主题切换、用户认证、语言设置和全局配置管理。实现方式:结合 createContext 和 useContext,实现灵活的状…

20250706-8-Docker快速入门(下)-Dockerfile介绍与基本使用_笔记

一、Dockerfile构建镜像1. Dockerfile概述定义:Dockerfile是一个用于自动构建镜像的文本文件,由一条条指令组成工作原理:指令逐步执行,每个指令完成不同功能典型指令示例:FROM centos:latest:基…

Git系列--3.分支管理

目录 一、理解分支 1.1图示 1.2 打印仓库下有哪些分支 1.3创建分支 1.4HEAD与切换分支 1.5合并分支 1.6流程图理解 二、删除分支 ​ 三、合并分支冲突 3.1.问题导入 3.2.解决 3.3合并图示 四、合并模式 4.1合并​编辑 4.2变基 五、bug分支 5.1背景建立 5.2解决步骤 5.2.1…

Vue.js TDD开发深度指南:工具链配置与精细化测试策略

“TDD不是测试优先的开发,而是设计优先的开发。” —— Robert C. Martin 引言 在Vue.js项目中实施测试驱动开发(TDD)是构建健壮应用的关键路径。但许多开发者在实践中常遇到: 工具链配置复杂导致放弃不同类型组件测试策略混淆测…

基于物联网的智能家居控制系统设计与实现

标题:基于物联网的智能家居控制系统设计与实现内容:1.摘要 随着物联网技术的飞速发展,智能家居逐渐成为人们关注的焦点。本文旨在设计并实现一个基于物联网的智能家居控制系统,以提高家居的智能化水平和用户的生活便利性。通过采用先进的传感器技术、通信…

Vue 中使用 Cesium 实现可拖拽点标记及坐标实时显示功能

在 Cesium 地图开发中,实现点标记的拖拽交互并实时显示坐标信息是一个常见的需求。本文将详细介绍如何在 Vue 框架中使用 Cesium 的 Primitive 方式创建点标记,并实现拖拽功能及坐标提示框跟随效果。先看效果图功能实现概述我们将实现的功能包括&#xf…

HTML 插件:构建网页的强大工具

HTML 插件:构建网页的强大工具 引言 HTML 插件是网页设计中不可或缺的一部分,它们为网页增添了丰富的交互性和动态效果。本文将深入探讨 HTML 插件的概念、类型、应用及其在网页开发中的重要性。 什么是 HTML 插件? HTML 插件,也称为 HTML 组件或 HTML 控件,是指嵌入到…

NeRF、3DGS、2DGS下三维重建相关方法介绍及以及在实景三维领域的最新实践

一、引言 在计算机视觉与图形学领域,三维重建技术正经历从传统几何建模向智能化神经表征的范式转变。近年来,随着深度学习算法的迭代、传感器技术的进步及计算硬件的升级,以神经辐射场(NeRF)和高斯泼溅(2D…

rt thread studio 和 KEIL对于使用rt thread 的中间件和组件,哪个更方便

下面我从中间件/组件集成和开发体验两个角度,详细对比 RT-Thread Studio 和 Keil MDK 的便利性:1. 中间件和组件集成 RT-Thread Studio 集成RT-Thread生态:内置RT-Thread的包管理器(RT-Thread Package Manager)&#x…

Spring Boot 项目开发实战:入门应用部分原理示例讲解

前言Spring Boot 作为当前 Java 开发领域最流行的框架之一,以其 "约定优于配置" 的理念极大简化了企业级应用的开发流程。本文将基于《Spring Boot 项目开发教程(慕课版)》中的资产管理系统项目,深入解析 Spring Boot 的…

ByteBrain x 清华 VLDB25|时序多模态大语言模型 ChatTS

资料来源:火山引擎-开发者社区 近年来,多模态大语言模型(MLLM)发展迅速,并在图像、视频、音频等领域取得了突破性成果。然而,相较于这些研究较为成熟的模态,时间序列这一类型的数据与大模型结合…

WPF学习笔记(25)MVVM框架与项目实例

MVVM框架与项目实例一、MVVM框架1. 概述2. 核心组件与优势一、MVVM项目1.普通项目2. MVVM架构3. MVVM项目实例1. 项目准备2. LoginViewModel与Login2. MainWindowViewModel4. MVVM项目优化1. BaseViewModel2. RealyCommand3. 效果展示总结一、MVVM框架 1. 概述 官方文档&…

MySQL实操

## 基于MySQL#先启动MySQL服务#第一次登录[rootlocalhost ~]# mysql -uroot -P3306#密码登录[rootlocalhost ~]# mysql -uroot -pEnter password: Welcome to the MySQL monitor. Commands end with ; or \g.Your MySQL connection id is 9Server version: 8.0.41 Source dist…

ez_rust_writeup

一道简单的[[rust逆向]] #rust逆向 #位运算 题目信息 文件名:ezrust.exe 题目附件:https://wwfj.lanzoul.com/iczMR30k5j4h 密码:bueq 题目分析 1. 初步分析 这是一道Rust编写的逆向题目。通过IDA分析可以看到,这是一个典型的flag验证程序。 …

【QT】-隐式转换 explicit用法

通俗易懂的解释:隐式转换 vs 显式转换 什么是隐式转换? 隐式转换就是编译器偷偷帮你做的类型转换,你甚至都没意识到它发生了。 例子: cpp 运行 double x = 5; // 隐式:int → double(5 变成 5.0) int y = x * 2.5; // 隐式:double → int(截断小数部分) 构造函数的隐…

Django核心知识点详解:JSON、AJAX、Cookie、Session与用户认证

1. JSON数据格式详解1.1 什么是JSON?JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,具有以下特点:独立于语言,几乎所有编程语言都支持易于人阅读和编写易于机器解析和生成基于文本&#xff…

[特殊字符] Python 实战 | 批量统计中文文档词频并导出 Excel

本文展示如何用 Python 脚本: 批量读取文件夹中的多篇中文文档; 用 jieba 分词并统计词频(过滤停用词与单字符); 将各文档词频输出为对应 Excel 文件; 是文本分析、内容审查、报告编写中的实用技巧。 &…

共享打印机(详细操作+常见问题:需输入用户名密码、无法连接等)

文章目录一、设置打印机共享的准备工作二、Windows系统下打印机共享设置1. 启用主机打印机共享2. 客户端添加共享打印机三、我所遇到的问题及解决方法客户机遇到输入用户名、密码错误代码 0x0000011b一、错误代码 0x0000011b 的含义二、解决方法添加打印机没成功其他问题此次打…

在 Windows 系统上配置 [go-zero](https://go-zero.dev) 开发环境教程

💻 在 Windows 系统上配置 go-zero 开发环境教程 本教程将详细介绍如何在 Windows 系统上配置 go-zero 微服务框架的开发环境,包括依赖安装、路径配置、常见问题等。 🧱 一、前置环境安装 1. 安装 Go 下载地址:https://go.dev/…