一句话总结
事件分发就是“谁该处理用户点击”的甩锅流程。手指一碰屏幕,系统就像老板派活,从高层(Activity)到基层(View)层层甩锅,没人接就自己干!
一、事件分发的起点:从硬件到系统
当用户触摸屏幕时,硬件会产生一个原始的触摸事件。这个事件会通过 Linux 内核传递给 WindowManagerService。WindowManagerService 根据事件的坐标和窗口层级,将事件传递给最顶层的应用窗口,也就是 Activity 的 Window。
二、事件分发的核心流程
事件分发是一个自上而下的分发过程和自下而上的处理过程。
1. 自上而下的分发
事件从 Activity 的 dispatchTouchEvent() 开始,依次传递给 Window、DecorView,再到 ViewGroup 和 View。
Activity.dispatchTouchEvent():这是分发链的入口。它首先将事件传递给Window的superDispatchTouchEvent()。ViewGroup.dispatchTouchEvent():这是事件分发的核心。它会调用onInterceptTouchEvent()来决定是否拦截事件。View.dispatchTouchEvent():View是事件分发链的终点。
2. ViewGroup 的拦截机制
onInterceptTouchEvent() 是 ViewGroup 独有的方法,它决定了 ViewGroup 是否要**“截胡”**事件。
onInterceptTouchEvent()返回true:ViewGroup拦截事件,不再将事件传递给子View,并开始调用自己的onTouchEvent()。onInterceptTouchEvent()返回false:ViewGroup不拦截事件,继续将事件传递给子View。
3. 自下而上的处理
事件分发到最底层的 View 后,如果 View 的 onTouchEvent() 返回 true,表示它消费了事件,事件分发流程结束。如果返回 false,事件会回传给父 ViewGroup,由父 ViewGroup 的 onTouchEvent() 处理,以此类推。
三、事件分发的核心规则
ACTION_DOWN是事件序列的“钥匙” :ACTION_DOWN是一个事件序列(DOWN -> MOVE -> UP)的开始。如果一个View的onTouchEvent()在处理ACTION_DOWN时返回false,那么系统会认为该View对此事件序列不感兴趣,后续的MOVE和UP事件将不会再传递给它。- 同一事件序列:一个事件序列从
ACTION_DOWN开始,到ACTION_UP结束。在一个事件序列中,事件要么被一个View处理,要么被传递给其父ViewGroup。 - 事件传递优先级:
onInterceptTouchEvent()>onTouchEvent()>onClick()。
四、常见问题与解决
-
滑动冲突:当一个
ViewPager中嵌套一个RecyclerView时,如果用户在RecyclerView上进行水平滑动,ViewPager可能会拦截事件。- 解决方法:在父
ViewGroup的onInterceptTouchEvent()中,根据滑动方向和业务逻辑,动态地返回true或false。
- 解决方法:在父
-
点击无效:如果
Button的onClick()没有被触发,可能是因为:- 父
ViewGroup在onInterceptTouchEvent()中拦截了事件。 Button的onTouchEvent()在处理ACTION_DOWN时返回了false。Button的isClickable属性为false。
- 父