事件很多 梳理一下(事件分發(fā)的那些事)

什么是事件

點(diǎn)擊事件(Touch事件)

事件類型
image.png

怎么描述事件: MotionEvent

事件分發(fā)的本質(zhì): 將點(diǎn)擊事件(MotionEvent)傳遞到某個(gè)具體的View & 處理的整個(gè)過(guò)程

事件分發(fā)的過(guò)程

事件的傳遞 activity ->viewgroup ->view


image.png

事件分發(fā)方法

dispatchTouchEvent() 、onInterceptTouchEvent()和onTouchEvent()


image.png

下面我們針對(duì)源碼 對(duì)這幾個(gè)方法進(jìn)行分析

Activity的事件分發(fā)機(jī)制

image.png

getWindow().superDispatchTouchEvent(ev)

window只有一個(gè)實(shí)現(xiàn)類PhoneWindow,所以我們看PhoneWindow里邊是怎么實(shí)現(xiàn)的


image.png

mDecor是DecorView 我們點(diǎn)進(jìn)去看DecorView會(huì)發(fā)現(xiàn)它繼承FrameLayout


image.png

即將事件傳遞到ViewGroup去處理。
我們總結(jié)一下就是
/**
 * 源碼分析:Activity.dispatchTouchEvent()
 */
public boolean dispatchTouchEvent(MotionEvent ev) {

    // 僅貼出核心代碼

    // ->>分析1
    if (getWindow().superDispatchTouchEvent(ev)) {

        return true;
        // 若getWindow().superDispatchTouchEvent(ev)的返回true
        // 則Activity.dispatchTouchEvent()就返回true,則方法結(jié)束。即 :該點(diǎn)擊事件停止往下傳遞 & 事件傳遞過(guò)程結(jié)束
        // 否則:繼續(xù)往下調(diào)用Activity.onTouchEvent

    }
    // ->>分析3
    return onTouchEvent(ev);
}

/**
 * 分析1:getWindow().superDispatchTouchEvent(ev)
 * 說(shuō)明:
 *     a. getWindow() = 獲取Window類的對(duì)象
 *     b. Window類是抽象類,其唯一實(shí)現(xiàn)類 = PhoneWindow類
 *     c. Window類的superDispatchTouchEvent() = 1個(gè)抽象方法,由子類PhoneWindow類實(shí)現(xiàn)
 */
@Override
public boolean superDispatchTouchEvent(MotionEvent event) {

    return mDecor.superDispatchTouchEvent(event);
    // mDecor = 頂層View(DecorView)的實(shí)例對(duì)象
    // ->> 分析2
}

/**
 * 分析2:mDecor.superDispatchTouchEvent(event)
 * 定義:屬于頂層View(DecorView)
 * 說(shuō)明:
 *     a. DecorView類是PhoneWindow類的一個(gè)內(nèi)部類
 *     b. DecorView繼承自FrameLayout,是所有界面的父類
 *     c. FrameLayout是ViewGroup的子類,故DecorView的間接父類 = ViewGroup
 */
public boolean superDispatchTouchEvent(MotionEvent event) {

    return super.dispatchTouchEvent(event);
    // 調(diào)用父類的方法 = ViewGroup的dispatchTouchEvent()
    // 即將事件傳遞到ViewGroup去處理,詳細(xì)請(qǐng)看后續(xù)章節(jié)分析的ViewGroup的事件分發(fā)機(jī)制

}
// 回到最初的分析2入口處

/**
 * 分析3:Activity.onTouchEvent()
 * 調(diào)用場(chǎng)景:當(dāng)一個(gè)點(diǎn)擊事件未被Activity下任何一個(gè)View接收/處理時(shí),就會(huì)調(diào)用該方法
 */
public boolean onTouchEvent(MotionEvent event) {

    // ->> 分析5
    if (mWindow.shouldCloseOnTouch(this, event)) {
        finish();
        return true;
    }

    return false;
    // 即 只有在點(diǎn)擊事件在Window邊界外才會(huì)返回true,一般情況都返回false,分析完畢
}

/**
 * 分析4:mWindow.shouldCloseOnTouch(this, event)
 * 作用:主要是對(duì)于處理邊界外點(diǎn)擊事件的判斷:是否是DOWN事件,event的坐標(biāo)是否在邊界內(nèi)等
 */
public boolean shouldCloseOnTouch(Context context, MotionEvent event) {

    if (mCloseOnTouchOutside && event.getAction() == MotionEvent.ACTION_DOWN
        && isOutOfBounds(context, event) && peekDecorView() != null) {

        // 返回true:說(shuō)明事件在邊界外,即 消費(fèi)事件
        return true;
    }

    // 返回false:在邊界內(nèi),即未消費(fèi)(默認(rèn))
    return false;
}

ViewGroup的事件分發(fā)

剛剛分析了activity的事件分發(fā),最后會(huì)分發(fā)到ViewGroup 實(shí)現(xiàn)了activity-->ViewGroup

public boolean dispatchTouchEvent(MotionEvent ev) {


        // 分析1:ViewGroup每次事件分發(fā)時(shí),都需調(diào)用onInterceptTouchEvent()詢問(wèn)是否攔截事件
        //我們可以通過(guò)改變disallowIntercept的值來(lái)讓ViewGroup不對(duì)事件進(jìn)行攔截 是否禁用事件攔截的功能(默認(rèn)是false),
        // 可通過(guò)調(diào)用requestDisallowInterceptTouchEvent()修改
        // Check for interception.
        final boolean intercepted;
        if (actionMasked == MotionEvent.ACTION_DOWN
                || mFirstTouchTarget != null) {
            final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
            if (!disallowIntercept) {
                //onInterceptTouchEvent 默認(rèn)返回 false,不攔截
                intercepted = onInterceptTouchEvent(ev);
                ev.setAction(action); // restore action in case it was changed
            } else {
                //不攔截
                intercepted = false;
            }
        } else {
            // There are no touch targets and this action is not an initial down
            // so this view group continues to intercept touches.
            //攔截
            intercepted = true;
        }

        //沒(méi)有取消也沒(méi)有攔截,則分發(fā)給子View
        if (!canceled && !intercepted) {
                //如果 Touch 狀態(tài)為空并且子 View 個(gè)數(shù)不為 0
                if (newTouchTarget == null && childrenCount != 0) {
                    final float x = ev.getX(actionIndex);
                    final float y = 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;
                    //循環(huán)查找
                    for (int i = childrenCount - 1; i >= 0; i--) {
                        //分發(fā)事件,記錄狀態(tài)
                        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();
                            newTouchTarget = addTouchTarget(child, idBitsToAssign);
                            alreadyDispatchedToNewTouchTarget = true;
                            break;
                        }

}

總結(jié)如下


image.png

View的事件分發(fā)機(jī)制

下面我們看源碼分析一下

                    public boolean dispatchTouchEvent(MotionEvent event) {

                        if (onFilterTouchEventForSecurity(event)) {
                            if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
                                result = true;
                            }
                            //noinspection SimplifiableIfStatement
                            ListenerInfo li = mListenerInfo;
                            //判斷是否注冊(cè) OnTouchListener
                            //判斷 OnTouchListener 中 onTouch 是否返回 true
                            //當(dāng) onTouch 返回 true 時(shí),事件被 onTouch 處理了,不會(huì)執(zhí)行 onTouchEvent 方法,說(shuō)明 onTouch 的優(yōu)先級(jí)高于 onTouchEvent
                            if (li != null && li.mOnTouchListener != null
                                    && (mViewFlags & ENABLED_MASK) == ENABLED
                                    && li.mOnTouchListener.onTouch(this, event)) {
                                result = true;
                            }

                            if (!result && onTouchEvent(event)) {
                                result = true;
                            }
                        }

                    }

onTouchEvent

                    public boolean onTouchEvent(MotionEvent event) {
...
                        if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) {
                            switch (action) {
                                case MotionEvent.ACTION_UP:
            ...
                                    //如果不能點(diǎn)擊則直接返回
                                    if (!clickable) {
                                        removeTapCallback();
                                        removeLongPressCallback();
                                        mInContextButtonPress = false;
                                        mHasPerformedLongPress = false;
                                        mIgnoreNextUpEvent = false;
                                        break;
                                    }
                                    if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {
                                        // This is a tap, so remove the longpress check
                                        removeLongPressCallback();

                                        // Only perform take click actions if we were in the pressed state
                                        if (!focusTaken) {
                                            // Use a Runnable and post this rather than calling
                                            // performClick directly. This lets other visual state
                                            // of the view update before click actions start.
                                            if (mPerformClick == null) {
                                                mPerformClick = new PerformClick();
                                            }
                                            if (!post(mPerformClick)) {
                                                performClickInternal();
                                            }
                                        }
                                    }
            ...
                                    break;
                                case MotionEvent.ACTION_DOWN:
            ...
                                    break;
                                case MotionEvent.ACTION_CANCEL:
            ...
                                    break;
                                case MotionEvent.ACTION_MOVE:
            ...
                                    break;
                            }
                        }
...
                    }

View 事件分發(fā)總結(jié)
1.事件分發(fā)首先會(huì)調(diào)用dispatchTouchEvent()
2.事件分發(fā)執(zhí)行的順序?yàn)?onTouch -> onTouchEvent -> onClick,可以理解成 優(yōu)先級(jí) onTouch > onTouchEvent > onClick
3.當(dāng) OnTouchListener 為 true 時(shí),會(huì)消費(fèi)當(dāng)前事件,只會(huì)執(zhí)行 onTouch 方法,不會(huì)傳遞到 onTouchEvent 方法。當(dāng) OnTouchListener 為 false 時(shí),則會(huì)將事件傳遞
onClick 事件觸發(fā)的前提條件是可點(diǎn)擊的,onClick 觸發(fā)在 onTouchEvent 的UP 事件中

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容