Android 事件是从Activity向Window、DecorView、ViewGroup、View这样依次传递的事件传递主要搞清楚ViewGroup和View的传递流程:一、View的事件分发流程public boolean dispatchTouchEvent(MotionEvent event) { if (mOnTouchListener ! null (mViewFlags ENABLED_MASK) ENABLED mOnTouchListener.onTouch(this, event)) { return true; } return onTouchEvent(event); }事件传递到子View会先调用其dispatchTouchEvent,1、如果子view注册了OnTouchListener且在onTouch方法里做了事件处理返回true则事件到此消费结束。2、如果上面的if不成立除了1外比如是不可用状态会走view的onTouchEvent方法如果进行了事件处理则返回true否则返回false总结: view的事件分发主要是在dispatchTouchEvent方法里控制如果返回true代表消耗了该事件false代表未消耗该事件。且必须事件的第一个ACTION_DOWN返回了true后续事件才会继续处理否则后续事件不会进入处理二、ViewGroup的事件分发流程public boolean dispatchTouchEvent(MotionEvent ev) { final int action ev.getAction(); final float xf ev.getX(); final float yf ev.getY(); final float scrolledXFloat xf mScrollX; final float scrolledYFloat yf mScrollY; final Rect frame mTempRect; boolean disallowIntercept (mGroupFlags FLAG_DISALLOW_INTERCEPT) ! 0; if (action MotionEvent.ACTION_DOWN) { if (mMotionTarget ! null) { mMotionTarget null; } if (disallowIntercept || !onInterceptTouchEvent(ev)) { ev.setAction(MotionEvent.ACTION_DOWN); final int scrolledXInt (int) scrolledXFloat; final int scrolledYInt (int) scrolledYFloat; final View[] children mChildren; final int count mChildrenCount; for (int i count - 1; i 0; i--) { final View child children[i]; if ((child.mViewFlags VISIBILITY_MASK) VISIBLE || child.getAnimation() ! null) { child.getHitRect(frame); if (frame.contains(scrolledXInt, scrolledYInt)) { final float xc scrolledXFloat - child.mLeft; final float yc scrolledYFloat - child.mTop; ev.setLocation(xc, yc); child.mPrivateFlags ~CANCEL_NEXT_UP_EVENT; if (child.dispatchTouchEvent(ev)) { mMotionTarget child; return true; } } } } } } boolean isUpOrCancel (action MotionEvent.ACTION_UP) || (action MotionEvent.ACTION_CANCEL); if (isUpOrCancel) { mGroupFlags ~FLAG_DISALLOW_INTERCEPT; } final View target mMotionTarget; if (target null) { ev.setLocation(xf, yf); if ((mPrivateFlags CANCEL_NEXT_UP_EVENT) ! 0) { ev.setAction(MotionEvent.ACTION_CANCEL); mPrivateFlags ~CANCEL_NEXT_UP_EVENT; } return super.dispatchTouchEvent(ev); } if (!disallowIntercept onInterceptTouchEvent(ev)) { final float xc scrolledXFloat - (float) target.mLeft; final float yc scrolledYFloat - (float) target.mTop; mPrivateFlags ~CANCEL_NEXT_UP_EVENT; ev.setAction(MotionEvent.ACTION_CANCEL); ev.setLocation(xc, yc); if (!target.dispatchTouchEvent(ev)) { } mMotionTarget null; return true; } if (isUpOrCancel) { mMotionTarget null; } final float xc scrolledXFloat - (float) target.mLeft; final float yc scrolledYFloat - (float) target.mTop; ev.setLocation(xc, yc); if ((target.mPrivateFlags CANCEL_NEXT_UP_EVENT) ! 0) { ev.setAction(MotionEvent.ACTION_CANCEL); target.mPrivateFlags ~CANCEL_NEXT_UP_EVENT; mMotionTarget null; } return target.dispatchTouchEvent(ev); }传递给ViewGroup的dispatchTouchEvent时会先判断是否拦截相关如果进行了拦截: 不往子view传递代码里面通过if条件进行跳过最后target为null然后调用super.dispatchTouchEvent即ViewGroup父类View的dispatchTouchEvent进行处理一般就调用ViewGroup的onTouchEvent事件了。如果不进行拦截: 根据点击位置查找点击区域位于哪一个子View并调用子View的dispatchTouchEvent方法,如果子view dispatchTouchEvent返回true消费了事件则结束如果没有消费事件则会走到target为null,super.dispatchTouchEvent总结: ViewGroup如果拦截则自己在onTouchEvent处理如果不拦截若点击区域子view处理了事件则结束如果不处理则走ViewGroup的onTouchEvent最后归纳: ViewGroup和View 的dispatchTouchEvent返回true代表事件被消费完毕如果返回false代表事件未被消费则继续往上层ViewGroup的onTouch传递这个过程的返回true和false建议追溯源码调试。再次回顾总结: 事件传递到ViewGroup时会先判断是否已经进行onIntercept事件拦截如果拦截了则走onGroup的拦截没有则走子view的dispatchTouchEvent子view的dispatchTouchEvent也同ViewGroup自身最后走的dispatchTouchEventview的dispatchTouchEvent会先判断是否onTouch没有onTouch则走onTouchEvent如果都没有消费则继续到ViewGroup的dispatchEvent如果viewGroup都没有消费则会在父ViewGroup里返回dispatchTouchEvent然后该ViewGroup再递归一遍
Android 事件分发学习心得
发布时间:2026/5/28 9:36:45
Android 事件是从Activity向Window、DecorView、ViewGroup、View这样依次传递的事件传递主要搞清楚ViewGroup和View的传递流程:一、View的事件分发流程public boolean dispatchTouchEvent(MotionEvent event) { if (mOnTouchListener ! null (mViewFlags ENABLED_MASK) ENABLED mOnTouchListener.onTouch(this, event)) { return true; } return onTouchEvent(event); }事件传递到子View会先调用其dispatchTouchEvent,1、如果子view注册了OnTouchListener且在onTouch方法里做了事件处理返回true则事件到此消费结束。2、如果上面的if不成立除了1外比如是不可用状态会走view的onTouchEvent方法如果进行了事件处理则返回true否则返回false总结: view的事件分发主要是在dispatchTouchEvent方法里控制如果返回true代表消耗了该事件false代表未消耗该事件。且必须事件的第一个ACTION_DOWN返回了true后续事件才会继续处理否则后续事件不会进入处理二、ViewGroup的事件分发流程public boolean dispatchTouchEvent(MotionEvent ev) { final int action ev.getAction(); final float xf ev.getX(); final float yf ev.getY(); final float scrolledXFloat xf mScrollX; final float scrolledYFloat yf mScrollY; final Rect frame mTempRect; boolean disallowIntercept (mGroupFlags FLAG_DISALLOW_INTERCEPT) ! 0; if (action MotionEvent.ACTION_DOWN) { if (mMotionTarget ! null) { mMotionTarget null; } if (disallowIntercept || !onInterceptTouchEvent(ev)) { ev.setAction(MotionEvent.ACTION_DOWN); final int scrolledXInt (int) scrolledXFloat; final int scrolledYInt (int) scrolledYFloat; final View[] children mChildren; final int count mChildrenCount; for (int i count - 1; i 0; i--) { final View child children[i]; if ((child.mViewFlags VISIBILITY_MASK) VISIBLE || child.getAnimation() ! null) { child.getHitRect(frame); if (frame.contains(scrolledXInt, scrolledYInt)) { final float xc scrolledXFloat - child.mLeft; final float yc scrolledYFloat - child.mTop; ev.setLocation(xc, yc); child.mPrivateFlags ~CANCEL_NEXT_UP_EVENT; if (child.dispatchTouchEvent(ev)) { mMotionTarget child; return true; } } } } } } boolean isUpOrCancel (action MotionEvent.ACTION_UP) || (action MotionEvent.ACTION_CANCEL); if (isUpOrCancel) { mGroupFlags ~FLAG_DISALLOW_INTERCEPT; } final View target mMotionTarget; if (target null) { ev.setLocation(xf, yf); if ((mPrivateFlags CANCEL_NEXT_UP_EVENT) ! 0) { ev.setAction(MotionEvent.ACTION_CANCEL); mPrivateFlags ~CANCEL_NEXT_UP_EVENT; } return super.dispatchTouchEvent(ev); } if (!disallowIntercept onInterceptTouchEvent(ev)) { final float xc scrolledXFloat - (float) target.mLeft; final float yc scrolledYFloat - (float) target.mTop; mPrivateFlags ~CANCEL_NEXT_UP_EVENT; ev.setAction(MotionEvent.ACTION_CANCEL); ev.setLocation(xc, yc); if (!target.dispatchTouchEvent(ev)) { } mMotionTarget null; return true; } if (isUpOrCancel) { mMotionTarget null; } final float xc scrolledXFloat - (float) target.mLeft; final float yc scrolledYFloat - (float) target.mTop; ev.setLocation(xc, yc); if ((target.mPrivateFlags CANCEL_NEXT_UP_EVENT) ! 0) { ev.setAction(MotionEvent.ACTION_CANCEL); target.mPrivateFlags ~CANCEL_NEXT_UP_EVENT; mMotionTarget null; } return target.dispatchTouchEvent(ev); }传递给ViewGroup的dispatchTouchEvent时会先判断是否拦截相关如果进行了拦截: 不往子view传递代码里面通过if条件进行跳过最后target为null然后调用super.dispatchTouchEvent即ViewGroup父类View的dispatchTouchEvent进行处理一般就调用ViewGroup的onTouchEvent事件了。如果不进行拦截: 根据点击位置查找点击区域位于哪一个子View并调用子View的dispatchTouchEvent方法,如果子view dispatchTouchEvent返回true消费了事件则结束如果没有消费事件则会走到target为null,super.dispatchTouchEvent总结: ViewGroup如果拦截则自己在onTouchEvent处理如果不拦截若点击区域子view处理了事件则结束如果不处理则走ViewGroup的onTouchEvent最后归纳: ViewGroup和View 的dispatchTouchEvent返回true代表事件被消费完毕如果返回false代表事件未被消费则继续往上层ViewGroup的onTouch传递这个过程的返回true和false建议追溯源码调试。再次回顾总结: 事件传递到ViewGroup时会先判断是否已经进行onIntercept事件拦截如果拦截了则走onGroup的拦截没有则走子view的dispatchTouchEvent子view的dispatchTouchEvent也同ViewGroup自身最后走的dispatchTouchEventview的dispatchTouchEvent会先判断是否onTouch没有onTouch则走onTouchEvent如果都没有消费则继续到ViewGroup的dispatchEvent如果viewGroup都没有消费则会在父ViewGroup里返回dispatchTouchEvent然后该ViewGroup再递归一遍