Android-onInterceptTouchEvent()和onTouchEvent()总结
Android事件分发机制完全解析,带你从源码的角度彻底理解(上)
Android事件分发机制完全解析,带你从源码的角度彻底理解(下)
在读完上面三篇文章以后,相信大家对事件分发机制有了一个比较清晰的认识,为了更好地理解事件分发机制,我在这里总结了四个问题,并给出了本人的见解
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事件。