触摸事件分发机制
Android 触摸事件(如点击、滑动)的分发遵循一个明确的链条,通常始于 Activity,经过 Window、DecorView 和各级 ViewGroup,最终到达具体的 View
。整个过程主要由三个核心方法控制:
dispatchTouchEvent(MotionEvent ev): 负责事件分发。每个 View 或 ViewGroup 都会首先调用此方法,决定是否继续向下传递事件。onInterceptTouchEvent(MotionEvent ev) : 仅 ViewGroup 拥有。用于判断是否拦截事件,阻止其继续向子 View 传递。默认返回false
onTouchEvent(MotionEvent ev): 如果事件传递到该View并且没有被消费,就会调用此方法。此方法负责事件处理。返回true表示消费事件,返回false表示不消费。
一个完整的事件序列通常以 ACTION_DOWN 开始,包含零个或多个 ACTION_MOVE,最后以 ACTION_UP 或 ACTION_CANCEL 结束
执行顺序:OnTouchListener.onTouch>View.onTouchEvent>OnClickListener.onClick
onTouch 的返回值会直接决定 onTouchEvent 以及 onClick 是否会被执行:
如果onTouch返回true,表示事件已在此被消费**。事件分发就此终止,onTouchEvent不会被调用,onClick也不会被触发
如果
onTouch返回false,表示事件未被消费。事件会继续传递,交给onTouchEvent方法处理。在onTouchEvent的ACTION_UP处理中,如果满足点击条件(如未长按、未移动超过阈值),则会调用performClick(),从而触发onClick.
注意:
确保 onClick 触发:如果你同时设置了 OnTouchListener 和 OnClickListener,并且希望在 onTouch 中进行一些自定义触摸处理后仍能触发 onClick,务必让 onTouch 方法返回 false。如果在 onTouch 中返回 true,onClick 将不会执行
ACTION_DOWN 的重要性:一个 View 只有消费了 ACTION_DOWN 事件(即 onTouchEvent 或 onTouch 对此事件返回了 true),后续的 ACTION_MOVE 和 ACTION_UP 事件才会继续传递给它
点击判定条件:onClick 的触发并非只需 ACTION_UP。系统还需判定手指从按下到抬起的过程中,移动距离是否超过系统设定的最小滑动阈值(ViewConfiguration.getScaledTouchSlop()),以及按下时间是否未超过长按时间阈值。如果移动距离过大或按下时间过长,都不会触发 onClick
请求父容器不拦截事件:在处理滑动冲突时,子 View 可以通过在 ACTION_DOWN 或 ACTION_MOVE 时调用 getParent().requestDisallowInterceptTouchEvent(true) 来请求父 ViewGroup 不要拦截事件序列。但请注意,父容器对 ACTION_DOWN 事件的拦截无法通过此方法阻止
ViewGroup事件分发:


如何解决滑动冲突
滑动冲突是指当两个或多个视图(View)具有相同或不同方向的滑动能力时,可能出现的滑动行为不符合预期的情况。
解决滑动冲突的基本思路是:明确哪个视图应该处理哪些滑动事件,然后通过拦截或不拦截事件来实现。
解决滑动冲突的方法有两种:外部拦截法和内部拦截法。
外部拦截法:指在父视图(ViewGroup)的onInterceptTouchEvent()方法中根据滑动方向和条件来判断是否拦截事件,然后交给自己或子视图的onTouchEvent()方法处理。
内部拦截法:指在子视图(View)的onTouchEvent()方法中根据滑动方向和条件来判断是否处理事件,如果不处理则调用父视图(ViewGroup)的requestDisallowInterceptTouchEvent()方法来请求父视图不要拦截事件。
Last updated