ViewGroup事件分发

来源:互联网 发布:深圳编程培训机构 编辑:程序博客网 时间:2024/05/18 19:42

ViewGroup事件分发

ViewGroup点击事件流程

ViewGroup:dispatchTouchEvent()->ViewGroup:onInterceptTouchEvent() - > View:dispatchTouchEvent() - > View:onTouchEvent() - >ViewGroup:onTouchEvent()

ViewGroup:dispatchTouchEvent()源码

  • 因为代码较多,分段思考。首先看ACTION_DOWN的代码
    //是否超屏幕 if (!onFilterTouchEventForSecurity(ev)) {             return false;         }         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) {              //---------------关键点1           if (mMotionTarget != null) {                 // this is weird, we got a pen down, but we thought it was                 // already down!                 // XXX: We should probably send an ACTION_UP to the current                 // target.                 mMotionTarget = null;             }            //------------关键点2           if (disallowIntercept || !onInterceptTouchEvent(ev)) {                 // reset this event's action (just to protect ourselves)                 ev.setAction(MotionEvent.ACTION_DOWN);                 // We know we want to dispatch the event down, find a child                 // who can handle it, start with the front-most child.                 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)) {                             // offset the event to the view's coordinate system                             final float xc = scrolledXFloat - child.mLeft;                             final float yc = scrolledYFloat - child.mTop;                             ev.setLocation(xc, yc);                             child.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;                             //关键点3                            if (child.dispatchTouchEvent(ev))  {                                 // Event handled, we have a target now.                                 mMotionTarget = child;                                 return true;                             }                             // The event didn't get handled, try the next view.                             // Don't reset the event's location, it's not                             // necessary here.                         }                     }                 }             }         }                                                    

在关键点1中我们能看到有一个字段mMotionTarget,先判断其是否为null, 如果不为null,则将其置为空。该字段主要目的是保存消费触摸事件的View的。

在关键点2中,对disallowIntercept!onInterceptTouchEvent(ev)进行了判段,如果两者有一个为true就可以向下分发。disallowIntercept的默认为false,可以通过在子View的dispatchTouchEvent()中调用getParent().requestDisallowInterceptTouchEvent(true)方法修改其值。onInterceptTouchEvent()方法在当前类中进行实现,返回false表示不拦截,返回true表示拦截。

在关键点3中,首先在之前判断了该View是否包含触摸点,其次if中判断childView的dispatchTouchEvent()是否返回true,如果返回true,表示childView捕获了本次触摸,则将childView 赋值给mMotionTarget,同时结束ViewGroup的disaptchTouchEvent()方法,并返回true。

  • ACTION_MOVE的代码
 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;        //...ACTION_DOWN        //...ACTIN_UP or ACTION_CANCEL         // The event wasn't an ACTION_DOWN, dispatch it to our target if         // we have one.          //-------关键点1        final View target = mMotionTarget;         // if have a target, see if we're allowed to and want to intercept its         // events         if (!disallowIntercept && onInterceptTouchEvent(ev)) {             //....         }         // finally offset the event to the target's coordinate system and         // dispatch the event.         final float xc = scrolledXFloat - (float) target.mLeft;         final float yc = scrolledYFloat - (float) target.mTop;         ev.setLocation(xc, yc);         return target.dispatchTouchEvent(ev);     }  

在关键点1中,直接将在ACTION_DOWN中保存的mMotionTarget赋值给target,并return target.dispatchTouchEvent(ev);。由此可见,之所以在childView中当ACTION_DOWN没有捕获,就无法捕获后续的ACTION_MOVE和ACTION_UP触摸,是因为此时在ViewGroup中直接将事件交给了在ACTION_DOWN中保存的处理触摸的View对象。

  • ACTION_UP的代码
public boolean dispatchTouchEvent(MotionEvent ev) {         if (!onFilterTouchEventForSecurity(ev)) {             return false;         }         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) {...}      boolean isUpOrCancel = (action == MotionEvent.ACTION_UP) ||                     (action == MotionEvent.ACTION_CANCEL);      if (isUpOrCancel) {                 mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;             }      final View target = mMotionTarget;      if(target ==null ){...}      if (!disallowIntercept && onInterceptTouchEvent(ev)) {...}         if (isUpOrCancel) {             mMotionTarget = null;         }         // finally offset the event to the target's coordinate system and         // dispatch the event.         final float xc = scrolledXFloat - (float) target.mLeft;         final float yc = scrolledYFloat - (float) target.mTop;         ev.setLocation(xc, yc);         return target.dispatchTouchEvent(ev);     }  

在ACTION_UP实现没有太大的难点,清楚一下字段属性,调用之前保存的childView对象的分发方法。

  • 当没有childView来处理触摸时
final View target = mMotionTarget;         if (target == null) {             // We don't have a target, this means we're handling the             // event as a regular view.             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);         }   

当没有触摸时,会调用父类的dispatchTouchEvent(ev),因为ViewGroup继承自View,那么一切就很简单了。就会调用ViewGroup本身的onTouchEvent()

总结

  1. 在ACTION_DOWN中,如果ViewGroup中有能处理该次触摸的childView,则将会调用childView的dispatchTouchEvent,同时保存childView对象。当本次触摸的ACTION_MOVE和ACTION_UP事件,也将有此次childView来处理。
  2. 当ViewGroup中没有能够处理此次触摸的childView,则会调用其父类View的dispatchTouchEvent()方法,那么就类似childView的调用了。
0 0
原创粉丝点击