天天看點

Android 事件分發onInterceptTouchEvent和onTouchEvent整理

文/milter(簡書作者)

原文連結:http://www.jianshu.com/p/2be492c1df96

著作權歸作者所有,轉載請聯系作者獲得授權,并标注“簡書作者”。

我們隻考慮最重要的四個觸摸事件,即: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 事件分發onInterceptTouchEvent和onTouchEvent整理

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

  • 預設情況下
    預設情況下事件傳遞順序是:
    ViewGroupA-onInterceptTouchEvent(false)---->  ViewGroupB-onInterceptTouchEvent(false)----> ViewC-onTouchEvent(false)---->  ViewGroupB-onTouchEvent(false)---->ViewGroupA-onTouchEvent(false)
               
  • 子View做出處理情況下
    如果ViewC對DOWN事件做出了處理,ViewGroupA和ViewGroupB都沒有攔截事件,那麼事件傳遞順序為:--(DOWN事件到來)-->ViewGroupA-onInterceptTouchEvent(false)---->  ViewGroupB-onInterceptTouchEvent(false)----> ViewC-onTouchEvent(true)--(下一個Move事件到來)-->  ViewGroupA-onInterceptTouchEvent(false)---->  ViewGroupB-onInterceptTouchEvent(false)---->ViewC-onTouchEvent(true或者false都沒有影響,為了保持一緻,最好傳回true)
               
  • 父View攔截Move事件情況下
    假設ViewGroupB沒有攔截DOWN事件,但它攔截了接下來的MOVE事件。原因可能是B是一個scrolling view。當使用者僅僅在它的區域内點選(tap)時,被點選到的元素應當能處理該點選事件。但是當使用者手指移動了一定的距離後,就不能再視該手勢(gesture)為點選了——很明顯,使用者是想scroll。這就是為什麼B要接管該手勢(gesture)。
    
    下面是事件被處理的順序:
    1. DOWN事件被依次傳到A和B的onInterceptTouchEvent方法中,它們都傳回的false,因為它們目前還不想攔截。
    2. DOWN事件傳遞到C的onTouchEvent方法,傳回了true。
    3. 在後續到來MOVE事件時,A的onInterceptTouchEvent方法仍然傳回false。B的onInterceptTouchEvent方法收到了該MOVE事件,此時B注意到使用者手指移動距離已經超過了一定的threshold(或者稱為slop)。是以,B的onInterceptTouchEvent方法決定傳回true,進而接管該手勢(gesture)後續的處理。
    4. 然後,這個MOVE事件将會被系統變成一個CANCEL事件,這個CANCEL事件将會傳遞給C的onTouchEvent方法。
    5. 現在,又來了一個MOVE事件,它被傳遞給A的onInterceptTouchEvent方法,A還是不關心該事件,是以onInterceptTouchEvent方法繼續傳回false。
    6. 此時,該MOVE事件将不會再傳遞給B的onInterceptTouchEvent方法,該方法一旦傳回一次true,就再也不會被調用了。事實上,該MOVE以及“手勢剩餘部分”都将傳遞給B的onTouchEvent方法(除非A決定攔截“手勢剩餘部分”)。
    7. C再也不會收到該手勢(gesture)産生的任何事件了。
    
    下面的一些小事情可能會令你感到吃驚:
    1. 如果一個ViewGroup攔截了最初的DOWN事件,該事件仍然會傳遞到該ViewGroup的onTouchEvent方法中。
    2. 另一方面,如果ViewGroup攔截了一個半路的事件(比如,MOVE),這個事件将會被系統變成一個CANCEL事件,并傳遞給之前處理該手勢(gesture)的子View,而且不會再傳遞(無論是被攔截的MOVE還是系統生成的CANCEL)給ViewGroup的onTouchEvent方法。隻有再到來的事件才會傳遞到ViewGroup的onTouchEvent方法中。