Android-ViewGroup的事件分發(fā)

偽代碼

boolean dispatchTouchEvent(Event event){
    boolean result = false;

    //第一步重置
    if(DOWN){
        //DOWN事件中重置。如清空FirstTarget,將禁止攔截標(biāo)志位重置為0
    }

    //第二步判斷是否要攔截
    if(DOWN || firstTarget != null){//firseTarget有值,代表事件曾經(jīng)分發(fā)到了子View
        if(禁止攔截標(biāo)志位為0){ //禁止攔截標(biāo)志位可以由子View設(shè)置為1
            intercepted = onInterceptTouchEvent();//詢(xún)問(wèn)自己是否要攔截
        }else {
            intercepted = false;
        }
    }else {
        intercepted = true;
    }

    //第三步 DOWN事件中的分發(fā)判斷
    if(!intercepted){
        if(DOWN){
            for(int i = childCount-1; i>=0; i--){ //倒著循環(huán),所以訪(fǎng)問(wèn)的是最后添加的View,即最上層顯示的View
                View v = childs[i];
                if(v在觸摸范圍內(nèi)){
                    if(v.dispatchTouchEvent(event)){
                        addTarget(v);//將該v加入firstTarget鏈表中,之所以是個(gè)鏈表因?yàn)橛卸嘀盖闆r,單指不考慮
                        alreadyDispatchedToNewTouchTarget = true;//用于第四步避免重復(fù)分發(fā)
                        break;
                    }
                }
            }
        }
    }

    //第四步 之后事件的分發(fā)判斷
    if(firstTarget == null){//當(dāng)無(wú)子View消耗事件
        result = super.dispatchTouchEvent(event);//把自己當(dāng)成一個(gè)View,事件交給自己判斷
    }else{
        target = firstTarget;
        while(target != null){//對(duì)于單指情況,只循環(huán)一次
            if(alreadyDispatchedToNewTouchTarget){
                result = true;
            }else {
                v = target.view;
                boolean cancelChild = intercepted || v是否需要cancel;
                if(cancelChild){
                    event.setAction(CANCEL);
                }
                result = v.dispatchTouchEvent(event);

                if(cancelChild){
                    從firstTarget鏈表中移除該v所在的target;
                }
                target = target.next;
            }
        }
    }

    return result;
}

}
···

代碼細(xì)節(jié)
1、mFirstTarget的用處:用于"非DOWN事件"的直接分發(fā)。在DOWN事件中會(huì)通過(guò)循環(huán)來(lái)詢(xún)問(wèn)子View是否消耗事件,如果消耗,mFirstTarget就會(huì)有值,后續(xù)時(shí)間就會(huì)通過(guò)第四步直接對(duì)其進(jìn)行分發(fā)。如果該值為空,說(shuō)明無(wú)子View會(huì)處理事件,則第四步就會(huì)交給自己處理。
2、alreadyDispatchedToNewTouchTarget變量,是為了第三步已經(jīng)分發(fā)了,而在第四步重復(fù)分發(fā)的情況,該變量在非DOWN事件就失去作用了。

結(jié)論
1、先判斷子View是否消耗事件,如果子View不消耗才交由自己消耗。
在DOWN事件中會(huì)通過(guò)循環(huán)來(lái)訪(fǎng)問(wèn)子View,如果消耗,則mFirstTarget有值,\color{#FF0000}{DOWN之后}的事件通過(guò)mFirstTarget對(duì)目標(biāo)子View直接進(jìn)行分發(fā);如果子View都不消耗,則mFirstTarget無(wú)值,\color{#FF0000}{DOWN及之后}事件交由自己判斷。

2、決定攔截后,后續(xù)事件交由自己處理
如果DOWN事件中攔截,則mFirstTarget無(wú)值,所以直接交由自己處理
如果非DOWN事件中攔截,則mFirstTarget有值,此時(shí),在第四步中當(dāng)前事件會(huì)清空mFirstTarget中的Target,并對(duì)該Target中的子View發(fā)送CANCEL事件,而后續(xù)事件mFirstTarget等同于無(wú)值處理(即交由自己處理)。并且之后在第二步也不會(huì)再詢(xún)問(wèn)是否要阻止攔截,直接認(rèn)為攔截事件。

3、子View顯示在上層的會(huì)先判斷觸摸事件
因?yàn)榈谌降难h(huán)時(shí)根據(jù)子View的添加順序逆序循環(huán),而最后添加的子View都是顯示在最上層,所以如果最上層子View消耗了事件,被遮擋的子View就無(wú)法收到事件了

4、子View無(wú)法在DOWN事件中阻止其父View攔截事件(如果其父View想要攔截的話(huà))
因?yàn)樽覸iew如果想阻止攔截,是通過(guò)設(shè)置FLAG的方式,然后在第二步會(huì)進(jìn)行判斷。但是第一步DOWN事件會(huì)重置狀態(tài)。

最后編輯于
?著作權(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)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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