一句话说透Android里面的事件分发的对象是谁

91 阅读3分钟

一句话总结:
Android事件分发的对象就像击鼓传花的游戏:

  • 是触摸事件(手指动作)
  • 传花人是Activity、ViewGroup、View组成的传递链
  • 决定权dispatchTouchEvent()方法手里

一、事件分发核心对象

1. 事件本体:MotionEvent(传递的花)

event.action  // 动作类型(种花姿势):  
   ACTION_DOWN → 手指按下(开始传花)  
   ACTION_MOVE → 手指滑动(花在传递中)  
   ACTION_UP → 手指抬起(花传完了)  

2. 传递链成员(传花人)

对象角色比喻
Activity游戏发起者班级老师(第一个拿到花)
Window传递中介班长(帮老师分发)
ViewGroup小组长课代表(决定给哪个组员)
View最终处理者普通同学(真正接花的人)

二、完整传递流程(击鼓传花全过程)

阶段1:自上而下传递(老师→班长→课代表→同学)

Activity → Window → DecorView → ViewGroup → ... → View  

关键方法

public boolean dispatchTouchEvent(MotionEvent ev)  

阶段2:拦截判断(课代表的特权)

只有ViewGroup有这个特殊能力:

public boolean onInterceptTouchEvent(MotionEvent ev)  

举个栗子

当手指在ScrollView里滑动时:

  • 纵向滑动 → ScrollView拦截事件(自己处理滚动)
  • 横向滑动 → 不拦截(传给子View处理)

阶段3:最终处理(同学接到花)

public boolean onTouchEvent(MotionEvent event)  

处理优先级

onTouchListener → onTouchEvent → onClickListener  

三、典型场景分析

场景1:按钮点击

Activity → DecorView → FrameLayout → Button  
                    ↓             ↓  
                不拦截           onTouch返回true(成功接花)  

场景2:列表滑动

Activity → DecorView → RecyclerView → ItemView  
                    ↓        ↓  
                不拦截    onIntercept返回true(拦截给子View的事件)  

场景3:事件无人处理

Activity → DecorView → ViewGroup → View  
                                     ↓  
                                  onTouch返回false(花掉地上了)  
                                     ↓  
事件反向传递:View → ViewGroup → Activity(最后没人接就销毁花)  

四、常见问题解答

Q1:为什么我的点击事件没反应?

可能原因

  • 父ViewGroup拦截了事件(课代表扣下了花)
  • 子View的onTouchEvent返回了false(同学拒绝接花)
  • 视图被遮挡(花传不过去)

Q2:onTouch和onClick有什么区别?

对比表

特性onTouchonClick
触发时机任何触摸动作完整点击(DOWN→UP)
返回值需要return true才能接收后续事件无返回值
信息量能获取详细坐标数据只有点击事件

Q3:如何自定义事件处理?

技巧三连

  1. 重写onInterceptTouchEvent控制是否拦截
  2. onTouchEvent处理复杂手势
  3. 使用GestureDetector识别双击/长按等操作

五、避坑指南

坑1:事件冲突(多个View抢花)

案例:ViewPager嵌套ScrollView

// 解决思路:根据滑动方向决定谁处理  
override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {  
    when(判断滑动方向) {  
        水平 → 自己处理(ViewPager翻页)  
        垂直 → 不拦截(给ScrollView滚动)  
    }  
}  

坑2:内存泄漏

错误示范

// 在Activity中直接创建Handler处理事件  
val handler = Handler() // 隐式持有Activity引用  

正确姿势

// 使用弱引用或View自带的Handler  
private class SafeHandler(activity: WeakReference<Activity>) : Handler()  

坑3:事件延迟

症状:滑动列表时有卡顿感
优化方案

// 在自定义View中减少onDraw的复杂度  
override fun onTouchEvent(event: MotionEvent): Boolean {  
    when(event.action)  {  
        ACTION_MOVE → 只更新必要数据,调用postInvalidate()  
    }  
}  

终极口诀:
事件分发如传花,Activity是发起家
ViewGroup可拦截,View是最终处理侠
onTouch优先执行,onClick最后到达
冲突解决靠判断,方向距离两手抓!