ViewGroup事件分发

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的调用了。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值