Android 事件分發原理和實際場景解析

事件分發是android裏的解決事件衝突的一種機制。一般我們的佈局都是一層疊着一層,那麼當我們手指點擊或者滑動的時候,屏幕怎麼知道哪一個控件該響應,哪一個控件該滑動呢?這依靠的就是事件分發機制。

本次文章源碼細節不扣,但是主要流程會列出來,感興趣的可以去源碼中找對應的邏輯,有疑問可以留言哦。

目錄

1.流程解讀:

從Activity--->DecorView:

從DecorView到子view:

2.場景分析:

場景一:

場景二:

場景三:


 

1.流程解讀:

從Activity--->DecorView:

當我們在activity中觸摸屏幕點擊的時候,它的流程是這樣的:

基本流程:

  1. 從activity的dispatchTouchEvent開始,調用到DecorView的dispatchTouchEvent()(DevorView是ViewGroup的子類,使用ViewGroup的事件分發機制)
  2. DecorView是所有view的父佈局,它的dispatchTouchEvent() 會遞歸從外到內進行事件分發。

從DecorView到子view:

DecorView和它的ziview是父佈局和子佈局的關係,所以他們的流程就是父佈局到子佈局事件分發的過程,之後所有的過程都是這樣遞歸的。記住一點,事件分發的流程順序是從父佈局到子佈局。

 

事件分發涉及到的幾個關鍵方法:

  • ①dispatchTouchEvent() // 事件分發的總調度方法
  • ②requestDisallowInterceptTouchEvent() // 子view限制父類不能攔截事件
  • ③onInterceptTouchEvent() // 決定當前的view是否攔截事件
  • ④onTouchEvent() // 當前view事件操作。返回值代表是否消費了事件

其中③④是在①中執行的,②是在外部執行的。

 

ok,下面從源碼的角度分析,在這之前,先貼幾條結論:

  1. 一般講事件的開始和結束是指事件流的開始和結束。一個事件流指一系列操作,一般是以DOWN事件開始,UP或cancel事件結束。例如 DOWN-->MOVE-->MOVE-->....-->UP 這是一個事件流。
  2. 一個事件流中,如果一個View在DOWN事件的時候消費了事件,那麼在父view不攔截的情況下,後續事件則都由它處理。
  3. 一個事件流中,父view可以在任何階段攔截事件。當父view攔截了事件,那麼後續的事件就由父view來接管了。
  4. 子view可以通過設置parent.requestDisallowInterceptTouchEvent(true)來禁止父view攔截事件。

 

下面進入源碼了,主要代碼都在ViewGroup#dispatchTouchEvent()中,代碼就不貼了。可以去看一下這個方法的源碼,大致流程如下:

  • ① 處理down事件:如果是DOWN事件,那麼清除觸摸對象mFirstTouchTarget,重置狀態。
  • ② 檢查事件攔截:通過狀態和onInterceptTouchEvent() 的返回值來設置intercepted
  • ③ 如果不攔截&不取消(其他過濾條件省略)&當前事件是DOWN事件,按照從前往後的順序遍歷子view。遍歷過程:
    • 如果找到子view消費了事件,那麼觸摸對象mFirstTouchTarget賦值,alreadyDispatchedToNewTouchTarget=true,跳出循環。
  • ④ 如果觸摸對象mFirstTouchTarget爲空,那麼自己消費事件,即執行自己的onTouchEvent()
  • ⑤ 如果觸摸對象不爲空,則判斷是否已經處理過(依據alreadyDispatchedToNewTouchTarget和target)
    • --如果未處理過:判斷是否攔截(即intercepted)
    • --如果不攔截,繼續交給target處理事件
    • --如果攔截,則向target傳遞一個cancel事件,然後清除mFristTouchTarget。 然後下一個後續事件中自己就消費事件了。

上面的流程在源碼中都能找到,說的比較詳細,下面我們來從實際場景分析以上流程。

 

2.場景分析:

舉例三個常見的場景

場景一:

父view不攔截,子view消費事件。

事件流開始===

1.DOWN事件:

經過①②後:

mFirstTouchTarget=null

intercepted=false

經過③後:

mFirstTouchTarget=taget

alreadyDispatchedToNewTouchTarget=true

這時已經處理了事件

④過

⑤過

 

2.MOVE事件

經過①②,

mFirstTouchTarget=target

intercepted=false

③過

④過

⑤滿足觸摸對象不爲空,未處理過,不攔截,繼續交給target處理事件

 

之後的MOVE/UP事件流程同2

===事件流結束

 

場景二:

父view攔截事件,子view消費事件

 

事件流開始===

1.DOWN事件:

經過①②後:

mFirstTouchTarget=null

intercepted=true

③過

④滿足mFirstTouchTarget=null, 自己消費事件

⑤過

 

之後的事件都同1

===事件流結束

 

場景三:

父view開始不攔截事件,當MOVE時開始攔截,子view消費事件。

 

事件流開始===

1.DOWN事件:

經過①②後:

mFirstTouchTarget=null

intercepted=false

經過③後:

mFirstTouchTarget=taget

alreadyDispatchedToNewTouchTarget=true

這時已經處理了事件

④過

⑤過

 

2.MOVE事件

經過①②後:

mFirstTouchTarget=target

intercepted=true

③過

④過

⑤滿足 觸摸對象不爲空,未處理過,攔截,則向target傳遞一個cancel事件,然後清除mFristTouchTarget。

 

3.下一個MOVE事件

經過①②後:

mFirstTouchTarget=null

intercepted=true

③過

④滿足mFirstTouchTarget=null, 自己消費事件

⑤過

 

之後的事件都同3

===事件流結束

 

 

 

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章