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建议追溯源码调试。
424

被折叠的 条评论
为什么被折叠?



