View体系9:View中的消息传递

对于一颗View树来说,它的消息传递应该是自上而下的,从根节点开始逐层往下递归传递;在消息流转过程中一旦有人处理了这个消息,那么传递即可宣告终止,从这一点来看,View树的上层具有消息处理的优先权。从理论角度来讲,因为我们并不能预先知道树的节点是View还是ViewGroup,因而递归过程中所规定的接口必定是由View提供的,然后由ViewGroup来重载#####

1.View中touchEvent的投递流程

1.1 View的dispatchTouchEvent负责事件的分发

View对象可以通过setOnTouchListener来设置一个Event监听,当有事件来临时候,onTouch会被调用;

如果没有设置TouchListener,或者flags中指明被disable了,又或者onTouch返回false,那么系统把Event传递给onTouchEvent。##
对比:onTouch简洁高效,onTouchEvent更适用于View扩展类的情况。

public boolean dispatchTouchEvent(MotionEvent event) { 
    if (onFilterTouchEventForSecurity(event)) { 
        ListenerInfo li = mListenerInfo;
        //onTouch 
        if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED
                && li.mOnTouchListener.onTouch(this, event)) {
            return true;
        }
        //onTouchEvent
        if (onTouchEvent(event)) { //
            return true;
        }
    } 
    return false;
}
 

1.2 onTouchEvent

1.2.1 disable的情况

public boolean onTouchEvent(MotionEvent event) {
    final int viewFlags = mViewFlags;
    if ((viewFlags & ENABLED_MASK) == DISABLED) {
        if (event.getAction() == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) {
            setPressed(false);
        }
        // A disabled view that is clickable still consumes the touch
        // events, it just doesn't respond to them.
        //view在被disable的情况下同样会消耗这个事件只是不作出任何响应
        return (((viewFlags & CLICKABLE) == CLICKABLE ||
                (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE));
    }
 

1.2.2 ACTION_DOWN

case MotionEvent.ACTION_DOWN:
    mHasPerformedLongPress = false;
    if (performButtonActionOnTouchDown(event)) {
        break;
    } 
    // Walk up the hierarchy to determine if we're inside a scrolling container.
    boolean isInScrollingContainer = isInScrollingContainer(); 
    // For views inside a scrolling container, delay the pressed feedback for
    // a short period in case this is a scroll.
    if (isInScrollingContainer) {
        mPrivateFlags |= PFLAG_PREPRESSED;
        if (mPendingCheckForTap == null) {
            mPendingCheckForTap = new CheckForTap();
        }
        postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
    } else {
        // Not inside a scrolling container, so show the feedback right away
        setPressed(true);//当前状态为pressed(pressed,normal)
        checkForLongClick(0);//检查是否被长按(通过ViewConfiguration.getLongPressTimeout来获取长按事件的产生标准)
    }
    break;

1.2.3 ACTION_MOVE

case MotionEvent.ACTION_MOVE:
    final int x = (int) event.getX();
    final int y = (int) event.getY(); 
    if (!pointInView(x, y, mTouchSlop)) {//是否已经超出了View范围 
        removeTapCallback();
        if ((mPrivateFlags & PFLAG_PRESSED) != 0) {
            // Remove any future long press/tap checks
            removeLongPressCallback();
            //不再是pressed状态
            setPressed(false);
        }
    }
    break; 

1.2.4 ACTION_UP

 case MotionEvent.ACTION_UP:
    boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;
    if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {
       

        if (!mHasPerformedLongPress) {//已经执行长按
            // This is a tap, so remove the longpress check
            removeLongPressCallback(); 
            if (!focusTaken) { 
                if (mPerformClick == null) {
                    mPerformClick = new PerformClick();
                }
                if (!post(mPerformClick)) {
                    performClick();
                }
            }
        } 
        if (mUnsetPressedState == null) {
            mUnsetPressedState = new UnsetPressedState();
        }

        if (prepressed) {
            postDelayed(mUnsetPressedState,
                    ViewConfiguration.getPressedStateDuration());
        } else if (!post(mUnsetPressedState)) { 
            mUnsetPressedState.run();
        }
        removeTapCallback();
    }
    break;

1.2.5 ACTION_CANCEL

case MotionEvent.ACTION_CANCEL:
    setPressed(false);
    removeTapCallback();
    removeLongPressCallback();
    break; 

2.ViewGroup中touchevent的投递流程

ViewGroup因为涉及子对象的处理,所以派发流程没有View那么直接简单,它重载了dispatchTouchEvent,对View提供的派发机制进行了重新规划。

拦截的概念,从深圳寄送快递到上海,中途会经过若干城市,那么当某个城市收到快递时,它首先判断要不要把快递拦截下来,也就是这个快递是不是要由这个城市进行处理,如果当前就是上海,那么快递就不应该继续向下传输,而是进入区->街道->门牌号等的内政处理环节,如果当前城市不是上海,而是其他中间城市,则收到快递时要继续往下一个城市运输,否则就会出现错误传递的情况。

public boolean dispatchTouchEvent(MotionEvent ev) {
   

    boolean handled = false;//event是否被处理
    if (onFilterTouchEventForSecurity(ev)) {
        final int action = ev.getAction();
        //判断是否是down事件
        final int actionMasked = action & MotionEvent.ACTION_MASK;

        // Handle an initial down.
        if (actionMasked == MotionEvent.ACTION_DOWN) {
            //down事件是后续事件的起点,
            cancelAndClearTouchTargets(ev);
            resetTouchState();//一旦收到down事件,先清除以前的所有状态
        }

        //检查interception的情况
        final boolean intercepted;
        if (actionMasked == MotionEvent.ACTION_DOWN
                || mFirstTouchTarget != null) {
            //如果是down事件或者mFirstTouchTarget不为空
            final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
            if (!disallowIntercept) {//ViewGroup允许拦截
                //android系统鼓励开发者在继承viewgroup的时候覆盖整个方法,而不是覆盖dispatchtouchEvent
                //
                intercepted = onInterceptTouchEvent(ev);

                ev.setAction(action); // restore action in case it was changed
            } else {//ViewGroup不允许拦截
                intercepted = false;//不需要拦截
            }
        } else {//如果不是down事件,而且之前的判断中mFirstTouchTarget为空,则ViewGroup继续拦截
            //继续拦截,viewgroup拦截该事件在内部处理这个时候意味这个该事件以及后续到达的Action_UP都会直接投递到ViewGroup的onTouchEvent中而不会继续传递
            intercepted = true;
        }

        // 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;
        if (!canceled && !intercepted) {//不拦截的情况
            //不拦截表示ViewGroup不希望拦截这一条消息,因而它的子对象将会有机会来处理它
            if (actionMasked == MotionEvent.ACTION_DOWN
                    || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
                    || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
                 
                //子对象的个数
                final int childrenCount = mChildrenCount;
                if (newTouchTarget == null && childrenCount != 0) {//子对象数量不为零
                    final float x = ev.getX(actionIndex);
                    final float y = ev.getY(actionIndex);
                  
                    final View[] children = mChildren;

                    final boolean customOrder = isChildrenDrawingOrderEnabled();
                    for (int i = childrenCount - 1; i >= 0; i--) {
                        //循环查找一个能处理此事件的child
                        final int childIndex = customOrder ?
                                getChildDrawingOrder(childrenCount, i) : i;
                        final View child = children[childIndex];
                        //canViewReceivePointerEvents该函数表示这个child能否接收pointerevent(包括touchevent)
                        //isTransformedTouchPointInView该函数表示x,y这个点是否落在child的管辖范围内
                        //也就是说如果这个child既要能接收事件而且touch的点还要在view的范围内才行,不满足的直接跳过
                        if (!canViewReceivePointerEvents(child)
                                || !isTransformedTouchPointInView(x, y, child, null)) {
                            continue;//不符合要求跳过
                        }
                        //找到了此touchEvent归属的child
                        newTouchTarget = getTouchTarget(child);
                        if (newTouchTarget != null) { 
                            newTouchTarget.pointerIdBits |= idBitsToAssign;
                            break;
                        }

                        resetCancelNextUpFlag(child);
                        //dispatchTransformedTouchEvent这个函数会调用到child的dispatchTouchEvent
                        //如果child是view,执行view的dispatchTouchEvent
                        //如果child是viewgroup,执行viewgroup的dispatchTouchEvent,
                        if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
                            //完成了任务,结束循环
                            break;
                        }
                    }
                }

                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.pointerIdBits |= idBitsToAssign;
                }
            }
        } 
    return handled;//返回处理结果
}

你可能感兴趣的:(View体系9:View中的消息传递)