事件分發勘誤:當前View的TouchTarget是什麼

前言

這裏先給你們道個歉! 上一篇文章爲啥還在聊:事件分發?還不是由於不會!,其實有一個嚴重的錯誤。不知道給小夥伴們帶來了多少困擾。那就是我在上一篇文章中說當前View的TouchTarget記錄的是消費當前事件的View。其實這是不對的面試

因此今天這篇文章首先是糾錯,其次是完全捋一下TouchTarget到底指的是什麼,以及事件分發的全流程。post

正文

上文我們從源碼看到,若是當前View的mFirstTouchTarget不爲null,那麼當前的TouchEvent事件,就會直接分發到mFirstTouchTarget中記錄的View上(也就是調用這個View的dispathTouchEvent())。學習

那麼假設mFirstTouchTarget爲消費這個事件的View,那麼事件就會從根View直接分發到消費事件的View,也就是說事件直接跳過全部中間的層級的View(「沒有中間商賺差價」)。不過我猜有經驗的小夥伴都會知道,這個錯的,不會越過中間的View。spa

沒錯,事實上這個假設的確是錯的!3d

Log打臉

代碼中分別是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。

Debug求證

接下來,讓咱們斷點到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消費此事件。

  • 若是返回true,說明事件分發成功,mFirstTouchTarget記錄爲ViewGroupB,之後的事件所有交給它處理
  • 若是返回false,此事件沒有View消費,mFirstTouchTarget爲null。調用自身onTouchEvent()嘗試本身消費,若是不消費,之後事件將再也不傳遞過來。(由於對於ViewGroupA的上一層來講,mFirstTouchTarget爲null)

ViewGroupB的dispatchTouchEvent()一樣要重走攔截這一套,固然自身一樣沒有攔截,而後事件一樣經過x、y交給本身的子View的dispatchTouchEvent(),也就是ViewC。

ViewGroupB一樣也在等ViewCdispatchTouchEvent()返回值來決定本身的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,小夥伴們,若是以爲文章不錯...我有個小小的請求,我想吃雞腿了....

我是一個應屆生,最近和朋友們維護了一個公衆號,內容是咱們在從應屆生過渡到開發這一路所踩過的坑,以及咱們一步步學習的記錄,若是感興趣的朋友能夠關注一下,一同加油~

我的公衆號:鹹魚正翻身
相關文章
相關標籤/搜索