欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android事件分發(fā)機制?ViewGroup分析

 更新時間:2022年09月07日 08:27:40   作者:是扣肉哦???????  
這篇文章主要介紹了Android事件分發(fā)機制?ViewGroup分析,事件分發(fā)從手指觸摸屏幕開始,即產(chǎn)生了觸摸信息,被底層系統(tǒng)捕獲后會傳遞給Android的輸入系統(tǒng)服務(wù)IMS,更多相關(guān)介紹,需要的朋友可以參考一下

前言:

事件分發(fā)從手指觸摸屏幕開始,即產(chǎn)生了觸摸信息,被底層系統(tǒng)捕獲后會傳遞給Android的輸入系統(tǒng)服務(wù)IMS,通過Binder把消息發(fā)送到activity,activity會通過phoneWindow、DecorView最終發(fā)送給ViewGroup。這里就直接分析ViewGroup的事件分發(fā)

整體流程

配合圖在看一段偽代碼:

public boolean dispatchTouchEvent(MotionEvent ev) :Boolean{
    val result = false  //處理結(jié)果,默認(rèn)是沒消費過的

    if (!onInterceptTouchEvent(ev)){ //是否攔截
        result = child.dispatchTouchEvent(ev) // 分發(fā)給子view處理
    }

    if (!result){ //事件沒有消費
        if (onTouchListener != null) { //先詢問是否設(shè)置了onTouchListener
            result = onTouchListener.onTouch(ev)
        }
        if (!result) { //還是沒有消費就交給onTouchEvent處理
            result = onTouchEvent(ev)
        }
    }

    return result
}

這張圖和這段偽代碼實際上已經(jīng)概括了ViewGroup和View對事件處理的整個流程,注意只有ViewGroup有攔截機制即onInterceptTouchEvent

源碼分析

在分析源碼之前先了解個基本概念 同一事件序列:同一個事件序列是指從手指接觸屏幕的那一刻起,到手指離開屏幕的那一刻結(jié)束,在這個過程中所產(chǎn)生的一系列事件,這個事件序列以down事件開始,中間含有數(shù)量不定的move事件,最終以up事件結(jié)束

public boolean dispatchTouchEvent(MotionEvent ev) {

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

        /**
         * step1
         * ACTION_DOWN是一個系列事件的起點,終點是ACTION_UP
         * 如果是ACTION_DOWN會重置一些flag并且會把mFirstTouchTarget置空
         */
        if (actionMasked == MotionEvent.ACTION_DOWN) {
            cancelAndClearTouchTargets(ev);
            resetTouchState();
        }

        final boolean intercepted;//變量判斷消息是否被攔截
        /**
         * step2
         * 從以下代碼可以看出如果事件不是ACTION_DOWN并且mFirstTouchTarget為空的話那么ViewGroup是不能再攔截同一系列的事件了
         * mFirstTouchTarget 代表的就是一個單鏈表,它會把處理當(dāng)前這一系列事件的view保存下來
         * 假如當(dāng)前事件是ACTION_MOVE,并攔截了該事件那么會在step9中把mFirstTouchTarget置空
         *
         * 結(jié)論1:
         * 如果View決定攔截一個事件那么該View的 onInterceptTouchEvent 方法不會再被調(diào)用了,
         * 同一序列事件后續(xù)的所有事件都只能由該View處理(當(dāng)然前提是事件能分發(fā)到該view,有可能在上層被攔截了)
         */
        if (actionMasked == MotionEvent.ACTION_DOWN
                || mFirstTouchTarget != null) {
            /**
             * disallowIntercept表示是否禁用攔截功能,子view通過 requestDisallowInterceptTouchEvent 方法
             * 可以要求父view不準(zhǔn)攔截事件,不過該方法在MotionEvent.ACTION_DOWN事件中不起作用,因為在step1中會把所有標(biāo)志位重置
             *
             */
            final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
            if (!disallowIntercept) {
                intercepted = onInterceptTouchEvent(ev);
                ev.setAction(action); // restore action in case it was changed
            } else {
                intercepted = false;
            }
        } else {
            /**
             * 如果進(jìn)不到上面的if判斷則表示當(dāng)前系列事件viewGroup已經(jīng)攔截過某個事件了
             * intercepted 直接置為true
             */
            intercepted = true;
        }


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

        final boolean isMouseEvent = ev.getSource() == InputDevice.SOURCE_MOUSE;
        final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0
                && !isMouseEvent;
        TouchTarget newTouchTarget = null;
        boolean alreadyDispatchedToNewTouchTarget = false;
        /**
         * step3
         * 看這里如果ViewGroup攔截了該事件則不會進(jìn)入step3里面了,而是直接走到step9中
         */
        if (!canceled && !intercepted) {
            /**
             * step4
             * 這里我們只考慮單指的點擊、移動和抬起
             * ACTION_POINTER_DOWN和多點觸控有關(guān),ACTION_HOVER_MOVE和鼠標(biāo)有關(guān)
             * 所以如果當(dāng)前事件是MOVE也不會走step4也是直接走到step9中找到對應(yīng)的子view繼而分發(fā)事件
             * 結(jié)論2:如果DOWN事件被某個view消耗那么后續(xù)的事件都會直接交給這個view(前提是父view沒有攔截)
             */
            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;

                final int childrenCount = mChildrenCount;
                if (newTouchTarget == null && childrenCount != 0) {
                    final float x =
                            isMouseEvent ? ev.getXCursorPosition() : ev.getX(actionIndex);
                    final float y =
                            isMouseEvent ? ev.getYCursorPosition() : ev.getY(actionIndex);
                    // Find a child that can receive the event.
                    // Scan children from front to back.
                    final ArrayList<View> preorderedList = buildTouchDispatchChildList();
                    final boolean customOrder = preorderedList == null
                            && isChildrenDrawingOrderEnabled();
                    final View[] children = mChildren;
                    /**
                     * step5
                     * 遍歷所有的子view
                     */
                    for (int i = childrenCount - 1; i >= 0; i--) {
                        final int childIndex = getAndVerifyPreorderedIndex(
                                childrenCount, i, customOrder);
                        final View child = getAndVerifyPreorderedView(
                                preorderedList, children, childIndex);

                        //省略部分代碼。。。
                        /**
                         * step6
                         * 當(dāng)找到一個合適的子view時,在 dispatchTransformedTouchEvent 中會調(diào)用子view的dispatchTouchEvent
                         * 如果該子view消耗了事件,會把子view保存到mFirstTouchTarget對應(yīng)的鏈表中,并結(jié)束for循環(huán)
                         *
                         * 結(jié)論3:
                         * 如果一個view沒有消耗DOWN事件那么后續(xù)的事件都不會再分發(fā)給該view
                         * 該結(jié)論和結(jié)論2呼應(yīng)上了,因為在這個for循環(huán)中只有子view的 dispatchTransformedTouchEvent返回true才會被加入到鏈表中
                         * 下一次的事件并不會再到step4中來了
                         */
                        if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
                            // Child wants to receive touch within its bounds.
                            mLastTouchDownTime = ev.getDownTime();
                            if (preorderedList != null) {
                                // childIndex points into presorted list, find original index
                                for (int j = 0; j < childrenCount; j++) {
                                    if (children[childIndex] == mChildren[j]) {
                                        mLastTouchDownIndex = j;
                                        break;
                                    }
                                }
                            } else {
                                mLastTouchDownIndex = childIndex;
                            }
                            mLastTouchDownX = ev.getX();
                            mLastTouchDownY = ev.getY();
                            /**
                             * step7
                             * 把子view保存到鏈表中,mFirstTouchTarget指向表頭
                             * alreadyDispatchedToNewTouchTarget置為true
                             * 結(jié)束for循環(huán)
                             */
                            newTouchTarget = addTouchTarget(child, idBitsToAssign);
                            alreadyDispatchedToNewTouchTarget = true;
                            break;
                        }

                        // The accessibility focus didn't handle the event, so clear
                        // the flag and do a normal dispatch to all children.
                        ev.setTargetAccessibilityFocus(false);
                    }
                }
            }
        }

        /**
         * step8
         * 如果攔截了事件會把 mFirstTouchTarget 置空這個時候就直接調(diào)用viewGroup的super.dispatchTouchEvent
         * 即view中的dispatchTouchEvent
         */
        if (mFirstTouchTarget == null) {
            // No touch targets so treat this as an ordinary view.
            handled = dispatchTransformedTouchEvent(ev, canceled, null,
                    TouchTarget.ALL_POINTER_IDS);
        } else {
            // 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;
            /**
             * step9
             * 如果攔截了就把mFirstTouchTarget置空,沒有攔截就找到對應(yīng)的childView把事件分發(fā)下去
             */
            while (target != null) {
                final TouchTarget next = target.next;
                if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
                    handled = true;
                } else {
                    final boolean cancelChild = resetCancelNextUpFlag(target.child)
                            || intercepted;
                    //注意這里cancelChild如果為true,并且target.child不為空的話,dispatchTransformedTouchEvent會把事件轉(zhuǎn)成CANCEL分發(fā)給target.child
                    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;
            }
        }

    }

    return handled;
}

看下cancel事件的由來,這里需要結(jié)合上文代碼step9看

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) {
        /**
         * 把事件轉(zhuǎn)換成ACTION_CANCEL
         */
        event.setAction(MotionEvent.ACTION_CANCEL);
        if (child == null) {
            handled = super.dispatchTouchEvent(event);
        } else {
            /**
             * 如果child不為空就分發(fā)給它
             */
            handled = child.dispatchTouchEvent(event);
        }
        event.setAction(oldAction);
        return handled;
    }
    
    return handled;
}

到此這篇關(guān)于Android事件分發(fā)機制 ViewGroup分析的文章就介紹到這了,更多相關(guān)Android  ViewGroup內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論