一句话说透Android里面事件分发机制

80 阅读2分钟

一句话总结:
事件分发的核心方法协作就像接力赛

  • Activity 第一棒 → ViewGroup 中途接棒 → View 最后一棒
  • 每个环节都有dispatchXxxonInterceptXxxonXxxEvent方法配合

一、核心方法全家福

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记
冲突解决靠协作,方法调用要守序!