這裏先給你們道個歉! 上一篇文章爲啥還在聊:事件分發?還不是由於不會!,其實有一個嚴重的錯誤。不知道給小夥伴們帶來了多少困擾。那就是我在上一篇文章中說當前View的TouchTarget記錄的是消費當前事件的View。其實這是不對的!面試
因此今天這篇文章首先是糾錯,其次是完全捋一下TouchTarget到底指的是什麼,以及事件分發的全流程。post
上文我們從源碼看到,若是當前View的mFirstTouchTarget
不爲null,那麼當前的TouchEvent事件,就會直接分發到mFirstTouchTarget
中記錄的View上(也就是調用這個View的dispathTouchEvent()
)。學習
那麼假設mFirstTouchTarget
爲消費這個事件的View,那麼事件就會從根View直接分發到消費事件的View,也就是說事件直接跳過全部中間的層級的View(「沒有中間商賺差價」)。不過我猜有經驗的小夥伴都會知道,這個錯的,不會越過中間的View。spa
沒錯,事實上這個假設的確是錯的!3d
代碼中分別是ViewGroupA中嵌套ViewGroupB,ViewGroupB嵌套了ViewC。全部的分發方法都直接返回super的實現,只有ViewC的
onTouchEvent()
方法返回true。此時咱們點擊滑動ViewC...code
ViewGroupA -> dispatchTouchEvent -> MotionEvent.ACTION_DOWNcdn
ViewGroupA -> onInterceptTouchEvent -> MotionEvent.ACTION_DOWNblog
ViewGroupB -> dispatchTouchEvent ->MotionEvent.ACTION_DOWN事件
ViewGroupB -> onInterceptTouchEvent -> MotionEvent.ACTION_DOWN開發
ViewC -> dispatchTouchEvent -> MotionEvent.ACTION_DOWN
ViewC -> onTouchEvent -> MotionEvent.ACTION_DOWN
ViewGroupA -> dispatchTouchEvent -> MotionEvent.ACTION_MOVE
ViewGroupA -> onInterceptTouchEvent -> MotionEvent.ACTION_MOVE
ViewGroupB -> dispatchTouchEvent ->MotionEvent.ACTION_MOVE
ViewGroupB -> onInterceptTouchEvent -> MotionEvent.ACTION_MOVE
ViewC -> dispatchTouchEvent -> MotionEvent.ACTION_MOVE
ViewC -> onTouchEvent -> MotionEvent.ACTION_MOVE
ViewGroupA -> dispatchTouchEvent -> MotionEvent.ACTION_UP
ViewGroupA -> onInterceptTouchEvent -> MotionEvent.ACTION_UP
ViewGroupB -> dispatchTouchEvent ->MotionEvent.ACTION_UP
ViewGroupB -> onInterceptTouchEvent -> MotionEvent.ACTION_UP
ViewC -> dispatchTouchEvent -> MotionEvent.ACTION_UP
ViewC -> onTouchEvent -> MotionEvent.ACTION_UP
咱們會發現,Touch事件是按照View層級的順序一級一級的去傳遞。所以咱們能夠得出一個結論:
當前View上的mFirstTouchTarget
頗有可能記錄的是它子View中可能消費事件的那個View。
接下來,讓咱們斷點到ViewGroupA上的dispathTouchEvent()
一看究竟:
的確如此,當前View的mFirstTouchTarget
就是記錄它子View中那個命中消費事件的View。
所以基於這個結果,事件分發的流程就真正串起來了!
上篇文章咱們知道事件分發的邏輯一切都在根View的dispatchTouchEvent()
中完成。此時只把場景限制到ViewGroupA->ViewGroupB->ViewC中,咱們來看一下分發是如何完成的。
事件DOWN來到ViewGroupA的dispatchTouchEvent()
,此方法中因爲本身onInterceptTouchEvent()
返回false,所以將經過事件的x、y來交給符合此範圍的子View繼續去分發此事件。可是此時誰都不知道這個事件能被誰消費。
找到子View後(也就是ViewGroupB),調用ViewGroupB的dispatchTouchEvent()
。對於ViewGroupA來講,ViewGroupB的dispatchTouchEvent()
返回值將決定是否有View消費此事件。
mFirstTouchTarget
記錄爲ViewGroupB,之後的事件所有交給它處理。mFirstTouchTarget
爲null。調用自身onTouchEvent()
嘗試本身消費,若是不消費,之後事件將再也不傳遞過來。(由於對於ViewGroupA的上一層來講,mFirstTouchTarget
爲null)ViewGroupB的dispatchTouchEvent()
一樣要重走攔截這一套,固然自身一樣沒有攔截,而後事件一樣經過x、y交給本身的子View的dispatchTouchEvent()
,也就是ViewC。
ViewGroupB一樣也在等ViewC
dispatchTouchEvent()
返回值來決定本身的mFirstTouchTarget
。
因爲ViewC是一個View,而對於View的dispatchTouchEvent()
返回值來講,它取決於自身的onTouchEvent()
。由於咱們的ViewC消費了事件,也就是onTouchEvent()
返回了true。所以ViewC的dispatchTouchEvent()
也返回true。
因此ViewGroupB的經過ViewC的dispatchTouchEvent()
返回true,知道了事件被ViewC消費,那麼此時mFirstTouchTarget
記錄了ViewC,後續的事件ViewGroupB就知道分發給ViewC了。所以此時VIewGroupB的dispatchTouchEvent()
返回true。
同理,ViewGroupA也就是知道了,分發到本身的事件之後能夠分發給VIewGroupB...
就這樣,後續的MVOE,UP事件就能夠順利的一層層往下分發了。
所以,有一些問題咱們就能夠經過源碼去解釋了:
問題1:ViewGroupB的onInterceptTouchEvent()
返回true,但onTouchEvent()
返回false會發生什麼?
當前View若是攔截了事件,那麼它的dispatchTouchEvent()
返回值,將取決子自身的onTouchEvent()
。所以此時返回了false。因此VewGroupB的dispatchTouchEvent()
返回false。
因此此時對於ViewGroupA來講,事件在子View這裏是沒有分發出去的,因此ViewGroupA嘗試消費,若是自身onTouchEvent()
返回false,那麼後續的事件將不過度發過來了。
問題2:ViewGroupB中的dispatchTouchEvent()
不掉任何方法直接返回true,會發生什麼?
咱們知道VewGroup以及View中dispatchTouchEvent()
的實現纔是真正進行事件分發的關鍵。若是咱們ViewGroupB中的dispatchTouchEvent()
直接返回true。對於VewGroupA來講,後續的事件將直接交給ViewGroupB,那是ViewGroupBdispatchTouchEvent()
沒有調任何方法,所以對於ViewGroupB來講它除了會回調dispatchTouchEvent()
將不會調用onInterceptTouchEvent()
和onTouchEvent()
。
這倆篇文章因此真正意義上把事件分發的基本流程從根本了梳理了一篇。建議各位小夥伴結合着文章本身也看一篇源碼...
一篇下來,關於事件分發的面試題絕對不可能難住你...(哈哈,還差一個CANCEL事件,之後有時間再補上)
OK,小夥伴們,若是以爲文章不錯...我有個小小的請求,我想吃雞腿了....