1. 程式人生 > >ViewGroup源碼解讀

ViewGroup源碼解讀

cancel 通過 transform cal 同步 事件傳遞 進行 先來 ets

  我們之前剛剛分析完事件傳遞機制和view的源碼,如果沒有看過的,建議看完View的事件攔截機制淺析以及View的事件源碼解析。這次我們來分析下viewgroup的。      可能有人會想,怎麽又是源碼分析,肯定又是一大通。其實沒你想的那麽復雜。仔細分析一波就行了。      解讀      我們都知道,一個事件完整的流程是從dispatchTouchevent–>onInterceptTouchevent–>onTouchEvent。我們先不說事件監聽的問題。上述三個步驟就是正常一個點擊的流程。前面我們分析view的時候發現它並沒有onInterceptTouchevent這個方法。這個我之前有提到,view已經是最底層了,所以就不需要攔截了。而這一整套的機制就是在ViewGroup中體現出來的。我們先來看一張圖:      這裏寫圖片描述      觸摸事件發生後,在Activity內最先接收到事件的是Activity自身的dispatchTouchEvent,然後Activity傳遞給Activity的Window。接著Window傳遞給最頂端的View,也就是DecorView。接下來才是我們熟悉的觸摸事件流程:首先是最頂端的ViewGroup(這邊便是DecorView)的dispatchTouchEvent接收到事件。並通過onInterceptTouchEvent判斷是否需要攔截。如果攔截則分配到ViewGroup自身的onTouchEvent,如果不攔截則查找位於點擊區域的子View(當事件是ACTION_DOWN的時候,會做一次查找並根據查找到的子View設定一個TouchTarget,有了TouchTarget以後,後續的對應id的事件如果不被攔截都會分發給這一個TouchTarget)。查找到子View以後則調用dispatchTransformedTouchEvent把MotionEvent的坐標轉換到子View的坐標空間,這不僅僅是x,y的偏移,還包括根據子View自身矩陣的逆矩陣對坐標進行變換(這就是使用setTranslationX,setScaleX等方法調用後,子View的點擊區域還能保持和自身繪制內容一致的原因。使用/www.zenmebanw.com Animation做變換點擊區域不同步是因為Animation使用的是Canvas的矩陣而不是View自身的矩陣來做變換)。      分析      我們先放上dispatchTouchevent的源碼,然後一步一步來分析      默認不消耗事件,如果本身沒有攔截,就交給子類的dispatch事件,如果事件沒有消費,就調用自身的onTouchEvent事件。你們仔細想想,流程是不是這樣的?      好了,我們現在開始分析整個dispatch事件。具體說明和代碼,你們自己對應= =因為太長了。      對action_down的處理:      我們發現,剛進方法的時候有個判斷,第一次按下的時候,他會通過 cancelAndClearTouchTargets(ev)取消並且清除所有的手勢操作,並且通過resetTouchState()把手勢狀態設置成默認狀態。      接下來的操作,當然就是檢查是否需要攔截事件拉。既然是攔截,當然就會走www.hbwfjx.cn/ onInterceptTouchEvent這個方法了。我們來看看,viewgroup的onInterceptTouchEvent方法是怎麽處理的。      }      我們可以發現,他默認就是false的。那麽我們繼續回到dispatch看。判斷是否攔截後,我們發現他還執行了一句話ev.setAction(action) 官方說明是恢復操作,防止被更改。      事件處理      接下來就是檢查事件是否取消咯。如果沒有取消並且沒有攔截就執行正常的事件處理。      如果事件是針對可訪問性焦點視圖,我們將其提供給具有可訪問性焦點的視圖。如果它不處理它,我們清除該標誌並像往常一樣將事件分派給所有的 ChildView。我們檢測並避免保持這種狀態,因為這些事非常罕見。這段是官方的解釋。我們繼續向下看,他執行這樣一個方法removePointersFromTouchTargets(idBitsToAssign)。是為了防止指針不同步,清除之前的觸摸標識。自我認為可能會和多指觸控有關,先不管他,我們繼續向下分析。      接下來就是打造了,他會先得到觸摸點的坐標位置,然後在當前位置查找可接觸的ChildView。然後重點!!!他的查找順序是從後向前查找。什麽意思呢?就是如果A和B有重疊的部分,並且B在A的上面,那麽他處理的便是B的事件了。而不處理A的事件。      如果子View可以接受事件,那麽我們就給他一個觸摸的標識。接下來他會通過調用dispatchTransformedTouchEvent把事件分配給子View。      最後他會判斷是否有touchtarget。如果沒有的話,那就處理子view的事件。否則就會遍歷touchtarget處理事件,也就是之前說的多點觸控。在往後就是對action_up和cancel做的一些處理了,譬如:重置手勢狀態,移除多指操作等等。      分析      前面我們說到了,會通過這個方法把事件分發給子view。我們還是先來看代碼:      這段代碼就比之前簡單很多了。我們會發現,他先判斷狀態是否取消,如果取消了,把當前事件變成取消狀態,然後在判斷是否有子view。如果有子view的話直接調用子view的dispatch事件。下面就是多指了,一個pointer對應一個ID,防止處理沖突。我印象中能簡單粗暴的處理多指,應該是ViewDragHelper了。具體,你們可以自己去看。後面就如之前一樣,判斷child是否為null。然後得到是執行自身的事件還是child的事件。      總結      包涵多個子view的時候,我們是從後遍歷,判斷當前view是否可以點擊,然後分發給需要處理的子view。      2.我們可以在onInterceptTouchEvent中進行事件攔截。      3.我們可以發現ViewGroup沒有onTouchEvent www.wmyl130.com 事件,說明他的處理邏輯和View是一樣的。      4.子view如果消耗了事件,那麽ViewGroup就不會在接受到事件了。

ViewGroup源碼解讀