ViewGroup touch事件分發機制

參考 http://www.cnblogs.com/linjzong/p/4191891.html



當一個Touch事件(觸摸事件爲例)到達根節點,即Acitivty的ViewGroup時,它會依次下發,


下發的過程是調用子View(ViewGroup)的dispatchTouchEvent方法實現的。


簡單來說,就是ViewGroup遍歷它包含着的子View,調用每個View的dispatchTouchEvent方法,


而當子View爲ViewGroup時,又會通過調用ViwGroup的dispatchTouchEvent方法繼續調用其內部的View的dispatchTouchEvent方法。


上述例子中的消息下發順序是這樣的:①-②-⑤-⑥-⑦-③-④。dispatchTouchEvent方法只負責事件的分發,


它擁有boolean類型的返回值,當返回爲true時,順序下發會中斷。在上述例子中如果⑤的dispatchTouchEvent返回結果爲true,


那麼 ⑥-⑦-③-④將都接收不到本次Touch事件。


ViewGroup的dispatchTouchEvent是真正在執行“分發”工作,

而View的dispatchTouchEvent方法,並不執行分發工作,或者說它分發的對象就是自己,決定是否把touch事件交給自己處理

Down方式通過dispatchTouchEvent分發,分發的目的是爲了找到真正需要處理完整Touch請求的View。

當某個View或者ViewGroup的onTouchEvent事件返回true時,便表示它是真正要處理這次請求的View,之後的Aciton_UP和Action_MOVE將由它處理


大致流程:

ViewGroup dispatchTouchEvent(MotionEvent event) -> ViewGroup onInterceptTouchEvent(MotionEvent ev) -> View.dispatchTouchEvent

public boolean dispatchTouchEvent(MotionEvent event)方法內部處理過程:

(1) 如果沒有被其他窗口遮蓋,則繼續下面的邏輯


(2) 如果是按下ACTION_DOWN,則 清理之前觸摸操作的所有痕跡,即派發取消操作,重置touch狀態


(3) 如果是按下ACTION_DOWN 或 mFirstTouchTarget 不爲空,則 判斷 是否允許攔截,允許攔截,則調用 onInterceptTouchEvent(ev)獲取是否被攔截標誌
    ViewGroup onInterceptTouchEvent(MotionEvent event)默認是返回false 不攔截,
    如果當前ViewGroup需要攔截傳遞給其子視圖的事件,需要return true


(4) 如果沒有被取消同時沒有被攔截

   判斷當前ACTION是否是 ACTION_DOWN,ACTION_HOVER_MOVE,則循環判斷child(過濾出消費了Touch事件的child)
   (a) 首先判斷child是否可以接收touch事件或是否在child區域之內, 如果其中一個不滿足則跳過此child,繼續下個child判斷
   (b) 如果滿足條件,則從child查找出消費了touch事件的子view,如果存在這樣的view則跳轉循環判斷
   (c) 如果不存在這樣的view,則調用dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,View child, int desiredPointerIdBits),


從頭到尾總結一下:


1.Touch事件分發中只有兩個主角:ViewGroup和View。ViewGroup包含onInterceptTouchEvent、dispatchTouchEvent、onTouchEvent三個相關事件。View包含dispatchTouchEvent、onTouchEvent兩個相關事件。其中ViewGroup又繼承於View。

2.ViewGroup和View組成了一個樹狀結構,根節點爲Activity內部包含的一個ViwGroup。

3.觸摸事件由Action_Down、Action_Move、Aciton_UP組成,其中一次完整的觸摸事件中,Down和Up都只有一個,Move有若干個,可以爲0個。

4.當Acitivty接收到Touch事件時,將遍歷子View進行Down事件的分發。ViewGroup的遍歷可以看成是遞歸的。

 分發的目的是爲了找到真正要處理本次完整觸摸事件的View,這個View會在onTouchuEvent結果返回true。

5.當某個子View返回true時,會中止Down事件的分發,同時在ViewGroup中記錄該子View。接下去的Move和Up事件將由該子View直接進行處理。由於子View是保存在ViewGroup中的,多層ViewGroup的節點結構時,上級ViewGroup保存的會是真實處理事件的View所在的ViewGroup對象:如ViewGroup0-ViewGroup1-TextView的結構中,TextView返回了true,它將被保存在ViewGroup1中,而ViewGroup1也會返回true,被保存在ViewGroup0中。當Move和UP事件來時,會先從ViewGroup0傳遞至ViewGroup1,再由ViewGroup1傳遞至TextView。

6.當ViewGroup中所有子View都不捕獲Down事件時,將觸發ViewGroup自身的onTouch事件。觸發的方式是調用super.dispatchTouchEvent函數,即父類View的dispatchTouchEvent方法。在所有子View都不處理的情況下,觸發Acitivity的onTouchEvent方法。

7.onInterceptTouchEvent有兩個作用:1.攔截Down事件的分發。2.中止Up和Move事件向目標View傳遞,使得目標View所在的ViewGroup捕獲Up和Move事件。

ViewGroup是否允許攔截 disallowIntercept(禁止攔截) 可以通過調用ViewGroup的如下方法

public void requestDisallowInterceptTouchEvent(boolean);

//是否禁用攔截,TRUE,那麼不會調用 false表示允許攔截, 如果允許攔截,則能夠執行到onInterceptTouchEvent方法的;

事件派發先是隧道方式、再是冒泡方式
隧道方式傳遞,直到某一個元素消耗此事件,由上至下逐層分發視圖。
冒泡方式傳遞,當某個視圖消耗事件後其return boolean 是與分發相反的方法向上傳遞。
具體分發給哪一個視圖是通過當前觸摸點座標在當前層哪個視圖上判斷


發佈了41 篇原創文章 · 獲贊 4 · 訪問量 14萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章