可能是講解Android事件分發最好的文章

姓名 連嘉瑋 學號 16040120086

轉自:http://www.lxweimin.com/p/2be492c1df96?utm_campaign=hugo&utm_medium=reader_share&utm_content=note&utm_source=qq

有刪節

【嵌牛導讀】:Android開發

【嵌牛鼻子】:關于安卓開發的事件

【嵌牛提問】:關于Android開發都有什么事件?

【嵌牛正文】:

? 我幾乎看過國內講解Android事件分發的所有文章,但遺憾的是都沒有這篇講的好,原因有二:它闡明了具體的事件分發機制的設計意圖,讓人既知其然,又知其所以然;它沒有貼源碼,嚇唬本寶寶。所以我決定將它翻譯出來,造福廣大Android開發者。原文請點擊這里

有時,你必須要自己處理觸摸事件(touch events)而不能依賴于有可用的onSomethingListener。我就遇到過這樣的時候,當時我很想有一篇文章能簡單地解釋觸摸事件是怎樣在視圖層次(view hierarchy)中傳播的,從而可以將之作為進一步深入學習的起點。這篇博客是我的一次嘗試,它看起來有點長,但這是因為我是按照觸摸事件的傳播過程一步一步來寫的。

一些假設

我們只考慮最重要的四個觸摸事件,即:DOWN,MOVE,UP和CANCEL。一個手勢(gesture)是一個事件列,以一個DOWN事件開始(當用戶觸摸屏幕時產生),后跟0個或多個MOVE事件(當用戶四處移動手指時產生),最后跟一個單獨的UP或CANCEL事件(當用戶手指離開屏幕或者系統告訴你手勢(gesture)由于其他原因結束時產生)。當我們說到“手勢剩余部分”時指的是手勢后續的MOVE事件和最后的UP或CANCEL事件。

在這里我也不考慮多點觸摸手勢(我們只假設用一個手指)并且忽略多個MOVE事件可以被歸為一組這一實際情況。最后,我們假設文中的view都沒有注冊onTouchListener。

我們將要討論的視圖層次是這樣的:最外層是一個ViewGroup A,包含一個或多個子view(children),其中一個子view是ViewGroup B,ViewGroupB中又包含一個或多個子view,其中一個子view是 View C,C不是一個ViewGroup。這里我們忽略同層級view之間可能的交叉疊加。

android-touch.png

假設用戶首先觸摸到的屏幕上的點是C上的某個點,該點被標記為觸摸點(touch point),DOWN事件就在該點產生。然后用戶移動手指并最后離開屏幕,此過程中手指是否離開C的區域無關緊要,關鍵是手勢(gesture)是從哪里開始的。

默認情況

假設上面的A,B,C都沒有覆寫默認的事件傳播行為,那么下面就是事件傳播的過程:

DOWN事件被傳到C的onTouchEvent方法中,該方法返回false,表示“我不關心這個手勢(gesture)”。

因此,DOWN事件被傳到B的onTouchEvent方法中,該方法同樣返回false,表示B也不關心這個手勢。

同樣,因為B不關心這個手勢,DOWN事件被傳到A的onTouchEvent方法中,該方法也返回false。

由于沒有view關心這個手勢(gesture),它們將不再會從“手勢剩余部分”中接收任何事件。

處理事件

現在,讓我們假設C實際上是關心這個手勢(gesture)的,原因可能是C被設置成可點擊的(clickable)或者你覆寫了C的onTouchEvent方法。

DOWN事件被傳遞給C的onTouchEvent方法,該方法可以做任何它想做的事情,最后返回true。

因為C說它正在處理這個手勢(gesture),則DOWN事件將不再被傳遞給B和A的onTouchEvent方法。

因為C說它正在處理這個手勢(gesture),所以“手勢剩余部分”的事件也將傳遞給C的onTouchEvent方法,此時該方法返回true或false都無關緊要了,但是為保持一致最好還是返回true。

個人理解:從這里可以看出,各個View的onTouchEvent方法對DOWN事件的處理,代表了該View對以此DOWN開始的整個手勢(gesture)的處理意愿,返回true代表愿意處理該gesture,返回false代表不愿意處理該gesture。

onInterceptTouchEvent

現在我們將討論一個新的方法:onInterceptTouchEvent,它只存在于ViewGroup中,普通的View中沒有這個方法。在任何一個view的onTouchEvent被調用之前,它的父輩們(ancestors)將先獲得攔截這個事件的一次機會,換句話說,它們可以竊取該事件。在剛才的“處理事件”部分中,我們遺漏了這一過程,現在,讓我們把它加上:

DOWN事件被傳給A的onInterceptTouchEvent,該方法返回false,表示它不想攔截。

DOWN又被傳遞給B的onInterceptTouchEvent,它也不想攔截,因此該方法也返回false。

現在,DOWN事件被傳遞到C的onTouchEvent方法,該方法返回true,因為它想處理以該事件為首的手勢(gesture)。

現在,該手勢的下一個事件MOVE到來了。這個MOVE事件再一次被傳遞給A的onInterceptTouchEvent方法,該方法再一次返回false,B也同樣如此。

然后,MOVE事件被傳遞給C的onTouchEvent,就像在前一部分中一樣。

“手勢剩余部分”中其他事件的處理過程和上面一樣,假如A和B的onInterceptTouchEvent方法繼續返回false的話。

這里有兩點需要注意:

雖然ViewGroup A和B的onInterceptTouchEvent方法對DOWN事件返回了false,后續的事件依然會傳遞給它們的onInterceptTouchEvent方法,這一點與onTouchEvent的行為是不一樣的。

假如DOWN事件傳給C的onTouchEvent方法時,它返回了false,DOWN事件會繼續向上傳遞給B和A的onTouchEvent,即使它們在onInterceptTouchEvent方法中說它們不想攔截這個DOWN事件,但沒辦法,沒有子View愿意處理該事件。

個人理解:感謝@編程世界的孩子 的提醒,由此可見,DOWN事件的處理實際上經歷了一下一上兩個過程,下是指A->B的onInterceptTouchEvent,上是指C->B->A的onTouchEvent,當然,任意一步的方法中返回true,都能阻止它繼續傳播。

攔截事件

現在,讓我們更進一步,假設B沒有攔截DOWN事件,但它攔截了接下來的MOVE事件。原因可能是B是一個scrolling view。當用戶僅僅在它的區域內點擊(tap)時,被點擊到的元素應當能處理該點擊事件。但是當用戶手指移動了一定的距離后,就不能再視該手勢(gesture)為點擊了——很明顯,用戶是想scroll。這就是為什么B要接管該手勢(gesture)。

下面是事件被處理的順序:

DOWN事件被依次傳到A和B的onInterceptTouchEvent方法中,它們都返回的false,因為它們目前還不想攔截。

DOWN事件傳遞到C的onTouchEvent方法,返回了true。

在后續到來MOVE事件時,A的onInterceptTouchEvent方法仍然返回false。

B的onInterceptTouchEvent方法收到了該MOVE事件,此時B注意到用戶手指移動距離已經超過了一定的threshold(或者稱為slop)。因此,B的onInterceptTouchEvent方法決定返回true,從而接管該手勢(gesture)后續的處理。

然后,這個MOVE事件將會被系統變成一個CANCEL事件,這個CANCEL事件將會傳遞給C的onTouchEvent方法。

現在,又來了一個MOVE事件,它被傳遞給A的onInterceptTouchEvent方法,A還是不關心該事件,因此onInterceptTouchEvent方法繼續返回false。

此時,該MOVE事件將不會再傳遞給B的onInterceptTouchEvent方法,該方法一旦返回一次true,就再也不會被調用了。事實上,該MOVE以及“手勢剩余部分”都將傳遞給B的onTouchEvent方法(除非A決定攔截“手勢剩余部分”)。

C再也不會收到該手勢(gesture)產生的任何事件了。

下面的一些小事情可能會令你感到吃驚:

如果一個ViewGroup攔截了最初的DOWN事件,該事件仍然會傳遞到該ViewGroup的onTouchEvent方法中。

另一方面,如果ViewGroup攔截了一個半路的事件(比如,MOVE),這個事件將會被系統變成一個CANCEL事件,并傳遞給之前處理該手勢(gesture)的子View,而且不會再傳遞(無論是被攔截的MOVE還是系統生成的CANCEL)給ViewGroup的onTouchEvent方法。只有再到來的事件才會傳遞到ViewGroup的onTouchEvent方法中。

從此開始,你可以更進一步。比如對mouthful-method (實在不知道該怎么翻譯啦!)requestDisallowInterceptTouchEvent,C可以用該方法阻止B竊取事件。如果你想更加瘋狂一點,你可以在你自己的ViewGroup中直接覆寫dispatchTouchEvent方法,并對傳遞進來的事件做任何你想做的處理。但這樣的話你可能會破壞一些約定,所以應當小心。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,797評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,179評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,628評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,642評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,444評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,948評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,040評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,185評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,717評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,602評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,794評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,316評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,045評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,418評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,671評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,414評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,750評論 2 370

推薦閱讀更多精彩內容