Android点击事件简单梳理

View与ViewGroup TouchEvent分析
ViewGroup继承于View,而View实现了两个方法 :
dispatchTouchEvent 主要用于分发事件, 在不考虑父view group拦截的情况下,所有touch事件首先产生down事件,父view从上层获取MotionEvent再调用子类的dispatchTouchEvent将事件传递给子View。
在子view的dispatchTouchEvent方法中,优先判定是否存在touchListerner , 如果有则调用touchlisterner的touch方法。当返回true时,dispatchTouchEvent将返回true,并且不再执行自己的onTouchEvent方法。否则dispatchTouchEvent将返回子viewonTouchEvent的返回值 。

当子view在dispatchTouchEvent的down事件里返回true之后,父view会将当前子view设置成targetView。只有target view才会收到后续的move ,up ,或者cancel事件。

说完子view,再看父类的viewGroup, 父view也是由上层调用自己的dispatchTouchEvent来分发MotionEvent,事件的传递逻辑是先调用dispatchTouchEvent方法,然后调用onInterceptTouchEvent判断是否拦截,这里默认是不拦截,拦截的情况后续再说。再之后

相比view ,view group多了一个onInterceptTouchEvent方法。
该方法主要用于决定是否拦截子view获取MotionEvent。默认实现为false,也就是不拦截。当返回true之后,则直接将当子view的后续点击事件传递给父view.

整个流程里最复杂的部分为viewgroup的dispatchTouchEvent方法。

当点击事件为ACTION_DOWN的时候,首先清除当前view group 处理点击事件的TouchTarget链表,恢复view group到默认状态(在detach的时候也会做)。
接着判断是否需要拦截,如果拦截则直接调用dispatchTransformedTouchEvent方法并传入空view(当孩子为空时默认调用自己的super.dispatchTouchEvent方法)。
当不拦截点击事件为ACTION_POINTER_DOWN ||ACTION_POINTER_DOWN的时候,根据从子view找出符合点击区域的孩子依次调用子view的dispatchTouchEvent方法来判断孩子是否处理,不论是否找到都会调用dispatchTransformedTouchEvent方法。当有子view处理的时候会生成一个TouchTarget并加入到链表中。

对于其他的事件,当TouchTarget链表为空时直接调用dispatchTransformedTouchEvent方法并传入空view丢给自己处理,否则遍历TouchTarget链表找到合适的子view处理。
在cancel或者up事件的时候会清理链表。

  @Override  
    public boolean dispatchTouchEvent(MotionEvent ev) {  
        if (mInputEventConsistencyVerifier != null) {  
            mInputEventConsistencyVerifier.onTouchEvent(ev, 1);  
        }  
  
        boolean handled = false;  
        if (onFilterTouchEventForSecurity(ev)) {  
            final int action = ev.getAction();  
            final int actionMasked = action & MotionEvent.ACTION_MASK;  
  
            /** 
             * 第一步:对于ACTION_DOWN进行处理(因为ACTION_DOWN是一系列事件的开端) 
             *  
             * 1. 清除以往的Touch状态(state)开始新的手势(gesture) 
             * cancelAndClearTouchTargets(ev)中有一个非常重要的操作: 
             * 将mFirstTouchTarget设置为null!!!! 
             * 2. 随后在resetTouchState()中重置Touch状态标识 
             */  
            if (actionMasked == MotionEvent.ACTION_DOWN) {  
                // Throw away all previous state when starting a new touch gesture.  
                cancelAndClearTouchTargets(ev);//取消和清除所有的touch target  
                resetTouchState();//重置所有touch状况,准备新的touch状况周期  
            }  
  
              
       /** 
        * 第二步:检查是否要拦截(Check for interception) 
        * 在哪些情况下会调用该代码呢?有如下几种情况 
        * 1 处理ACTION_DOWN事件时 
        * 2 当ACTION_DOWN事件被子View消费后处理ACTION_MOVE和ACTION_UP时 
        *     因为此时mFirstTouchTarget!=null。所以此时ViewGroup 
        *     是有机会拦截ACTION_MOVE和ACTION_UP的,但是我们也可以调用方法: 
        *     requestDisallowInterceptTouchEvent来禁止ViewGroup的事件拦截. 
        *     如果子View没有消费Touch事件,那么那么当后续的ACTION_MOVE和ACTION_UP 
        *     到来时是不会调用到本处代码的. 
        *   
        * ViewGroup.dispatchTouchEven中,使用变量intercepted来标记ViewGroup是否拦截Touch事件的传递. 
        * 该变量在后续代码中起着很重要的作用. 
        *  
        * 从此处if(actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null)及其内部代码可知: 
        * 当ViewGroup决定拦截事件后,那么后续的点击事件将会默认交给它处理,不再调用onInterceptTouchEvent() 
        *    因为在处理ACTION_DOWN时如果Touch事件被子View消费,那么mFirstTouchTarget不为空; 
        *    反之,如果Touch事件没有被子View消费,那么mFirstTouchTarget为空,即此时Touch由当前 
        *    的ViewGroup拦截。此时当ACTION_MOVE和ACTION_UP来到时,不再满足: 
        *    if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) 
        *    当然也就无法调用其内部的onInterceptTouchEvent()。 
        * 通俗地说:一旦ViewGroup拦截了ACTION_DOWN事件由自身的onTouchEvent()处理,那么 
        * 对于后续的ACTION_MOVE和ACTION_UP而言ViewGroup不再调用onInterceptTouchEvent() 
        *  
        * 这里有个东西需要注意:FLAG_DISALLOW_INTERCEPT 
        * 在子View中调用requestDisallowInterceptTouchEvent()后造成disallowIntercept为true 
        * 即禁止拦截.于是不满足if(!disallowIntercept)所以也就调用不到该if内的onInterceptTouchEvent() 
        * 自然就没有办法拦截了. 
        * 但是requestDisallowInterceptTouchEvent()对于ACTION_DOWN是无效的. 
        * 因为对于ACTION_DOWN会调用 cancelAndClearTouchTargets(ev)和resetTouchState(); 
        * 对FLAG_DISALLOW_INTERCEPT等状态值复原重置(参考上面的代码) 
        *  
        * 举两种情况说明: 
        * 1 当处理ACTION_DOWN时当然会满足 
        *  if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) 
        *  对于ACTION_DOWN子View有两种处理结果 
        *  1.1 消耗了Touch事件,那么mFirstTouchTarget不为null. 
        *      所以处理后续的ACTION_MOVE和ACTION_UP时依然满足该if判断 
        *  1.2 没有消耗Touch事件.mFirstTouchTarget=null.不满足该if. 
        *      所以后续的ACTION_MOVE和ACTION_UP由ViewGroup处理,此时再讨论什么拦截也就没有意义了. 
        *      同样的道理当子View消费了ACTION_DOWN后当处理ACTION_MOVE的时候ViewGroup拦截了该事件 
        *      那么当ACTION_UP随之到来时由于mFirstTouchTarget=null所以不会再调用该段代码,自然也就 
        *      不会调用onInterceptTouchEvent()判断是否拦截了.这点在上面的注释也有提及 
        * 2 当出现1.1的情况时满足该if判断. 
        *  如果在子View中调用了requestDisallowInterceptTouchEvent()那么就禁止拦截 
        *  即disallowIntercept=true.所以不满足if (!disallowIntercept)当然也就调用不到 
        *  onInterceptTouchEvent(ev)了,而是执行else{ intercepted = false;} 
        *  也就是说ViewGroup无法拦截Touch了. 
        */  
            final boolean intercepted;//使用变量intercepted来标记ViewGroup是否拦截Touch事件的传递.  
  
            // 事件为ACTION_DOWN或者mFirstTouchTarget不为null(即已经找到能够接收touch事件的目标组件)时if成立  
            if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) {  
                //判断disallowIntercept(禁止拦截)标志位  
        //因为在其他地方可能调用了requestDisallowInterceptTouchEvent()改变该值.  
                final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;  
                //当禁止拦截为false时(即disallowIntercept为false)调用onInterceptTouchEvent(ev)方法  
                if (!disallowIntercept) {  
                    //既然disallowIntercept为false那么就调用onInterceptTouchEvent()方法将结果赋值给intercepted  
                    //这就是常说的事件传递流程:dispatchTouchEvent->onInterceptTouchEvent->onTouchEvent  
                    //注意:这是dispatchTouchEvent中唯一调用onInterceptTouchEvent()的地方  
                    //所以,只有2种情况系统会调用onInterceptTouchEvent:  
                    //1.派发action_donwn;2.ViewGroup里有子控件拦截或消费了action_down  
                    //换句话说,若ViewGroup拦截消费了down,那么它在接收后续的up和move时不再调用onInterceptTouchEvent  
                    intercepted = onInterceptTouchEvent(ev);  
                    ev.setAction(action); // restore action in case it was changed  
                } else {  
                    //禁止拦截的FLAG为ture说明没有必要去执行是否需要拦截了能够顺利通过,所以设置拦截变量为false  
                    //即,当禁止拦截为true时(即disallowIntercept为true)设置intercepted = false  
                    intercepted = false;  
                }  
            } else {  
                //当事件不是ACTION_DOWN并且mFirstTouchTarget为null(即没有Touch的目标组件)时  
                //设置 intercepted = true表示ViewGroup执行Touch事件拦截的操作。  
                //There are no touch targets and this action is not an initial down  
                //so this view group continues to intercept touches.  
                intercepted = true;  
            }  
  
              
            /** 
             * 第三步:检查cancel(Check for cancelation) 
             */  
            final boolean canceled = resetCancelNextUpFlag(this) || actionMasked == MotionEvent.ACTION_CANCEL;  
  
              
            /** 
             * 第四步:事件分发(Update list of touch targets for pointer down, if needed) 
             */  
            final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;  
            TouchTarget newTouchTarget = null;  
            boolean alreadyDispatchedToNewTouchTarget = false;  
            //不是ACTION_CANCEL并且ViewGroup的拦截标志位intercepted为false(不拦截)  
            if (!canceled && !intercepted) { //这是intercepted体现作用的唯一一行代码  
                //处理ACTION_DOWN事件.这个环节比较繁琐.  
                if (actionMasked == MotionEvent.ACTION_DOWN  
                    || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)  
                    || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {  
                    final int actionIndex = ev.getActionIndex(); // always 0 for down  
                    final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex):TouchTarget.ALL_POINTER_IDS;  
  
                    // Clean up earlier touch targets for this pointer id in case they  
                    // have become out of sync.  
                    removePointersFromTouchTargets(idBitsToAssign);  
  
                    final int childrenCount = mChildrenCount;  
                    if (childrenCount != 0) {  
                        // 依据Touch坐标寻找子View来接收Touch事件  
                        // Find a child that can receive the event.  
                        // Scan children from front to back.  
                        final View[] children = mChildren;  
                        final float x = ev.getX(actionIndex);  
                        final float y = ev.getY(actionIndex);  
  
                        final boolean customOrder = isChildrenDrawingOrderEnabled();  
                        // 遍历子View判断哪个子View接受Touch事件  
                        for (int i = childrenCount - 1; i >= 0; i--) {  
                            final int childIndex = customOrder ? getChildDrawingOrder(childrenCount, i) : i;  
                            final View child = children[childIndex];  
                            if (!canViewReceivePointerEvents(child) || !isTransformedTouchPointInView(x, y, child, null)) {  
                                continue;  
                            }  
  
                            newTouchTarget = getTouchTarget(child);  
                            if (newTouchTarget != null) {  
                                // 找到接收Touch事件的子View!!!!!!!即为newTouchTarget.  
                                // 既然已经找到了,所以执行break跳出for循环  
                                // Child is already receiving touch within its bounds.  
                                // Give it the new pointer in addition to the ones it is handling.  
                                newTouchTarget.pointerIdBits |= idBitsToAssign;  
                                break;  
                            }  
  
                            resetCancelNextUpFlag(child);  
                            /** 
                             * 如果上面的if不满足,当然也不会执行break语句. 
                             * 于是代码会执行到这里来. 
                             *  
                             *  
                             * 调用方法dispatchTransformedTouchEvent()将Touch事件传递给子View做 
                             * 递归处理(也就是遍历该子View的View树) 
                             * 该方法很重要,看一下源码中关于该方法的描述: 
                             * Transforms a motion event into the coordinate space of a particular child view, 
                             * filters out irrelevant pointer ids, and overrides its action if necessary. 
                             * If child is null, assumes the MotionEvent will be sent to this ViewGroup instead. 
                             * 将Touch事件传递给特定的子View. 
                             * 该方法十分重要!!!!!!!!!!!!!!!!! 
                             * 在该方法中为一个递归调用,会递归调用dispatchTouchEvent()方法!!!!!!!!!! 
                             * 在dispatchTouchEvent()中: 
                             * 如果子View为ViewGroup并且Touch没有被拦截那么递归调用dispatchTouchEvent() 
                             * 如果子View为View那么就会调用其onTouchEvent(),这个不再赘述. 
                             *  
                             *  
                             * 该方法返回true则表示子View消费掉该事件,同时进入该if判断. 
                             * 满足if语句后重要的操作有: 
                             * 1 给newTouchTarget赋值 
                             * 2 给alreadyDispatchedToNewTouchTarget赋值为true. 
                             *   看这个比较长的英语名字也可知其含义:已经将Touch派发给新的TouchTarget 
                             * 3 执行break. 
                             *   因为该for循环遍历子View判断哪个子View接受Touch事件,既然已经找到了 
                             *   那么就跳出该for循环. 
                             * 4 注意: 
                             *   如果dispatchTransformedTouchEvent()返回false即子View的onTouchEvent返回false 
                             *   (即Touch事件未被消费)那么就不满足该if条件.所以也就无法执行addTouchTarget(). 
                             *   在此简单说一下addTouchTarget()中涉及到的ViewGroup的一个内部类TouchTarget——它是一个事件链. 
                             *   该处的mFirstTouchTarget就是一个TouchTarget.它保存了可以消耗Touch事件的View. 
                             *   在该处,如果dispatchTransformedTouchEvent()返回true即子View的onTouchEvent返回true则说明 
                             *   该View消耗了Touch事件,那么将该View加入到事件链中!!!!!!!!!!!!!!! 
                             *   尤其注意: 
                             *   这个操作是在处理ACTION_DOWN的代码块里进行的.即是在: 
                             *    if (actionMasked == MotionEvent.ACTION_DOWN||  
                             *    (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN) ||  
                             *    actionMasked == MotionEvent.ACTION_HOVER_MOVE) 
                             *    这个大的if判断中处理的. 
                             *    当处理ACTION_MOVE事件和ACTION_UP事件的时候是不会进入这个if判断的!!!!! 
                             *    而是直接从去判断mFirstTouchTarget!!!!!!!!!!!!!!!! 
                             *    所以如果一个View不处理ACTION_DOWN那么该,那么该View是不会保存在mFirstTouchTarget 
                             *    中的,也就无法继续处理ACTION_MOVE事件和ACTION_UP事件!!!!!!!!!!即若该View不消耗 
                             *    ACTION_DOWN事件那么系统是不会讲ACTION_MOVE和ACTION_UP事件传给给该View的 
                             * 5 注意: 
                             *   如果dispatchTransformedTouchEvent()返回true即子View 
                             *   的onTouchEvent返回true(即Touch事件被消费)那么就满足该if条件. 
                             *   从而mFirstTouchTarget不为null!!!!!!!!!!!!!!!!!!! 
                             * 6 小结: 
                             *   对于此处ACTION_DOWN的处理具体体现在dispatchTransformedTouchEvent() 
                             *   该方法返回boolean,如下: 
                             *   true---->事件被消费----->mFirstTouchTarget!=null 
                             *   false--->事件未被消费--->mFirstTouchTarget==null 
                             *   因为在dispatchTransformedTouchEvent()会调用递归调用dispatchTouchEvent()和onTouchEvent() 
                             *   所以dispatchTransformedTouchEvent()的返回值实际上是由onTouchEvent()决定的. 
                             *    
                             *   简单地说onTouchEvent()是否消费了Touch事件(true or false)的返回值决定了 
                             *   dispatchTransformedTouchEvent()的返回值!!!!从而决定了mFirstTouchTarget是否为null!!!!!! 
                             *   从而进一步决定了ViewGroup是否处理Touch事件.这一点在下面的代码中很有体现. 
                             */  
                            if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {  
                                // Child wants to receive touch within its bounds.  
                                mLastTouchDownTime = ev.getDownTime();  
                                mLastTouchDownIndex = childIndex;  
                                mLastTouchDownX = ev.getX();  
                                mLastTouchDownY = ev.getY();  
                                //调用addTouchTarget()将child添加到mFirstTouchTarget链表的表头  
                                //注意在addTouchTarget()方法内部会对mFirstTouchTarget操作,使其不为null  
                                newTouchTarget = addTouchTarget(child, idBitsToAssign);  
                                alreadyDispatchedToNewTouchTarget = true;  
                                break;  
                            }  
                        }//for循环结束  
                    }  
  
                      
                    /** 
                     * 该if条件表示: 
                     * 经过前面的for循环没有找到子View接收Touch事件并且之前的mFirstTouchTarget不为空 
                     */  
                    if (newTouchTarget == null && mFirstTouchTarget != null) {  
                        // Did not find a child to receive the event.  
                        // Assign the pointer to the least recently added target.  
                        newTouchTarget = mFirstTouchTarget;  
                        while (newTouchTarget.next != null) {  
                            newTouchTarget = newTouchTarget.next;  
                        }  
                        //newTouchTarget指向了最初的TouchTarget  
                        newTouchTarget.pointerIdBits |= idBitsToAssign;  
                    }//处理ACTION_DOWN结束  
                }  
            }  
  
              
              
            /** 
             * 经过上面对于ACTION_DOWN的处理后mFirstTouchTarget有两种情况: 
             * (当然如果不是ACTION_DOWN就不会经过上面较繁琐的流程而是从此处开始执行,比如ACTION_MOVE和ACTION_UP) 
             *  
             * 情况1 mFirstTouchTarget为null 
             *       即没有找到能够消费touch事件的子组件或者是touch事件被拦截了 
             * 情况2 mFirstTouchTarget不为null 
             *       即找到了能够消费touch事件的子组件则后续的touch事件都可以传递到子View 
             * 这两种情况的详细分析见下. 
             *  
             * 这两种情况下都会去调用方法: 
             * dispatchTransformedTouchEvent(MotionEvent event,boolean cancel,View child,int desiredPointerIdBits) 
             * 我们重点关注该方法的第三个参数View child. 
             * 详情请参加下面dispatchTransformedTouchEvent()源码分析 
             * 在该源码中解释了: 
             * 为什么子view对于Touch事件处理返回true那么其上层的ViewGroup就无法处理Touch事件了!!!!!!!!! 
             * 为什么子view对于Touch事件处理返回false那么其上层的ViewGroup才可以处理Touch事件!!!!!!!!!! 
             *  
             */  
            if (mFirstTouchTarget == null) {  
                /** 
                 * 情况1:mFirstTouchTarget为null 
                 *  
                 * 经过上面的分析mFirstTouchTarget为null就是说Touch事件未被消费. 
                 * 即没有找到能够消费touch事件的子组件或Touch事件被拦截了, 
                 * 则调用ViewGroup的dispatchTransformedTouchEvent()方法处理Touch事件则和普通View一样. 
                 * 即子View没有消费Touch事件,那么子View的上层ViewGroup才会调用其onTouchEvent()处理Touch事件. 
                 * 在源码中的注释为:No touch targets so treat this as an ordinary view. 
                 * 也就是说此时ViewGroup像一个普通的View那样调用dispatchTouchEvent(),且在dispatchTouchEvent() 
                 * 中会去调用onTouchEvent()方法. 
                 * 具体的说就是在调用dispatchTransformedTouchEvent()时第三个参数为null. 
                 * 第三个参数View child为null会做什么样的处理呢? 
                 * 请参见下面dispatchTransformedTouchEvent()的源码分析 
                 */  
                handled = dispatchTransformedTouchEvent(ev, canceled, null,TouchTarget.ALL_POINTER_IDS);  
            } else {  
                /** 
                 * 情况2:mFirstTouchTarget不为null 
                 * 即找到了可以消费Touch事件的子View且后续Touch事件可以传递到该子View 
                 * 在源码中的注释为: 
                 * Dispatch to touch targets, excluding the new touch target if we already dispatched to it.   
                 * Cancel touch targets if necessary. 
                 */  
                TouchTarget predecessor = null;  
                TouchTarget target = mFirstTouchTarget;  
                while (target != null) {  
                    final TouchTarget next = target.next;  
                    if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {  
                        //如果前面利用ACTION_DOWN事件寻找符合接收条件的子组件的同时消费掉了ACTION_DOWN事件  
                        //那么这里为handled赋值为true  
                        handled = true;  
                    } else {  
                        final boolean cancelChild = resetCancelNextUpFlag(target.child) || intercepted;  
                        //对于非ACTION_DOWN事件继续传递给目标子组件进行处理  
                        //依然是递归调用dispatchTransformedTouchEvent()  
                        if (dispatchTransformedTouchEvent(ev, cancelChild, target.child, target.pointerIdBits)) {  
                            handled = true;  
                        }  
                        if (cancelChild) {  
                            if (predecessor == null) {  
                                mFirstTouchTarget = next;  
                            } else {  
                                predecessor.next = next;  
                            }  
                            target.recycle();  
                            target = next;  
                            continue;  
                        }  
                    }  
                    predecessor = target;  
                    target = next;  
                }  
            }  
  
            /** 
             * 处理ACTION_UP和ACTION_CANCEL 
             * Update list of touch targets for pointer up or cancel, if needed. 
             * 在此主要的操作是还原状态 
             */  
            if (canceled|| actionMasked == MotionEvent.ACTION_UP  
                        || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {  
                resetTouchState();  
            } else if (split && actionMasked == MotionEvent.ACTION_POINTER_UP) {  
                final int actionIndex = ev.getActionIndex();  
                final int idBitsToRemove = 1 << ev.getPointerId(actionIndex);  
                removePointersFromTouchTargets(idBitsToRemove);  
            }  
        }  
  
        if (!handled && mInputEventConsistencyVerifier != null) {  
            mInputEventConsistencyVerifier.onUnhandledEvent(ev, 1);  
        }  
        return handled;  
    }  
      
      
      
    //=====================以上为dispatchTouchEvent()源码分析======================  
      
      
      
      
      
      
      
    //===============以下为dispatchTransformedTouchEvent()源码分析=================  
      
    /** 
     * 在dispatchTouchEvent()中会调用dispatchTransformedTouchEvent()将事件分发给子View处理 
     *  
     * Transforms a motion event into the coordinate space of a particular child view, 
     * filters out irrelevant pointer ids, and overrides its action if necessary. 
     * If child is null, assumes the MotionEvent will be sent to this ViewGroup instead. 
     *  
     * 在此请着重注意第三个参数:View child 
     * 在dispatchTouchEvent()中多次调用dispatchTransformedTouchEvent(),但是有时候第三个参数为null,有时又不是. 
     * 那么这个参数是否为null有什么区别呢? 
     * 在如下dispatchTransformedTouchEvent()源码中可见多次对于child是否为null的判断,并且均做出如下类似的操作: 
     * if (child == null) { 
     *       handled = super.dispatchTouchEvent(event); 
     *    } else { 
     *       handled = child.dispatchTouchEvent(event); 
     * } 
     * 这个代码是什么意思呢? 
     *  
     * 当child == null时会将Touch事件传递给该ViewGroup自身的dispatchTouchEvent()处理. 
     * 即super.dispatchTouchEvent(event) 
     * 正如源码中的注释描述的一样: 
     * If child is null, assumes the MotionEvent will be sent to this ViewGroup instead. 
     *  
     * 当child != null时会调用该子view(当然该view可能是一个View也可能是一个ViewGroup)的dispatchTouchEvent(event)处理. 
     * 即child.dispatchTouchEvent(event); 
     *  
     * 那么该child是否为null又是由什么决定的呢? 
     * 在dispatchTouchEvent()中已经知道了: 
     * 如果Touch事件被消耗掉那么child不为null 
     * 如果Touch事件未被消耗掉那么child为null 
     *  
     * 这就解释了: 
     * 为什么子view对于Touch事件处理返回true那么其上层的ViewGroup就无法处理Touch事件了!!!!!!!!! 
     * 为什么子view对于Touch事件处理返回false那么其上层的ViewGroup才可以处理Touch事件!!!!!!!!!! 
     */  
    private boolean dispatchTransformedTouchEvent(MotionEvent event,boolean cancel,View child,int desiredPointerIdBits) {  
        final boolean handled;  
        // Canceling motions is a special case.  We don't need to perform any transformations  
        // or filtering.  The important part is the action, not the contents.  
        final int oldAction = event.getAction();  
        if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {  
            event.setAction(MotionEvent.ACTION_CANCEL);  
            if (child == null) {  
                handled = super.dispatchTouchEvent(event);  
            } else {  
                handled = child.dispatchTouchEvent(event);  
            }  
            event.setAction(oldAction);  
            return handled;  
        }  
  
        // Calculate the number of pointers to deliver.  
        final int oldPointerIdBits = event.getPointerIdBits();  
        final int newPointerIdBits = oldPointerIdBits & desiredPointerIdBits;  
  
        // If for some reason we ended up in an inconsistent state where it looks like we  
        // might produce a motion event with no pointers in it, then drop the event.  
        if (newPointerIdBits == 0) {  
            return false;  
        }  
  
        // If the number of pointers is the same and we don't need to perform any fancy  
        // irreversible transformations, then we can reuse the motion event for this  
        // dispatch as long as we are careful to revert any changes we make.  
        // Otherwise we need to make a copy.  
        final MotionEvent transformedEvent;  
        if (newPointerIdBits == oldPointerIdBits) {  
            if (child == null || child.hasIdentityMatrix()) {  
                if (child == null) {  
                    //调用自身的dispatchTouchEvent()!!!!!!  
                    handled = super.dispatchTouchEvent(event);  
                } else {  
                    final float offsetX = mScrollX - child.mLeft;  
                    final float offsetY = mScrollY - child.mTop;  
                    event.offsetLocation(offsetX, offsetY);  
                    //调用子View的dispatchTouchEvent()!!!!!!  
                    handled = child.dispatchTouchEvent(event);  
                    event.offsetLocation(-offsetX, -offsetY);  
                }  
                return handled;  
            }  
            transformedEvent = MotionEvent.obtain(event);  
        } else {  
            transformedEvent = event.split(newPointerIdBits);  
        }  
  
        // Perform any necessary transformations and dispatch.  
        if (child == null) {  
            handled = super.dispatchTouchEvent(transformedEvent);  
        } else {  
            final float offsetX = mScrollX - child.mLeft;  
            final float offsetY = mScrollY - child.mTop;  
            transformedEvent.offsetLocation(offsetX, offsetY);  
            if (! child.hasIdentityMatrix()) {  
                transformedEvent.transform(child.getInverseMatrix());  
            }  
  
            handled = child.dispatchTouchEvent(transformedEvent);  
        }  
  
        transformedEvent.recycle();  
        return handled;  
    }  
  //===============以上为dispatchTransformedTouchEvent()源码分析=================  
  
}  
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,026评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,655评论 1 296
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,726评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,204评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,558评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,731评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,944评论 2 314
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,698评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,438评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,633评论 2 247
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,125评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,444评论 3 255
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,137评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,103评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,888评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,772评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,669评论 2 271

推荐阅读更多精彩内容