我看過國內講解Android事件分發的大多數文章,但遺憾的是都沒有這篇講的好,緣由有二:它闡明瞭具體的事件分發機制的設計意圖,讓人既知其然,又知其因此然;它沒有貼源碼,嚇唬本寶寶。因此我決定將它翻譯出來,造福廣大Android開發者。原文請點擊這裏android
有時,你必需要本身處理觸摸事件(touch events)而不能依賴於有可用的onSomethingListener。我就遇到過這樣的時候,當時我很想有一篇文章能簡單地解釋觸摸事件是怎樣在視圖層次(view hierarchy)中傳播的,從而能夠將之做爲進一步深刻學習的起點。這篇博客是個人一次嘗試,它看起來有點長,但這是由於我是按照觸摸事件的傳播過程一步一步來寫的。post
咱們只考慮最重要的四個觸摸事件,即: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之間可能的交叉疊加。spa
假設用戶首先觸摸到的屏幕上的點是C上的某個點,該點被標記爲觸摸點(touch point),DOWN事件就在該點產生。而後用戶移動手指並最後離開屏幕,此過程當中手指是否離開C的區域可有可無,關鍵是手勢(gesture)是從哪裏開始的。翻譯
假設上面的A,B,C都沒有覆寫默認的事件傳播行爲,那麼下面就是事件傳播的過程:設計
因爲沒有view關心這個手勢(gesture),它們將再也不會從「手勢剩餘部分」中接收任何事件。3d
如今,讓咱們假設C其實是關心這個手勢(gesture)的,緣由多是C被設置成可點擊的(clickable)或者你覆寫了C的onTouchEvent方法。cdn
我的理解:從這裏能夠看出,各個View的onTouchEvent方法對DOWN事件的處理,表明了該View對以此DOWN開始的整個手勢(gesture)的處理意願,返回true表明願意處理該gesture,返回false表明不肯意處理該gesture。blog
如今咱們將討論一個新的方法:onInterceptTouchEvent,它只存在於ViewGroup中,普通的View中沒有這個方法。在任何一個view的onTouchEvent被調用以前,它的父輩們(ancestors)將先得到攔截這個事件的一次機會,換句話說,它們能夠竊取該事件。在剛纔的「處理事件」部分中,咱們遺漏了這一過程,如今,讓咱們把它加上:
這裏有兩點須要注意:
我的理解:因而可知,DOWN事件的處理實際上經歷了一下一上兩個過程,下是指A->B的onInterceptTouchEvent,上是指C->B->A的onTouchEvent,固然,任意一步的方法中返回true,都能阻止它繼續傳播。
如今,讓咱們更進一步,假設B沒有攔截DOWN事件,但它攔截了接下來的MOVE事件。緣由多是B是一個scrolling view。當用戶僅僅在它的區域內點擊(tap)時,被點擊到的元素應當能處理該點擊事件。可是當用戶手指移動了必定的距離後,就不能再視該手勢(gesture)爲點擊了——很明顯,用戶是想scroll。這就是爲何B要接管該手勢(gesture)。
下面是事件被處理的順序:
下面的一些小事情可能會令你感到吃驚:
今後開始,你能夠更進一步。好比對mouthful-method (實在不知道該怎麼翻譯啦!)requestDisallowInterceptTouchEvent,C能夠用該方法阻止B竊取事件。若是你想更加瘋狂一點,你能夠在你本身的ViewGroup中直接覆寫dispatchTouchEvent方法,並對傳遞進來的事件作任何你想作的處理。但這樣的話你可能會破壞一些約定,因此應當當心。