android 事件分发流程源码分析注释笔记

package android.view;

public abstract class ViewGroup extends View implements ViewParent, ViewManager

//ViewGroup分发事件方法
//该方法返回false表示ViewGroup自身和子View都没有人消费事件,返回true表示
//有人消费了,具体谁消费的,保存在mFirstTouchTarget中,这样后续就不用再一个个遍历查找了

public boolean dispatchTouchEvent(MotionEvent ev) {

        
        boolean handled = false; 
        
        final int action = ev.getAction();
        final int actionMasked = action & MotionEvent.ACTION_MASK;

        if (actionMasked == MotionEvent.ACTION_DOWN) {
            //如果当前是down事件,重置参数
            cancelAndClearTouchTargets(ev);
            resetTouchState();
        }

        final boolean intercepted;

        //如果是down事件 或者 已经找到能消费事件的子view了,进入下面if
        if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) {

            //如果是down事件,那么肯定要判断是否拦截事件,如果是move事件并且已经找到可以消费事件的子view,那么每次调用move方法的时候,也是要判断是否拦截的,并不是down事件不拦截,然后move、up也不判断拦截。另外,FLAG_DISALLOW_INTERCEPT这个变量是子view 申请父控件不要拦截事件的变量,具体在子view中获取父view调用requestDisallowInterceptTouchEvent()来实现。

            final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
            if (!disallowIntercept) {
                //如果子view没有要求父view拦截事件
                intercepted = onInterceptTouchEvent(ev);
                ev.setAction(action); 
            } else {
                //子view要求父view拦截事件
                intercepted = false;
            }
        } else {
            //如果不是down事件(move或up)并且 没有找到能消费事件的子view,就直接拦截了,也就是不要把后续事件再交给子view了,
            //全由父view自己处理,也就是当前viewGroup。
            intercepted = true;
        }

        final boolean canceled = resetCancelNextUpFlag(this)
                || actionMasked == MotionEvent.ACTION_CANCEL;

        //如果需要,更新指针向下的触摸目标列表
        final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;

        //这个单链表对象记录能消费事件的子view
        TouchTarget newTouchTarget = null;

        //是否已经发送事件到目标子view
        boolean alreadyDispatchedToNewTouchTarget = false;

        if (!canceled && !intercepted) {
            //如果没有拦截事件,就会进这个方法。

            if (actionMasked == MotionEvent.ACTION_DOWN
                    || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
                    || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
                final int actionIndex = ev.getActionIndex(); //总是为0,代表从上往下
                final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex)
                        : TouchTarget.ALL_POINTER_IDS;
                //down事件,多手指事件都会进来这个方法,move,up不进来。

                //清除此指针id的早期能够消费事件的view,以防它们已经不同步了
                removePointersFromTouchTargets(idBitsToAssign);

                final int childrenCount = mChildrenCount;
                if (newTouchTarget == null && childrenCount != 0) {
                    //如果暂未找到能够消费事件的子view并且子view不为0,那么遍历查找能够消费事件的子view.

                    final float x = ev.getX(actionIndex);
                    final float y = ev.getY(actionIndex);

                    //buildOrderedChildList()表示按照z轴从上往下排列子view,用于寻找可消费事件子view的时候从最上面找。
                    final ArrayList<View> preorderedList = buildOrderedChildList();
                    //判断是否要重新排序
                    final boolean customOrder = preorderedList == null
                            && isChildrenDrawingOrderEnabled();
                    final View[] children = mChildren;
                    for (int i = childrenCount - 1; i >= 0; i--) {
                        final int childIndex = customOrder
                                ? getChildDrawingOrder(childrenCount, i) : i;

                        final View child = (preorderedList == null)
                                ? children[childIndex] : preorderedList.get(childIndex);

                        ......

                        if (!canViewReceivePointerEvents(child)
                                || !isTransformedTouchPointInView(x, y, child, null)) {
                            ev.setTargetAccessibilityFocus(false);
                            //如果子view不能收到点击事件或者子view不在点击的范围之内,跳过该view
                            continue;
                        }

                        //第一次down事件进来从链表中是拿不到对象的,所以newTouchTarget必然返回null
                        newTouchTarget = getTouchTarget(child);
                        if (newTouchTarget != null) {
                            newTouchTarget.pointerIdBits |= idBitsToAssign;
                            //如果从链表中拿到了能够消费事件的子view,则直接跳出循环,防止每次都重新遍历一遍。
                            break;
                        }

                        //分发事件给子view去处理,返回true则有子view消费,返回false没有子view消费
                        //返回true创建newTouchTarget并赋值给mFirstTouchTarget变量
                        //一直递归找到子view中能消费的就break退出循环。
                        //这里走的是子view的handled = child.dispatchTouchEvent(event);方法
                        //递归过程:子view是viewgroup则继续分发事件,子view是view则开始处理事件,包括执行onTouch方法等。
                        if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {

                            //最终经过漫漫递归终于沿着一条路径找到了能够消费事件的view链条,逐级返回到这里为true。

                            mLastTouchDownTime = ev.getDownTime();
                            if (preorderedList != null) {
                                for (int j = 0; j < childrenCount; j++) {
                                    if (children[childIndex] == mChildren[j]) {
                                        mLastTouchDownIndex = j;
                                        break;
                                    }
                                }
                            } else {
                                mLastTouchDownIndex = childIndex;
                            }
                            mLastTouchDownX = ev.getX();
                            mLastTouchDownY = ev.getY();
                            //newTouchTarget这里赋值,不再为null,并且将newTouchTarget赋值为mFirstTouchTarget
                            //按照从最终消费的子view一层层往上回调的逻辑,这里的链表应该是永远往头部插入节点,最后形成的结构是从顶级父view到最终的子view,从上到下的一个单链接结构,头部节点为顶级viewGroup下一级能够消费的子view,尾部就是最后处理事件的子view。
                            newTouchTarget = addTouchTarget(child, idBitsToAssign);
                            //标记已经将事件分发给了子view并且子view能够消费(也就是说alreadyDispatchedToNewTouchTarget标记了本view肯定不是消费事件的view,而是它的下级们才有可能消费事件)。
                            alreadyDispatchedToNewTouchTarget = true;
                            break;
                        }

                        ev.setTargetAccessibilityFocus(false);
                    } // for循环结束
                    if (preorderedList != null) preorderedList.clear();
                }

                if (newTouchTarget == null && mFirstTouchTarget != null) {
                    newTouchTarget = mFirstTouchTarget;
                    while (newTouchTarget.next != null) {
                        newTouchTarget = newTouchTarget.next;
                    }
                    newTouchTarget.pointerIdBits |= idBitsToAssign;
                }
            }
        }

        //move,up事件到这里开始执行啦

        if (mFirstTouchTarget == null) {
            //无接触目标,因此将此视为普通视图
            //遍历完都没有找到能够消费事件的子view,则super.dispatchTouchEvent()自己处理事件。
            handled = dispatchTransformedTouchEvent(ev, canceled, null,
                    TouchTarget.ALL_POINTER_IDS);
        } else {
            //发送到触摸目标,如果我们已经发送到新的触摸目标,则不包括它。必要时取消触摸目标。
            //遍历完找到了能够消费事件的子view,这里处理move事件,不用再遍历了,直接找到mFirstTouchTarget。
            TouchTarget predecessor = null;
            TouchTarget target = mFirstTouchTarget;
            //开始遍历从顶级到最终消费事件的子view的所有路径,如果分发过事件的,直接handled=true,如果没有分发过事件的,就将事件分发给它处理。
            while (target != null) {
                final TouchTarget next = target.next;
                if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
                    //已经发送到新的目标的view肯定不是处理事件的view,直接返回true就可以了
                    handled = true;
                } else {
                    final boolean cancelChild = resetCancelNextUpFlag(target.child)
                            || intercepted;
                    //分发事件给找到的子view
                    if (dispatchTransformedTouchEvent(ev, cancelChild,
                            target.child, target.pointerIdBits)) {
                        //事件返回true
                        handled = true;
                    }
                    if (cancelChild) {
                        //子view取消事件,然后跳过该子view?
                        if (predecessor == null) {
                            //mFirstTouchTarget
                            mFirstTouchTarget = next;
                        } else {
                            predecessor.next = next;
                        }
                        target.recycle();
                        target = next;
                        continue;
                    }
                }
                //
                predecessor = target;
                target = next;
            }
        }
        //取消或则up事件走这里
        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);
        }
        

        return handled;
    }
//child参数为null,父view自己处理事件,child参数不为null,执行事件的分发。
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
            View child, int desiredPointerIdBits) {
        final boolean handled;

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

        //父view分发过来的事件cancel都为false,走下面流程,也就是没有取消事件.

        // 计算要传递的指针数.
        final int oldPointerIdBits = event.getPointerIdBits();
        final int newPointerIdBits = oldPointerIdBits & desiredPointerIdBits;

        if (newPointerIdBits == 0) {
            return false;
        }

        final MotionEvent transformedEvent;
        if (newPointerIdBits == oldPointerIdBits) {
            if (child == null || child.hasIdentityMatrix()) {
                if (child == null) {
                    handled = super.dispatchTouchEvent(event);
                } else {
                    final float offsetX = mScrollX - child.mLeft;
                    final float offsetY = mScrollY - child.mTop;
                    event.offsetLocation(offsetX, offsetY);

                    handled = child.dispatchTouchEvent(event);

                    event.offsetLocation(-offsetX, -offsetY);
                }
                return handled;
            }
            transformedEvent = MotionEvent.obtain(event);
        } else {
            transformedEvent = event.split(newPointerIdBits);
        }

        if (child == null) {
            //当父view没有找到能够消费事件的子view后调用dispatchTransformedTouchEvent(),并在child参数中传null,这样就让自己来处理事件了。
            handled = super.dispatchTouchEvent(transformedEvent);
        } else {
            handled = child.dispatchTouchEvent(transformedEvent);
        }

        return handled;
    }
//调用super.dispatchTouchEvent就会来这里
//这里处理view的事件
public boolean dispatchTouchEvent(MotionEvent event) {

        boolean result = false;

        final int actionMasked = event.getActionMasked();
        if (actionMasked == MotionEvent.ACTION_DOWN) {
            stopNestedScroll();
        }

        if (onFilterTouchEventForSecurity(event)) {

            ListenerInfo li = mListenerInfo;
        
            if (li != null && li.mOnTouchListener != null
                    && (mViewFlags & ENABLED_MASK) == ENABLED
                    && li.mOnTouchListener.onTouch(this, event)) {
                //1、判断view是否设置了OnTouchListener事件监听
                //2、以及view是否可用
                //3、调用onTouch方法,如果返回值为true,则整个方法返回true,表示消费事件,
                //后面流程都不走了
                result = true;
            }

            if (!result && onTouchEvent(event)) {
                //onTouch方法返回false才进来这里,调用onTouchEvent方法
                result = true;
            }
        }

        return result;
    }

 

//View消费事件的方法
//返回true表示消费事件,返回false表示不消费
public boolean onTouchEvent(MotionEvent event) {
        final float x = event.getX();
        final float y = event.getY();
        final int viewFlags = mViewFlags;
        final int action = event.getAction();

        if (((viewFlags & CLICKABLE) == CLICKABLE ||
                (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) ||
                (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE) {
            switch (action) {
                case MotionEvent.ACTION_UP:
                    boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;
                    if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {
                        boolean focusTaken = false;
                        
                        if (prepressed) {
                            setPressed(true, x, y);
                       }

                        if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {
                            removeLongPressCallback();

                            if (!focusTaken) {
                                //使用Runnable并发布此消息,而不是直接调用performClick。
                                //这允许在单击操作开始之前更新视图的其他可视状态
                                if (mPerformClick == null) {
                                    mPerformClick = new PerformClick();
                                }
                                if (!post(mPerformClick)) {
                                    //没有放进消息队列就直接调用
                                    performClick();
                                }
                            }
                        }

                        removeTapCallback();
                    }
                    mIgnoreNextUpEvent = false;
                    break;

                case MotionEvent.ACTION_DOWN:
                    mHasPerformedLongPress = false;

                    if (performButtonActionOnTouchDown(event)) {
                        break;
                    }

                    boolean isInScrollingContainer = isInScrollingContainer();

                    if (isInScrollingContainer) {
                        mPrivateFlags |= PFLAG_PREPRESSED;
                        if (mPendingCheckForTap == null) {
                            mPendingCheckForTap = new CheckForTap();
                        }
                        mPendingCheckForTap.x = event.getX();
                        mPendingCheckForTap.y = event.getY();
                        postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
                    } else {
                        setPressed(true, x, y);
                        checkForLongClick(0);
                    }
                    break;

                case MotionEvent.ACTION_CANCEL:
                    ......
                    break;

                case MotionEvent.ACTION_MOVE:
                    ......
                    break;
            }

            return true;
        }

        return false;
    }

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值