ViewGroup的dispatchTouchEvent方法源码解读

任何一次touch事件都包含以下几个操作:

(1) ACTION_DOWN:一个touch事件首先执行的按下操作,其它的操作都是以按下操作为基础;
(2) ACTION_MOVE:一个touch事件可以有多个或者0个移动操作;
(3) ACTION_UP:一个touch事件以抬起操作为结束。

假设有一个父布局(ViewGroup),该父布局有多个子View。下面将通过这个假设来分析,每次touch事件发生时,首先调用的是ViewGroup的dispatchTouchEvent方法,这个方法内进行事件分发。

dispatchTouchEvent方法的源码:

       @Override  
       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;  

           //这个值默认是false, 然后我们可以通过requestDisallowInterceptTouchEvent(boolean disallowIntercept)方法  
           //来改变disallowIntercept的值  
           boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;  

           //这里是ACTION_DOWN的处理逻辑  
           if (action == MotionEvent.ACTION_DOWN) {  
            //清除mMotionTarget, 每次ACTION_DOWN都很设置mMotionTarget为null  
               if (mMotionTarget != null) {  
                   mMotionTarget = null;  
               }  

               //disallowIntercept默认是false, 就看ViewGroup的onInterceptTouchEvent()方法  
               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;  
                   //遍历其子View  
                   for (int i = count - 1; i >= 0; i--) {  
                       final View child = children[i];  

                       //如果该子View是VISIBLE或者该子View正在执行动画, 表示该View才  
                       //可以接受到Touch事件  
                       if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE  
                               || child.getAnimation() != null) {  
                        //获取子View的位置范围  
                           child.getHitRect(frame);  

                           //如Touch到屏幕上的点在该子View上面  
                           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;  

                               //调用该子View的dispatchTouchEvent()方法  
                               if (child.dispatchTouchEvent(ev))  {  
                                   // 如果child.dispatchTouchEvent(ev)返回true表示  
                                //该事件被消费了,设置mMotionTarget为该子View  
                                   mMotionTarget = child;  
                                   //直接返回true  
                                   return true;  
                               }  
                               // The event didn't get handled, try the next view.  
                               // Don't reset the event's location, it's not  
                               // necessary here.  
                           }  
                       }  
                   }  
               }  
           }  

           //判断是否为ACTION_UP或者ACTION_CANCEL  
           boolean isUpOrCancel = (action == MotionEvent.ACTION_UP) ||  
                   (action == MotionEvent.ACTION_CANCEL);  

           if (isUpOrCancel) {  
               //如果是ACTION_UP或者ACTION_CANCEL, 将disallowIntercept设置为默认的false  
            //假如我们调用了requestDisallowInterceptTouchEvent()方法来设置disallowIntercept为true  
            //当我们抬起手指或者取消Touch事件的时候要将disallowIntercept重置为false  
            //所以说上面的disallowIntercept默认在我们每次ACTION_DOWN的时候都是false  
               mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;  
           }  

           // The event wasn't an ACTION_DOWN, dispatch it to our target if  
           // we have one.  
           final View target = mMotionTarget;  
           //mMotionTarget为null意味着没有找到消费Touch事件的View, 所以我们需要调用ViewGroup父类的  
           //dispatchTouchEvent()方法,也就是View的dispatchTouchEvent()方法  
           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);  
           }  

           //这个if里面的代码ACTION_DOWN不会执行,只有ACTION_MOVE  
           //ACTION_UP才会走到这里, 假如在ACTION_MOVE或者ACTION_UP拦截的  
           //Touch事件, 将ACTION_CANCEL派发给target,然后直接返回true  
           //表示消费了此Touch事件  
           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)) {  
               }  
               // clear the target  
               mMotionTarget = null;  
               // Don't dispatch this event to our own view, because we already  
               // saw it when intercepting; we just want to give the following  
               // event to the normal onTouchEvent().  
               return true;  
           }  

           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);  

           if ((target.mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {  
               ev.setAction(MotionEvent.ACTION_CANCEL);  
               target.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;  
               mMotionTarget = null;  
           }  

           //如果没有拦截ACTION_MOVE, ACTION_DOWN的话,直接将Touch事件派发给target  
           return target.dispatchTouchEvent(ev);  
       }  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值