Android事件分發(fā)機(jī)制的詳解
Android事件分發(fā)機(jī)制
我們只考慮最重要的四個(gè)觸摸事件,即:DOWN,MOVE,UP和CANCEL。一個(gè)手勢(shì)(gesture)是一個(gè)事件列,以一個(gè)DOWN事件開(kāi)始(當(dāng)用戶(hù)觸摸屏幕時(shí)產(chǎn)生),后跟0個(gè)或多個(gè)MOVE事件(當(dāng)用戶(hù)四處移動(dòng)手指時(shí)產(chǎn)生),最后跟一個(gè)單獨(dú)的UP或CANCEL事件(當(dāng)用戶(hù)手指離開(kāi)屏幕或者系統(tǒng)告訴你手勢(shì)(gesture)由于其他原因結(jié)束時(shí)產(chǎn)生)。當(dāng)我們說(shuō)到“手勢(shì)剩余部分”時(shí)指的是手勢(shì)后續(xù)的MOVE事件和最后的UP或CANCEL事件。
在這里我也不考慮多點(diǎn)觸摸手勢(shì)(我們只假設(shè)用一個(gè)手指)并且忽略多個(gè)MOVE事件可以被歸為一組這一實(shí)際情況。最后,我們假設(shè)文中的view都沒(méi)有注冊(cè)onTouchListener。
我們將要討論的視圖層次是這樣的:最外層是一個(gè)ViewGroup A,包含一個(gè)或多個(gè)子view(children),其中一個(gè)子view是ViewGroup B,ViewGroupB中又包含一個(gè)或多個(gè)子view,其中一個(gè)子view是 View C,C不是一個(gè)ViewGroup。這里我們忽略同層級(jí)view之間可能的交叉疊加。
假設(shè)用戶(hù)首先觸摸到的屏幕上的點(diǎn)是C上的某個(gè)點(diǎn),該點(diǎn)被標(biāo)記為觸摸點(diǎn)(touch point),DOWN事件就在該點(diǎn)產(chǎn)生。然后用戶(hù)移動(dòng)手指并最后離開(kāi)屏幕,此過(guò)程中手指是否離開(kāi)C的區(qū)域無(wú)關(guān)緊要,關(guān)鍵是手勢(shì)(gesture)是從哪里開(kāi)始的。
默認(rèn)情況
假設(shè)上面的A,B,C都沒(méi)有覆寫(xiě)默認(rèn)的事件傳播行為,那么下面就是事件傳播的過(guò)程:
- DOWN事件被傳到C的onTouchEvent方法中,該方法返回false,表示“我不關(guān)心這個(gè)手勢(shì)(gesture)”。
- 因此,DOWN事件被傳到B的onTouchEvent方法中,該方法同樣返回false,表示B也不關(guān)心這個(gè)手勢(shì)。
- 同樣,因?yàn)锽不關(guān)心這個(gè)手勢(shì),DOWN事件被傳到A的onTouchEvent方法中,該方法也返回false。
由于沒(méi)有view關(guān)心這個(gè)手勢(shì)(gesture),它們將不再會(huì)從“手勢(shì)剩余部分”中接收任何事件。
處理事件
現(xiàn)在,讓我們假設(shè)C實(shí)際上是關(guān)心這個(gè)手勢(shì)(gesture)的,原因可能是C被設(shè)置成可點(diǎn)擊的(clickable)或者你覆寫(xiě)了C的onTouchEvent方法。
- DOWN事件被傳遞給C的onTouchEvent方法,該方法可以做任何它想做的事情,最后返回true。
- 因?yàn)镃說(shuō)它正在處理這個(gè)手勢(shì)(gesture),則DOWN事件將不再被傳遞給B和A的onTouchEvent方法。
- 因?yàn)镃說(shuō)它正在處理這個(gè)手勢(shì)(gesture),所以“手勢(shì)剩余部分”的事件也將傳遞給C的onTouchEvent方法,此時(shí)該方法返回true或false都無(wú)關(guān)緊要了,但是為保持一致最好還是返回true。
個(gè)人理解:從這里可以看出,各個(gè)View的onTouchEvent方法對(duì)DOWN事件的處理,代表了該View對(duì)以此DOWN開(kāi)始的整個(gè)手勢(shì)(gesture)的處理意愿,返回true代表愿意處理該gesture,返回false代表不愿意處理該gesture。
onInterceptTouchEvent
現(xiàn)在我們將討論一個(gè)新的方法:onInterceptTouchEvent,它只存在于ViewGroup中,普通的View中沒(méi)有這個(gè)方法。在任何一個(gè)view的onTouchEvent被調(diào)用之前,它的父輩們(ancestors)將先獲得攔截這個(gè)事件的一次機(jī)會(huì),換句話(huà)說(shuō),它們可以竊取該事件。在剛才的“處理事件”部分中,我們遺漏了這一過(guò)程,現(xiàn)在,讓我們把它加上:
- DOWN事件被傳給A的onInterceptTouchEvent,該方法返回false,表示它不想攔截。
- DOWN又被傳遞給B的onInterceptTouchEvent,它也不想攔截,因此該方法也返回false。
- 現(xiàn)在,DOWN事件被傳遞到C的onTouchEvent方法,該方法返回true,因?yàn)樗胩幚硪栽撌录槭椎氖謩?shì)(gesture)。
- 現(xiàn)在,該手勢(shì)的下一個(gè)事件MOVE到來(lái)了。這個(gè)MOVE事件再一次被傳遞給A的onInterceptTouchEvent方法,該方法再一次返回false,B也同樣如此。
- 然后,MOVE事件被傳遞給C的onTouchEvent,就像在前一部分中一樣。
- “手勢(shì)剩余部分”中其他事件的處理過(guò)程和上面一樣,假如A和B的onInterceptTouchEvent方法繼續(xù)返回false的話(huà)。 這里有兩點(diǎn)需要注意:
- 雖然ViewGroup A和B的onInterceptTouchEvent方法對(duì)DOWN事件返回了false,后續(xù)的事件依然會(huì)傳遞給它們的onInterceptTouchEvent方法,這一點(diǎn)與onTouchEvent的行為是不一樣的。
- 假如DOWN事件傳給C的onTouchEvent方法時(shí),它返回了false,DOWN事件會(huì)繼續(xù)向上傳遞給B和A的onTouchEvent,即使它們?cè)趏nInterceptTouchEvent方法中說(shuō)它們不想攔截這個(gè)DOWN事件,但沒(méi)辦法,沒(méi)有子View愿意處理該事件。
個(gè)人理解:感謝@編程世界的孩子 的提醒,由此可見(jiàn),DOWN事件的處理實(shí)際上經(jīng)歷了一下一上兩個(gè)過(guò)程,下是指A->B的onInterceptTouchEvent,上是指C->B->A的onTouchEvent,當(dāng)然,任意一步的方法中返回true,都能阻止它繼續(xù)傳播。
攔截事件
現(xiàn)在,讓我們更進(jìn)一步,假設(shè)B沒(méi)有攔截DOWN事件,但它攔截了接下來(lái)的MOVE事件。原因可能是B是一個(gè)scrolling view。當(dāng)用戶(hù)僅僅在它的區(qū)域內(nèi)點(diǎn)擊(tap)時(shí),被點(diǎn)擊到的元素應(yīng)當(dāng)能處理該點(diǎn)擊事件。但是當(dāng)用戶(hù)手指移動(dòng)了一定的距離后,就不能再視該手勢(shì)(gesture)為點(diǎn)擊了——很明顯,用戶(hù)是想scroll。這就是為什么B要接管該手勢(shì)(gesture)。
下面是事件被處理的順序:
- DOWN事件被依次傳到A和B的onInterceptTouchEvent方法中,它們都返回的false,因?yàn)樗鼈兡壳斑€不想攔截。
- DOWN事件傳遞到C的onTouchEvent方法,返回了true。
- 在后續(xù)到來(lái)MOVE事件時(shí),A的onInterceptTouchEvent方法仍然返回false。
- B的onInterceptTouchEvent方法收到了該MOVE事件,此時(shí)B注意到用戶(hù)手指移動(dòng)距離已經(jīng)超過(guò)了一定的threshold(或者稱(chēng)為slop)。因此,B的onInterceptTouchEvent方法決定返回true,從而接管該手勢(shì)(gesture)后續(xù)的處理。
- 然后,這個(gè)MOVE事件將會(huì)被系統(tǒng)變成一個(gè)CANCEL事件,這個(gè)CANCEL事件將會(huì)傳遞給C的onTouchEvent方法。
- 現(xiàn)在,又來(lái)了一個(gè)MOVE事件,它被傳遞給A的onInterceptTouchEvent方法,A還是不關(guān)心該事件,因此onInterceptTouchEvent方法繼續(xù)返回false。
- 此時(shí),該MOVE事件將不會(huì)再傳遞給B的onInterceptTouchEvent方法,該方法一旦返回一次true,就再也不會(huì)被調(diào)用了。事實(shí)上,該MOVE以及“手勢(shì)剩余部分”都將傳遞給B的onTouchEvent方法(除非A決定攔截“手勢(shì)剩余部分”)。
- C再也不會(huì)收到該手勢(shì)(gesture)產(chǎn)生的任何事件了。
下面的一些小事情可能會(huì)令你感到吃驚:
- 如果一個(gè)ViewGroup攔截了最初的DOWN事件,該事件仍然會(huì)傳遞到該ViewGroup的onTouchEvent方法中。
- 另一方面,如果ViewGroup攔截了一個(gè)半路的事件(比如,MOVE),這個(gè)事件將會(huì)被系統(tǒng)變成一個(gè)CANCEL事件,并傳遞給之前處理該手勢(shì)(gesture)的子View,而且不會(huì)再傳遞(無(wú)論是被攔截的MOVE還是系統(tǒng)生成的CANCEL)給ViewGroup的onTouchEvent方法。只有再到來(lái)的事件才會(huì)傳遞到ViewGroup的onTouchEvent方法中。
從此開(kāi)始,你可以更進(jìn)一步。比如對(duì)mouthful-method (實(shí)在不知道該怎么翻譯啦!)requestDisallowInterceptTouchEvent,C可以用該方法阻止B竊取事件。如果你想更加瘋狂一點(diǎn),你可以在你自己的ViewGroup中直接覆寫(xiě)dispatchTouchEvent方法,并對(duì)傳遞進(jìn)來(lái)的事件做任何你想做的處理。但這樣的話(huà)你可能會(huì)破壞一些約定,所以應(yīng)當(dāng)小心。
如有疑問(wèn)請(qǐng)留言或者到本站社區(qū)交流討論,感謝閱讀,希望能幫助到大家,謝謝大家對(duì)本站的支持!
相關(guān)文章
Android嵌套滾動(dòng)與協(xié)調(diào)滾動(dòng)的實(shí)現(xiàn)方式匯總
如何實(shí)現(xiàn)這種協(xié)調(diào)滾動(dòng)的布局呢,我們使用CoordinatorLayout+AppBarLayout或者CoordinatorLayout+Behavior實(shí)現(xiàn),另一種方案是MotionLayout,我們看看都是怎么實(shí)現(xiàn)的吧2022-06-06Android中給fragment寫(xiě)入?yún)?shù)的輕量開(kāi)發(fā)包FragmentArgs簡(jiǎn)介
這篇文章主要介紹了Android中給fragment寫(xiě)入?yún)?shù)的輕量開(kāi)發(fā)包FragmentArgs簡(jiǎn)介,需要的朋友可以參考下2014-10-10Android開(kāi)發(fā)基礎(chǔ)簡(jiǎn)化Toast調(diào)用方法詳解
這篇文章主要為大家介紹了Android開(kāi)發(fā)基礎(chǔ)簡(jiǎn)化Toast調(diào)用方法的相關(guān)資料,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-02-02Android開(kāi)發(fā)中計(jì)算器的sin、cos及tan值計(jì)算問(wèn)題分析
這篇文章主要介紹了Android開(kāi)發(fā)中計(jì)算器的sin、cos及tan值計(jì)算問(wèn)題,結(jié)合實(shí)例形式分析了Android三角函數(shù)運(yùn)算中的弧度與角度計(jì)算問(wèn)題與相關(guān)解決方法,需要的朋友可以參考下2017-11-11Android仿微信左右滑動(dòng)點(diǎn)擊切換頁(yè)面和圖標(biāo)
這篇文章主要為大家詳細(xì)介紹了Android仿微信左右滑動(dòng)點(diǎn)擊切換頁(yè)面和圖標(biāo),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-05-05Android實(shí)現(xiàn)布局動(dòng)畫(huà)和共享動(dòng)畫(huà)的結(jié)合效果
今天給大家?guī)?lái)能夠提升用戶(hù)體驗(yàn)感的交互動(dòng)畫(huà),使用起來(lái)非常簡(jiǎn)單,體驗(yàn)效果非常贊,其中僅使用到布局動(dòng)畫(huà)和共享動(dòng)畫(huà),文章通過(guò)代碼示例介紹的非常詳細(xì),感興趣的同學(xué)可以自己動(dòng)手試一試2023-09-09Android 動(dòng)畫(huà)實(shí)現(xiàn)幾種方案
這篇文章主要介紹了Android 動(dòng)畫(huà)實(shí)現(xiàn)幾種方案的相關(guān)資料,需要的朋友可以參考下2017-06-06