当前位置: 首页 > news >正文

Android: 事件分发

1. 事件分发 是什么?

事件分发是指 触摸事件(MotionEvent) 从屏幕产生后,系统如何将其传递给具体的 View 并决定由谁处理的过程。

本质上是 View 系统对触摸事件的一套传递与消费机制。


Android 的事件传递顺序:
Activity → ViewGroup → View

Activity → PhoneWindow → DecorView → ViewGroup → View

  • PhoneWindow 内部持有 DecorView(整个界面的根 View,继承自 FrameLayout)。
  • DecorView 的 superDispatchTouchEvent 会直接调父类 ViewGroup 的 dispatchTouchEvent,正式进入 ViewGroup 分发。

2. 三个核心方法

方法所属类作用返回值含义
dispatchTouchEvent()View / ViewGroup事件分发入口,决定事件流向true:事件被自己或子控件消耗,停止上级传递;false:自己及子控件均不处理,回传给上层
onInterceptTouchEvent()只 ViewGroup 有拦截事件,将事件拉回给自己处理true:拦截,事件交给自己的onTouchEventfalse(默认):不拦截,继续向子控件传递
onTouchEvent()View / ViewGroup实际处理触摸事件true:表示自己消耗了事件;false:未消耗,事件回传

3. 整体流程

因为 事件传递顺序 是以 Activity → ViewGroup → View,所以以这三个阶段来说明事件分发的具体流程

3.1 Activity 分发
publicbooleandispatchTouchEvent(MotionEventev){if(ev.getAction()==MotionEvent.ACTION_DOWN){onUserInteraction();}if(getWindow().superDispatchTouchEvent(ev)){returntrue;}returnonTouchEvent(ev);}

可以看出,这儿Activity.dispatchTouchEvent内部调用了windowdispatchTouchEvent方法,如果所有子View没有消费的话会调用Activity自身的onTouchEvent方法

Activity.dispatchTouchEvent() └→ getWindow().superDispatchTouchEvent(ev) // PhoneWindow └→ mDecor.superDispatchTouchEvent(ev) // DecorView └→ super.dispatchTouchEvent(ev) // 实际调的是 ViewGroup.dispatchTouchEvent()
  • 若 ViewGroup 最终消费事件 → 返回 true,事件终止。
  • 若 ViewGroup 没消费(返回 false)→ 会执行 Activity.onTouchEvent(ev)。
3.2 ViewGroup 分发

简陋流程,伪代码:

booleandispatchTouchEvent(MotionEventev){if(!disallowIntercept&&onInterceptTouchEvent(ev)){// 拦截:自己处理,调用自己的 onTouchEvent()returnsuper.dispatchTouchEvent(ev);// 实际上是 View.dispatchTouchEvent}// 不拦截:遍历所有子 View,找触摸点落在哪个子 View 上for(Viewchild:children){if(child 被触摸){if(child.dispatchTouchEvent(ev)){// 交给子 View 分发returntrue;// 子 View 消费了,自己就不处理了}}}// 子 View 都不消费,自己处理returnsuper.dispatchTouchEvent(ev);// 最终走到自己的 onTouchEvent()}

大致分为三步:

  • 拦截:重写 onInterceptTouchEvent 返回 true,事件就停止下传,自己处理。

  • 不拦截:找到具体子 View,调用子 View 的 dispatchTouchEvent。

  • 消费与冒泡:子 View 消费(返回 true)则结束;都不消费则“冒泡”回自身。

3.3 View 分发与消费
3.3.1 View 分发
booleandispatchTouchEvent(MotionEventevent){if(ENABLED&&mOnTouchListener!=null&&mOnTouchListener.onTouch(this,event)){returntrue;// onTouch 直接消费,不再往下}returnonTouchEvent(event);// 否则交给 onTouchEvent 处理}

判断当前的View有没有注册TouchListener,如果注册了,那就不往下走了,说明事件已经被消费了,如果没注册,那就继续往下走,执行onTouchEvent()来消费这个事件。

  • 若 onTouch 返回 true,事件立即被消费,不会触发 onTouchEvent,更不会触发 onClick。
  • 若 onTouch 返回 false 或没有注册,才会进入下一步:View 的消费(即 onTouchEvent)。
3.3.2 View消费
  • onTouchEvent 内部根据控件是否可点击来决定是否消费事件:
    • 可点击(CLICKABLE 或 LONG_CLICKABLE 为 true):
      • 在 ACTION_UP 时调用 performClick(),进而触发 onClick()。
      • 返回 true,表示事件被当前 View 消费。
    • 不可点击:
      • 返回 false,事件向上层传递(冒泡给父 View)。

完整执行顺序:onTouch → onTouchEvent → onClick

4. 相关结论&问题

结论:

  1. 事件序列的连续性
    • 事件序列:在一次事件分发中,以每个 DOWN 事件为开始, UP / CANCEL 事件为停止,在 DOWN -> MOVE -> MOVE -> UP / CANCEL 整个过程看做是一个事件序列
    • 一旦某个 View 决定不处理 DOWN 事件(onTouchEvent 返回 false),同一序列的后续事件(MOVE, UP)都不会再给它。
    • 某个 View 处理了 DOWN 事件,那么同序列的后续事件(MOVE, UP) 都会交给它
    • 如果 ViewGroup 拦截了非 DOWN 事件,子 View 会收到 CANCEL。
  2. ViewGroup 拦截注意事项
    • onInterceptTouchEvent 只存在于 ViewGroup,View 没有这个方法。
    • 子 View 可通过 requestDisallowInterceptTouchEvent(true) 阻止父 View 拦截。
  3. 返回值的含义
    • dispatchTouchEvent 返回 true:事件被当前层级或子层级消费。
    • dispatchTouchEvent 返回 false:自身及子层级都不处理,事件向上传递。
    • onTouchEvent 返回 true:表示当前 View 消费了事件,终止传递。
  4. onTouch 和 onClick 的关系
    • 注册了 OnTouchListener 且 onTouch() 返回 true → onClick 不会再调用。
    • 只有 onTouch() 返回 false,才会走到 onTouchEvent,最终触发 onClick。

问题:
Q1:ViewGroup 为什么没有 onTouchEvent?

有。ViewGroup 继承自 View,天然有onTouchEvent。当它拦截事件或没有子 View 消费时,就会执行自己的onTouchEvent

Q2:如何解决滑动冲突?

两种方案:

  • 外部拦截法:在父 View 的onInterceptTouchEvent中根据手势逻辑返回true/false
  • 内部拦截法:子 View 调用requestDisallowInterceptTouchEvent(true),父 View 配合不要拦截DOWN事件。

Q3:为什么推荐默认不拦截 DOWN 事件?

因为一旦父 View 拦截了DOWN,子 View 将收不到任何事件序列,后续 MOVE/UP 都不会传给它,容易出现事件丢失。

Q4:如果所有 View 都不消费事件,最终会怎样?

事件会回溯到 Activity,调用Activity.onTouchEvent(),如果它也不消费,系统最终丢弃该事件。

http://www.jsqmd.com/news/937647/

相关文章:

  • 记录一次ardupilot_sitl调试longitude的输入数据流
  • 2026西安黄金回收门店深度测评,大克重金条变现能力TOP10权威盘点名录 - 西安闲转记
  • AI模型漂移导致SPC失控?——实时质量监控系统失效的4类根源及12小时热修复方案
  • Video2X 6.0.0:免费AI视频修复神器,让模糊视频秒变4K高清
  • 项目管理中的铁三角:时间、成本与质量如何达到平衡?
  • 智能图像矢量化:3步将PNG/JPG转换为可无限缩放的SVG矢量图
  • 告别网盘限速:LinkSwift 终极下载助手完全指南
  • 2026年6月国内热门的普拉提学校推荐,普拉提,普拉提机构哪家好 - 品牌推荐师
  • 为什么92%的AI项目卡在实验阶段?——揭秘头部科技公司私有化实验管理平台的5个核心模块
  • WarcraftHelper终极指南:5个简单步骤让魔兽争霸3在现代电脑上流畅运行
  • 叉臂提升机厂家推荐:金拓机械在智能物料提升系统中的应用与优势
  • 终极英雄联盟智能工具包:5大突破性功能让你轻松提升游戏体验
  • RAG 技术全解析:让大模型学会“开卷考试“
  • 解锁B站宝藏:用Python开源工具打造你的个人视频图书馆
  • Obsidian插件翻译终极指南:5分钟让任意插件说中文
  • 【题解】CF2232C2
  • 微信消息批量发送终极指南:5分钟掌握WeChat-mass-msg自动化神器
  • StardewPlanner:基于网格化约束的可视化农场规划系统架构解析
  • 终极解决方案:如何在Windows 10上完美安装PL-2303旧版芯片驱动
  • 如何在Windows上实现完全离线的实时语音识别与会议转录
  • 微信QQ消息防撤回实战指南:保护你的聊天记录不被消失
  • JetBrains Maple Mono:终极免费编程字体解决方案
  • 学Simulink--交错并联 Buck 变换器的均流控制与热应力分析仿真
  • D2RML:暗黑破坏神2重制版终极多开神器,3分钟搞定全账号自动登录
  • 古河道淘金船价格 - 舒雯文化
  • 微信聊天记录终极备份指南:永久保存你的数字记忆
  • Qwen图像编辑革命:4步完成专业级AI修图的终极指南
  • 岗位干货|测试岗位全解析:小白 0-1 落地指南(职责拆解 + 环境搭建 + 实战避坑 + 面试题库)
  • 2026指纹浏览器字体指纹溯源机制:系统私有字体栈引发的隐性集群风控详解
  • leecodecode【反转链表+快慢指针】【2026.5.29打卡-java版本】