一句话总结:
事件分发的核心方法协作就像接力赛:
- Activity 第一棒 → ViewGroup 中途接棒 → View 最后一棒
- 每个环节都有
dispatchXxx、onInterceptXxx、onXxxEvent方法配合
一、核心方法全家福
1. 发令枪:dispatchTouchEvent()
所有事件处理的入口方法,相当于接力赛的发令员
存在位置:
- Activity
- ViewGroup
- View
作用:
- 决定事件是否向下传递
- 触发后续拦截判断
2. 拦截裁判:onInterceptTouchEvent()
ViewGroup专属特权,像赛道上的路障设置员
代码示例:
override fun onInterceptTouchEvent(e: MotionEvent): Boolean {
// 返回true拦截事件(设路障)
return super.onInterceptTouchEvent(e)
}
典型场景:
- ScrollView滑动时拦截子View的事件
3. 终点线:onTouchEvent()
事件处理的最终站,像接力赛的终点裁判
存在位置:
- Activity
- ViewGroup
- View
返回值意义:
true:事件被消费(终点确认)false:事件继续传递(退回上一棒)
二、完整协作流程图
Activity.dispatchTouchEvent()
↓
Window.superDispatchTouchEvent()
↓
DecorView.dispatchTouchEvent()
↓
ViewGroup.dispatchTouchEvent()
├─ onInterceptTouchEvent() → 是否拦截?
│ ↓
├─ 拦截 → 自己onTouchEvent()
│
└─ 不拦截 → 子View.dispatchTouchEvent()
↓
子View.onTouchEvent()
三、方法协作实战案例
案例1:按钮点击事件
// 流程顺序:
1. Activity.dispatchTouchEvent()
2. DecorView.dispatchTouchEvent()
3. ViewGroup.dispatchTouchEvent()
4. Button.dispatchTouchEvent()
5. Button.onTouchEvent()
6. onClickListener触发
案例2:滑动冲突处理
// ViewPager嵌套RecyclerView时:
override fun onInterceptTouchEvent(e: MotionEvent): Boolean {
when(计算滑动方向()) {
水平 → return true // ViewPager拦截
垂直 → return false // 不拦截
}
}
override fun onTouchEvent(e: MotionEvent): Boolean {
// 处理翻页逻辑
return super.onTouchEvent(e)
}
四、辅助方法协作
1. requestDisallowInterceptTouchEvent()
子View要求父ViewGroup不要拦截事件的"免死金牌"
使用场景:
- RecyclerView横向滑动时禁止父容器拦截
recyclerView.addOnItemTouchListener(object : OnItemTouchListener {
override fun onInterceptTouchEvent(rv: RecyclerView, e: MotionEvent): Boolean {
parent.requestDisallowInterceptTouchEvent(true)
return false
}
})
2. onTouchListener
比onTouchEvent()更高优先级的"VIP通道"
优先级比较:
onTouchListener → onTouchEvent → onClickListener
代码示例:
button.setOnTouchListener { v, event ->
when(event.action) {
ACTION_DOWN -> { /* 按下处理 */ true }
else -> false
}
}
五、方法协作关系表
| 方法 | 所属类 | 调用顺序 | 关键作用 |
|---|---|---|---|
| dispatchTouchEvent() | Activity/ViewGroup/View | 最先 | 事件分发入口 |
| onInterceptTouchEvent() | ViewGroup | 中间 | 拦截决策 |
| onTouchEvent() | 所有View | 最后 | 最终消费 |
| requestDisallowInterceptTouchEvent() | ViewGroup | 任意 | 反拦截控制 |
六、避坑指南
坑1:方法执行顺序混乱
错误现象:
- 父View的onTouchEvent比子View先触发
解决方案:
- 理解事件传递是 先自上而下分发,再 自下而上回传
坑2:忘记调用super方法
错误代码:
override fun dispatchTouchEvent(e: MotionEvent): Boolean {
// 没调用super导致事件链断裂
return true
}
正确做法:
override fun dispatchTouchEvent(e: MotionEvent): Boolean {
// 先处理自定义逻辑
val handled = customHandleEvent(e)
return handled || super.dispatchTouchEvent(e)
}
坑3:内存泄漏
危险代码:
// 在匿名内部类中持有外部引用
view.setOnTouchListener { event ->
activity.doSomething() // 隐式持有Activity
false
}
安全方案:
// 使用弱引用
private val weakActivity = WeakReference(activity)
view.setOnTouchListener { event ->
weakActivity.get()?.doSomething()
false
}
终极口诀:
事件分发三剑客,dispatch先发车
ViewGroup有拦截权,onIntercept做判断
onTouchEvent终处理,消费返回true记
冲突解决靠协作,方法调用要守序!