为了更好地理解事件分发机制,我在这里总结了四个问题,并给出了本人的见解
1、touch事件由父控件向子控件一层一层的传递,这个过程是怎么发生的?
2、ViewGroup.onInterceptTouchEvent(ev)返回true的时候,ViewGroup是如何拦截touch事件的?
3、view.onTouchEvent(ev)返回false时,该touch事件是如何向其父控件传递的?
4、view.onTouchEvent(ev)返回true时,该view是如何消费该touch事件的?
5、若在action_down时,view没有消费该事件,那么后续的action_up、action_move,该view都不能捕获到,这是为什么?
1、touch事件由父控件向子空间一层一层的传递,这个过程是怎么发生的?
我们知道当一个view接收一个touch事件时,首先会调用dispatchTouchEvent(ev),通过阅读viewGoup.dispatchTouchEvent(ev)源码来解释第一个问题
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);
}
注意29行,child.dispachTouchEvent(ev),通过这行代码父控件将touch事件传给了子控件,同样的子控件若也是一个ViewGroup且不对该事件进行拦截的话,它也会通过同样的方式将该touch事件传递给它的子控件。直到该事件传递给某个ViewGroup且被拦截,或者接受该事件的是一个view,该事件停止向下传递。
2、ViewGroup.onInterceptTouchEvent(ev)返回true的时候,ViewGroup是如何拦截touch事件的?
这个问题依然需要到viewGoup.dispatchTouchEvent(ev)去找答案,当onInterceptTouchEvent(ev)返回true时,13-37都不会执行,由于29行没有执行到,所以该touch事件并没有向下传递。
3、view.onTouchEvent(ev)返回false时,该touch事件是如何向其父控件传递的?
假设A是B的父控件,A将touch事件传递给了B,B执行dispatchTouchEvent(ev)——onTouchEvent(ev),由于onTouchEvent(ev)返回了false,所以B.dispatchTouchEvent(ev)也返回false,这样导致A.dispatchTouchEvent(ev)中的第30-37行不会执行,由于mMotionTarget=null,所以44行的if代码块会执行,代码末尾的super.dispatchTouchEvent(ev),即View的dispatchTouchEvent(ev),源码如下:
public boolean dispatchTouchEvent(MotionEvent event) {
if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&
mOnTouchListener.onTouch(this, event)) {
return true;
}
return onTouchEvent(event);
}
第6行的onToucEvent(ev)的到了执行,这样B就将touch事件传递给了A,如果A的onTouchEvent(ev)返回false,它会以同样的方式将该touch事件传递给上一层的父控件
4、view.onTouchEvent(ev)返回true时,该view是如何消费该touch事件的?
同样的例子,假设A是B的父控件,A将touch事件传递给了B,B执行dispatchTouchEvent(ev)——onTouchEvent(ev),由于onTouchEvent(ev)返回了true,所以B.ispatchTouchEvent(ev)也返回true,这样A.dispatchToucEvent(ev)中的第29行的if代码块会执行,将A的mMotionTarget设为B,并且返回true,同样的A的父控件的dispatchToucEvent(ev)的第29行if代码块将其mMotionTaret赋值为A并返回true,这个过程一直持续到最顶层的容器。这样只有B执行了onTouchEvent(ev),A及更上一层的所有控件都没执行onTouchEvent(ev)。
5、若在action_down时,view没有消费该事件,那么后续的action_up、action_move,该view都不能捕获到,这是为什么?
同样的例子,假设A是B的父控件,A将touch事件传递给了B,由于B没有消费该事件,所以A中的第29行的if代码块不会执行,mMotionTarget=null。当下一个事件action_up或者action_move传递到A时,会从第8行跳到第38行,我们看重点44行的if代码块会执行,并返回A.onTouchEvent(ev)的返回值。返回true控件A消费该事件,返回false控件A将该touch事件传递给A的父控件。B并没有接收该touch事件。