一句话总结:
事件分发的本质是责任链模式的典型应用——就像医院的分诊系统:
- **患者(触摸事件)**按流程经过多个科室(View层级)
- **分诊台(ViewGroup)**决定是自己治疗还是转给下级
- **最终科室(View)**进行实际治疗(处理事件)
一、核心设计原理
1. 责任链模式(看病流程)
患者挂号 → 分诊台 → 专科医生 → 检查治疗
↓ ↓ ↓
Activity → ViewGroup → View
设计目的:
- 解耦处理逻辑(各科室独立工作)
- 动态决定处理者(分诊台灵活调度)
2. 事件消费机制(治标还是治本)
// return true:表示治好患者(事件不再传递)
override fun onTouchEvent(e: MotionEvent): Boolean {
return true
}
// return false:表示治不了(事件继续传递)
override fun onTouchEvent(e: MotionEvent): Boolean {
return false
}
二、底层运作流程
阶段1:事件采集(患者挂号)
系统底层通过InputManagerService:
- 监听屏幕触摸中断
- 通过
EventHub读取/dev/input事件 - 封装为
MotionEvent对象
阶段2:事件传递(科室间转运)
Activity.dispatchTouchEvent()
↓
PhoneWindow.superDispatchTouchEvent()
↓
DecorView.dispatchTouchEvent()
↓
ViewGroup.dispatchTouchEvent()
↓
View.dispatchTouchEvent()
关键点:
- 每个环节都可中断传递(类似科室拒诊)
- 完整事件序列(DOWN→MOVE→UP)必须由同一View处理
阶段3:事件处理(实际治疗)
public boolean onTouchEvent(MotionEvent event) {
// 这里实现具体业务逻辑
return super.onTouchEvent(event);
}
处理优先级:
触摸监听器 > View自身处理 > 点击监听器
三、为什么这样设计?
1. 历史兼容需求
早期Android实体键为主,触控普及后需要兼容旧设计:
- Back键 → Activity处理
- Menu键 → Window处理
- 触控操作 → View处理
2. UI层级结构特性
View树形结构天然适合逐级传递:
Activity(根)
└─ FrameLayout(Window)
└─ LinearLayout(DecorView)
└─ Button(子View)
3. 性能优化考虑
- 避免全局广播(精准定位处理者)
- 减少无效遍历(通过拦截机制提前终止)
四、核心冲突解决方案
1. 滑动冲突(科室抢病人)
案例:ViewPager嵌套RecyclerView
// 解决思路:根据滑动方向判断责任方
override fun onInterceptTouchEvent(e: MotionEvent): Boolean {
when(计算滑动角度) {
水平滑动 → 自己处理(ViewPager翻页)
垂直滑动 → 不拦截(交给RecyclerView滚动)
}
}
2. 多点触控(多个患者同时就诊)
系统通过pointerId区分不同手指:
event.getPointerId(actionIndex) // 获取当前手指ID
event.findPointerIndex(pointerId) // 查找对应手指数据
五、本质特性总结表
| 特性 | 原理 | 类比 |
|---|---|---|
| 传递顺序 | 树形结构深度优先遍历 | 医院科室层级 |
| 消费机制 | 布尔值控制事件流 | 病历本签章 |
| 拦截能力 | ViewGroup专属特权 | 分诊台调度权 |
| 事件序列 | 必须完整处理同一序列 | 患者完整疗程 |
六、避坑指南
坑1:事件丢失
症状:快速滑动时事件不连贯
解决方案:
override fun onTouchEvent(e: MotionEvent): Boolean {
when(e.actionMasked) {
ACTION_CANCEL → 处理异常中断
ACTION_POINTER_UP → 处理多指抬起
}
return true
}
坑2:内存泄漏
错误示范:
// 在自定义View中直接持有Activity引用
class MyView(context: Context) : View(context) {
private val activity = context as Activity // 危险!
}
正确姿势:
// 使用弱引用
private var weakActivity = WeakReference(context)
坑3:性能瓶颈
优化技巧:
// 减少onDraw调用频率
override fun onTouchEvent(e: MotionEvent): Boolean {
when(e.action) {
ACTION_MOVE → {
updateData()
postInvalidate() // 异步刷新
}
}
return true
}
终极口诀:
事件分发责任链,层级传递像医院
ViewGroup是分诊台,View是医生把病看
拦截消费靠返回,触摸序列要完整
冲突解决方向判,内存泄漏需防范!