天天看點

View源碼分析之事件分發機制

前言

事件分發機制主要是為了處理使用者觸摸螢幕産生的事件,因為每個頁面都是由多個View組合而成的,那麼具體由誰來處理這個事件呢,接下來我們來分析一下。

概念

觸摸事件也就是對MotionEvent事件的分發過程,即當一個MotionEvent産生以後,系統需要把這個事件傳遞給具體的view。MotionEvent包含觸摸的動作、位置等;這個類裡包含了一系列的事件,事件的類型有

觸摸狀态 具體動作
ACTION_DOWN 按下 手勢開始
ACTION_UP 擡起 手勢結束
ACTION_CANCLE 取消 目前手勢已終止
ACTION_MOVE 移動 在down和up之間發生

事件分發的順序是Activity->ViewGroup->View,事件基本是從外向裡再向外,是以說預設情況下,最後消費事件的都是View。

事件分發的核心方法有三個:

方法 作用
dispatchTouchEvent 用來進行事件分發,第一個被調用,表示是否消耗目前事件
onInterceptTouchEvent 用來判斷是否攔截某個方法(ViewGroup有此方法,View沒有),在dispatchTouchEvent中調用,如果目前ViewGroup攔截了某個事件,那麼同一事件序列将不會再調用,表示是否攔截目前事件
onTouchEvent 用來處理點選事件,在dispatchTouchEvent中調用,不消耗目前事件,那麼同一事件序列将無法再接收到事件,表示是否消耗目前事件

從上述表格可以這麼了解,事件從Activity出發,因為布局中加載的頂級View是DecorView(繼承FrameLayout),本身就是一個ViewGroup,是以它的dispatchTouchEvent就會被調用,開始進行事件的分發,首先會繼續判斷目前ViewGroup是否進行了攔截,如果攔截,那麼點選事件就會交給ViewGroup去處理,不再向下傳遞,分發結束;如果沒有攔截,那麼就會調用ViewGroup中包含的子控件的dispatchTouchEvent方法,事件向下傳遞,如果子控件還是ViewGroup就繼續上面的循環,直到事件最終被處理消費掉。整個流程大緻如此。

源碼分析

接下來我們從源碼的角度來分析下上面的流程。上面說了事件首先是傳遞給目前Activity,那麼就從Activity的dispatchTouchEvent開始

View源碼分析之事件分發機制

重點看下第二個if語句,這裡的getWindow傳回的是window的實作類PhoneWindow,那麼找到這個方法如下:

View源碼分析之事件分發機制
View源碼分析之事件分發機制

ViewGroup事件分發

可以看到Activity的分發過程交由頂級DecorView來處理,然後又調用到ViewGroup的dispatchTouchEvent;

View源碼分析之事件分發機制

以上為第一個重點代碼,是否攔截事件

View源碼分析之事件分發機制
View源碼分析之事件分發機制
View源碼分析之事件分發機制

以上為第二個重點,對ViewGroup的子元素進行周遊,找到能夠處理點選事件的子元素并調用dispatchTransformedTouchEvent方法,進行事件的分發,當子元素能夠處理事件,就調用addTouchTarget方法,對mFirstTouchTarget方法進行指派

View源碼分析之事件分發機制

在同一事件結束後調用resetTouchState,對mFirstTouchTarget清空還原

這樣我們就将ViewGroup的dispatchTouchEvent方法分析完成了。

View事件分發

由上面dispatchTransformedTouchEvent方法可知,最後無論是ViewGroup消耗還是View消耗都會調用View.dispatchTouchEvent方法,接下來看下:

View源碼分析之事件分發機制

View的dispatchTouchEvent比較簡單,首先判斷mOnTouchListener != null同時li.mOnTouchListener.onTouch(this, event)傳回值為true那麼result=true;那麼下面的!result && onTouchEvent(event)中的第一個條件就不會成立是以onTouchEvent永遠不會得到執行,由此可見onTouch優先級要高于onTouchEvent。

下面我們來看下onTouchEvent方法:

View源碼分析之事件分發機制

這部分代碼比較簡單,如果控件不是DISABLED,那麼就會進入下面的switch語句,當接收到MotionEvent.ACTION_UP時,最後會執行performClickInternal方法,代碼如下:

View源碼分析之事件分發機制

可以看到我們設定的li.mOnClickListener.onClick(this)會調用,會回調到onClick方法裡,自此onTouchEvent方法也講解完畢。

總結

  • 事件起始由ACTION_DOWN結束為ACTION_UP
  • 整個流程是責任鍊模式,從上到下再到上
  • 事件傳遞優先級:onTouch -> onTouchEvent -> onClickListener.onClick
  • ViewGroup預設不攔截任何事件