事件分发主要涉及以下三个核心方法:
public boolean dispatchTouchEvent(MotionEvent ev): 事件分发
Activity, ViewGroup, View。View 的 onInterceptTouchEvent(仅 ViewGroup 有)或将事件直接传递给子 View 的 dispatchTouchEvent。public boolean onInterceptTouchEvent(MotionEvent ev): 事件拦截
dispatchTouchEvent 内部被调用,用于判断是否要拦截当前事件,阻止其继续向子 View 传递。ViewGroup。true: 表示拦截该事件。事件将不再向下分发,而是交由当前 ViewGroup 自己的 onTouchEvent 方法进行处理。false: 表示不拦截该事件,继续将其分发给子 View。这是默认行为。public boolean onTouchEvent(MotionEvent ev): 事件处理
ViewGroup, View。true: 表示成功处理并消费了该事件。后续的事件序列(如 ACTION_MOVE, ACTION_UP)将继续传递给这个 View 的 onTouchEvent 处理,事件分发流程结束。false: 表示不处理该事件。事件会“冒泡”回传给父 View 的 onTouchEvent 方法进行处理。触摸事件的传递遵循一个自顶向下的分发顺序和自底向上的处理顺序。
分发(Dispatch)顺序(自顶向下):
Activity.dispatchTouchEvent -> Window.superDispatchTouchEvent -> DecorView.dispatchTouchEvent -> ViewGroup.dispatchTouchEvent -> ... -> View.dispatchTouchEvent
处理(Handle)顺序(自底向上):
如果一个 View 的 onTouchEvent 返回 false,事件会回传给其父 ViewGroup 的 onTouchEvent 进行处理,以此类推,直到事件被消费或最终传回给 Activity 的 onTouchEvent。
方法的布尔返回值在事件分发中起着决定性作用:
| 返回值 | 含义 |
|---|---|
true |
事件已消费 (Consumed)。事件传递终止,后续事件序列将持续分发到该消费者。 |
false |
事件未消费 (Not Consumed)。事件将向上传递(冒泡)给父容器的 onTouchEvent 方法处理。 |
super.xxx(...) |
调用父类的默认实现。这是最常见的写法,其行为取决于具体方法和场景: - 在 ViewGroup 的 dispatchTouchEvent 中,super 实现包含了遍历子 View 并分发事件的复杂逻辑。- 在 View 的 onTouchEvent 中,super 实现的默认行为通常等同于返回 false(除非该 View 是可点击的,如设置了 OnClickListener)。 |
子 View 可以通过调用其父容器的 requestDisallowInterceptTouchEvent(true) 方法,来“请求”父容器在当前事件序列中(从 ACTION_DOWN 到 ACTION_UP/ACTION_CANCEL)不要通过 onInterceptTouchEvent 拦截事件。
这是一种子 View “反抗”父 View 拦截的机制,常用于解决滑动冲突,例如在水平滚动的 ViewPager 中嵌套一个垂直滚动的 RecyclerView。
// 在子 View 内部调用,以阻止父 View 拦截后续的 MOVE 和 UP 事件
getParent().requestDisallowInterceptTouchEvent(true);
在一个可交互的 View 中,触摸事件的处理存在一个明确的优先级链:
OnTouchListener.onTouch(): 如果通过 View.setOnTouchListener() 设置了监听器,其 onTouch() 方法将最先被调用。如果 onTouch() 返回 true,则 View 自身的 onTouchEvent() 将不会被执行。
View.onTouchEvent(): 如果 OnTouchListener 不存在或其 onTouch() 方法返回 false,则会执行 View 自己的 onTouchEvent() 方法。
OnClickListener.onClick(): 该回调在 View 的 onTouchEvent() 内部触发。具体来说,当一个 ACTION_UP 事件发生,并且该 View 在之前的 ACTION_DOWN 事件中消费了事件(即 onTouchEvent 返回 true),同时 View 是可点击的 (isClickable() == true),onClick() 方法才会被调用。
优先级总结: OnTouchListener > onTouchEvent > OnClickListener。