觸摸事件之事件分發(fā)

上篇文章中,分析了我之前關(guān)于觸摸事件的一點(diǎn)疑問,感興趣的,可點(diǎn)擊觸摸事件之onTouch和onTouchEvent查看

趁著熱乎勁兒,繼續(xù)再來鞏固下完整的事件分發(fā)流程吧。
先不回憶細(xì)節(jié),單純從最簡單的角度來看,事件分發(fā)無非就3步:事件產(chǎn)生->事件傳遞->事件處理。就跟春晚小品宋丹丹問趙大叔把大象放進(jìn)冰箱分幾步一個(gè)道理。從最原始的角度出發(fā)來看待這個(gè)問題,中間的過程再逐步細(xì)化,這樣大腦中有個(gè)清晰的流程,分析問題也會(huì)順暢的多。

從手指觸摸屏幕的那一刻,觸摸事件便產(chǎn)生了,拋開硬件層面的電容電流感應(yīng),到應(yīng)用層的APP層面,最先肯定是由Activity接收到事件,咱們來瞧瞧Activity里面的處理過程;

 public boolean dispatchTouchEvent(MotionEvent ev) {
        //...
        if (getWindow().superDispatchTouchEvent(ev)) {
            return true;
        }
        return onTouchEvent(ev);
    }

里面有行代碼很關(guān)鍵,“getWindow().superDispatchTouchEvent(ev)”,事件由window分發(fā),如果返回true,后面的onTouchEvent將不執(zhí)行,怎么感覺這句話很熟悉??原來上篇文章剛分析過類似的。繼續(xù)追查這個(gè)window是什么,原來是:

mWindow = new PhoneWindow(this, window, activityConfigCallback);

熟悉安卓界面加載的都知道,phoneWindow跟DecorView密切相關(guān),莫非window將事件傳給了DecorView,繼續(xù)看源碼:

 @Override
public boolean superDispatchTouchEvent(MotionEvent event) {
    return mDecor.superDispatchTouchEvent(event);
}

果然,一切盡在預(yù)料之中啊,這種感覺很爽。DecorView中分發(fā)過程也很簡單,

    public boolean superDispatchTouchEvent(MotionEvent event) {
        return super.dispatchTouchEvent(event);
    }

DecorView是一個(gè)FrameLayout布局,它由上下兩部分組成,上面是actionBar,下面是我們最最親愛的在setContentView()方法中傳進(jìn)xml布局文件,生成的視圖組。上面的事件已經(jīng)分發(fā)至DecorView了,現(xiàn)在我們將樣式設(shè)為為noActionBar,那么DecorView布局中就只有一個(gè)contentView布局。在上面的方法中,由于FrameLayout沒有重寫分發(fā)方法,所以會(huì)接著向上查找分發(fā)方法,最終找到ViewGroup中的dispatchTouchEvent方法,而這個(gè)viewGroup中的第一個(gè)子view就是contentView生成的視圖組。接下來看看viewGroup中的分發(fā)方法。

for (int i = childrenCount - 1; i >= 0; i--) {
//...
        if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
             // Child wants to receive touch within its bounds.
             mLastTouchDownTime = ev.getDownTime();
        }
//...
}

代碼很長,挑關(guān)鍵的看,看到for循環(huán),知道重頭戲來了,我們知道DecorView作為所有Activity根視圖的外層容器,一個(gè)Activity界面就是有一個(gè)個(gè)ViewGroup不斷包含子View構(gòu)成的,在ViewGroup里對所有子view進(jìn)行遍歷,肯定也會(huì)遍歷傳遞處理觸摸事件,我們找child和touchEvent兩個(gè)關(guān)鍵字,找到一個(gè)方法
“dispatchTransformedTouchEvent”,進(jìn)去看一下:

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

期待已久的child.dispatchTouchEvent(event)出現(xiàn)了?。〕霈F(xiàn)了!!截止到目前為止,根據(jù)我們所掌握的信息,可以很肯定的是,\color{#FF0000}{觸摸事件從Activity開始,沿著最頂層DecorView,一路往下分發(fā)下去。}

根據(jù)child類型的不同,dispatchTouchEvent實(shí)現(xiàn)肯定也不同,view類型的不細(xì)說了,直接貼代碼吧:

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

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

ViewGroup相比于View肯定要復(fù)雜些,其內(nèi)部多了要處理子view的情況,在這里貼上一位網(wǎng)友制作的圖,個(gè)人認(rèn)為很不錯(cuò):


ViewGroup分發(fā)流程

途中很清晰的描繪了viewgroup是如何把事件傳遞給子view的,onInterceptTouchEvent是viewGroup中獨(dú)有的方法,它的返回值直接決定了是否會(huì)將事件傳遞給子view處理。如果子 View 不處理,這個(gè)“鍋”就得 ViewGroup 自己擔(dān)著。所以事件會(huì)傳遞到 super.dispatchTouchEvent()。ViewGroup 類繼承自 View 類,也就是進(jìn)入了前文說的 View 事件分發(fā)流程,就相當(dāng)于詢問當(dāng)前 ViewGroup 自己是否處理這個(gè)事件,細(xì)節(jié)這里就不重復(fù)了。如果當(dāng)前 ViewGroup自己處理了,對于上級 ViewGroup 而言,還是找到了 target,如果當(dāng)前 ViewGroup 不處理,這個(gè)“鍋”繼續(xù)拋給上級 ViewGroup。

當(dāng)最外層的子view接收到分發(fā)事件時(shí),會(huì)進(jìn)入它自身的dispatchTouchEvent方法,當(dāng)它不攔截時(shí),事件會(huì)繼續(xù)進(jìn)入到onTouchEvent方法中處理,然后再一級一級向上傳遞返回值。

以上就是觸摸事件分發(fā)流程的簡要分析,源碼也一直在更新。掌握了主要的流程,任他怎么改,也能做到心中有數(shù)。

參考:
通過流程圖來分析Android事件分發(fā)

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

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