参考文章:图解 Android 事件分发机制
这个文章很早我就看过了,但是只是知道是这个流程,具体怎么操作,点击事件如何传递依然有点迷,甚至连这个流程图是否正确都不敢持肯定态度,所以今天就来用代码实现下,毕竟眼见为实嘛。
debug过程太繁琐这里就不一步步贴了,直接上带注释的结果
1、点击viewgroup时,事件分发流程:
activity.dispatchTouchEvent { super } ==》viewgroup.dispatchTouchEvent { super } ==》viewgroup.onTouchEvent { super } ==》viewgroup.setOnClickListener2、点击view时,事件分发流程:
activity.dispatchTouchEvent { super } ==》viewgroup.dispatchTouchEvent { super } ==》viewgroup.onInterceptTouchEvent { super } ==》 view.dispatchTouchEvent { super } ==》view.onTouchEvent { super } ==》view.setOnClickListener3、activity的dispatchTouchEvent返回true或者false时,事件分发流程:
activity.dispatchTouchEvent { true/false } ==》无4、viewgroup的dispatchTouchEvent返回true时,事件分发流程:
activity.dispatchTouchEvent { super } ==》viewgroup.dispatchTouchEvent { true } ==》无5、viewgroup的dispatchTouchEvent返回false时,事件分发流程:
activity.dispatchTouchEvent { super } ==》activity.onTouchEvent { super } ==》无6、viewgroup的onInterceptTouchEvent返回true时,事件分发流程:
activity.dispatchTouchEvent { super } ==》viewgroup.dispatchTouchEvent { super } ==》viewgroup.onInterceptTouchEvent { true } ==》 viewgroup.onTouchEvent { super } ==》viewgroup.setOnClickListener7、viewgroup的onInterceptTouchEvent返回false时,事件分发流程:(和super一样)
activity.dispatchTouchEvent { super } ==》viewgroup.dispatchTouchEvent { super } ==》viewgroup.onInterceptTouchEvent { false } ==》 view.dispatchTouchEvent { super } ==》view.onTouchEvent { super } ==》view.setOnClickListener8、view的dispatchTouchEvent返回true时,事件分发流程:
activity.dispatchTouchEvent { super } ==》viewgroup.dispatchTouchEvent { super } ==》viewgroup.onInterceptTouchEvent { super } ==》 view.dispatchTouchEvent { true } ==》无9、view的dispatchTouchEvent返回false时,事件分发流程:
activity.dispatchTouchEvent { super } ==》viewgroup.dispatchTouchEvent { super } ==》viewgroup.onTouchEvent { super } ==》viewgroup.setOnClickListener10、view的onTouchEvent返回true时,事件分发流程:
activity.dispatchTouchEvent { super } ==》viewgroup.dispatchTouchEvent { super } ==》viewgroup.onInterceptTouchEvent { super } ==》 view.dispatchTouchEvent { super } ==》view.onTouchEvent { true } ==》无11、view的onTouchEvent返回false时,事件分发流程:
activity.dispatchTouchEvent { super } ==》viewgroup.dispatchTouchEvent { super } ==》viewgroup.onTouchEvent { super } ==》viewgroup.setOnClickListener12、viewgroup的onTouchEvent返回true时,事件分发流程:
activity.dispatchTouchEvent { super } ==》viewgroup.dispatchTouchEvent { super } ==》viewgroup.onTouchEvent { true } ==》无13、viewgroup的onTouchEvent返回false时,事件分发流程:
activity.dispatchTouchEvent { super } ==》activity.onTouchEvent { super } ==》无14、activity的onTouchEvent返回true或者false时,事件分发流程:
activity.onTouchEvent { true/false } ==》无
super.dispatchTouchEvent也是返回true、false,为什么和直接设置 dispatchTouchEvent{ true、false }效果完全不一样呢?这个消费是什么意思?
dispatchTouchEvent 返回 true 只代表"这次 MotionEvent 被消费了",但它不等于点击事件(onClick)一定发生。这两件事在源码里是分开的。
true:事件被消费,不再向父级回传,本次调用直接结束
false:事件没被消费,上交父 ViewGroup 的 onTouchEvent
super.dispatchTouchEvent():走默认分发逻辑(拦截判断 → 遍历子 View → 自身 onTouchEvent)
super.dispatchTouchEvent()返回 true 是因为内部的 onTouchEvent 返回了 true(或者子 View 消费了)。整个事件序列走完了完整的 U 型链路
viewgroup的onInterceptTouchEvent是不是有点多余
- 当 ViewGroup 通过
onInterceptTouchEvent返回 true 拦截事件时,原先正在处理事件的子 View 会收到一个ACTION_CANCEL,告诉它"事件序列被抢走了,请你重置状态"。- 如果你直接在
dispatchTouchEvent里 return true,系统不会自动给子 View 补发 CANCEL。子 View 会永远卡在"按下"状态——按钮高亮清不掉、RecyclerView 的滚动状态无法复位。dispatchTouchEvent方法有有一整套分发遍历流程,如果直接设置就需要手动配置这些逻辑,所以onInterceptTouchEvent才会被抽出单独来管是否拦截
具体到代码上如何设置拦截
- 外部拦截法(父主导):父在
onInterceptTouchEvent的 MOVE 中根据滑动方向决定拦截- 内部拦截法(子主导):子在
dispatchTouchEvent里根据业务逻辑调用requestDisallowInterceptTouchEvent,调用后,父 ViewGroup 的onInterceptTouchEvent在后续 MOVE/UP 中会被跳过
/** * 事件分发测试 * activity层 */ public class MainActivity extends AppCompatActivity { private LinearlayoutViewGroup mViewGroup; private TextViewView mView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mViewGroup = findViewById(R.id.mViewGroup); mView = findViewById(R.id.mView); mViewGroup.setOnClickListener(v -> { Log.e("点击", "ViewGroup点击事件触发"); }); mView.setOnClickListener(v -> { Log.e("点击", "View点击事件触发"); }); } /** * event 普通点击会触发三次,ACTION_DOWN、ACTION_MOVE、ACTION_UP * 这里只讨论 ACTION_UP 的传递流程 * return super 默认方法 点击viewgroup和view时,传递到viewgroup 的 dispatchTouchEvent 方法 * return true/false 事件到这里结束 */ @Override public boolean dispatchTouchEvent(MotionEvent event) { if (event.getAction() == 1) Log.e("点击", "activity-dispatchTouchEvent"); return super.dispatchTouchEvent(event); } /** * return super/true/false activity的点击事件触发,事件到这里结束 */ @Override public boolean onTouchEvent(MotionEvent event) { if (event.getAction() == 1) Log.e("点击", "activity-onTouchEvent"); return super.onTouchEvent(event); } }/** * viewGroup层 */ public class LinearlayoutViewGroup extends LinearLayout { public LinearlayoutViewGroup(Context context) { super(context); } public LinearlayoutViewGroup(Context context, @Nullable AttributeSet attrs) { super(context, attrs); } public LinearlayoutViewGroup(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } public LinearlayoutViewGroup(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); } /** * return super 默认方法,点击viewgroup时,传递给自己的 onTouchEvent 方法。点击view时,传递给自己的 dispatchTouchEvent 方法 * return true 事件到这里结束 * return false 事件传递给acivity的 onTouchEvent 方法 */ @Override public boolean dispatchTouchEvent(MotionEvent event) { if (event.getAction() == 1) Log.e("点击", "ViewGroup-dispatchTouchEvent"); return super.dispatchTouchEvent(event); } /** * 判断是否拦截了某个事件 * return super/false 默认方法,点击view时,传递到view的 dispatchTouchEvent 方法 * return true 事件传递给自己的 onTouchEvent 方法 */ @Override public boolean onInterceptTouchEvent(MotionEvent event) { if (event.getAction() == 1) Log.e("点击", "ViewGroup-onInterceptTouchEvent"); return super.onInterceptTouchEvent(event); } /** * return super 默认方法,点击viewgroup时,触发viewgroup的 setOnClickListener 方法 * return true 事件到这里结束 * return false 事件传递到activity的 onTouchEvent 方法 */ @Override public boolean onTouchEvent(MotionEvent event) { if (event.getAction() == 1) Log.e("点击", "ViewGroup-onTouchEvent"); return super.onTouchEvent(event); } }/** * view层 */ //@SuppressLint("AppCompatCustomView") public class TextViewView extends View { public TextViewView(Context context) { super(context); } public TextViewView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); } public TextViewView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } public TextViewView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); } /** * return super 默认方法,点击view时,传递给自己的 onTouchEvent 方法。 * return true 事件到这里结束 * return false 事件传递给viewgroup的 onTouchEvent 方法 */ @Override public boolean dispatchTouchEvent(MotionEvent event) { if (event.getAction() == 1) Log.e("点击", "View-dispatchTouchEvent"); return super.dispatchTouchEvent(event); } /** * return super 默认方法,点击view时,触发view的 setOnClickListener 方法 * return true 事件到这里结束 * return false 事件传递给viewgroup的 onTouchEvent 方法 */ @Override public boolean onTouchEvent(MotionEvent event) { if (event.getAction() == 1) Log.e("点击", "View-onTouchEvent"); return super.onTouchEvent(event); } }