Android 事件分發機制

事件分發機制在android中非常常見,比如:手勢滑動,自定義View,多點觸控都有它的身影。事件分發的順序是:Activity->GroupView->View,GrounpView包括五大布局,ListView和GridView等,View包括TextView,ImageView等。我們今天就來搞一下。

首先我們的手指和屏幕接觸的MotionEvent對象會產生一系列事件,它有四種狀態:

MotionEvent對象的四種狀態:

  • MotionEvent.ACTION_DOWN:手指按下屏幕的瞬間
  • MotionEvent.ACTION_MOVE:手指在屏幕上移動
  • MotionEvent.ACTION_UP:手指離開屏幕瞬間
  • MotionEvent.ACTION_CANCEL :取消手勢,一般由程序產生

事件分發所涉及的主要方法:

1.1 dispatchTouchEvent

用來進行事件分發和傳遞的,返回true的時候一定是自己去消費,返回false有可能是自己消費也有可能是傳遞給上一級的OnTouchEvent方法, super就傳遞給其他的view。該方法也是觸摸事件第一個執行的方法。

1.2 onInterceptTouchEvent

是viewGroup特有的,用來做觸摸事件攔截的,默認返回false:如果false或者super表示不攔截,事件繼續向下傳遞。事件所經過的每一層的viewGroup都會去調用該方法來詢問是否攔截。如果返回true,則代表攔截該事件,停止傳遞給子view,會走自己的onTouchEvent事件 事件被攔截後,子view會接收到一個cancel事件,來恢復之前的狀態,結束當前事件流。

1.3 requestDisallowInterceptTouchEvent

是viewGroup專有的方法,也是事件攔截用的,和onInterceptTouchEvent類似。不過一般是在子view中來調用的。

1.4 onTouch

是觸摸事件,當一個觸摸事件被分發到一個view的時候。

1.5 onTouchEvent

真正用來處理觸摸事件的最後調用的方法, onTouchEvent是否攔截取決於down事件。

1.6 onClick

是一個點擊事件,是在onTouchEvent的up事件裏面執行的,它的優先級是最低,如果在onTouchEvent或OnTouchListener的onTouch方法如果返回true,則不響應onClick方法。

onTouch,onTouchEvent和onClick的執行順序:onTouch—>onTouchEvent—>onClick,注意:onClick直接消費掉了事件,不會再向上回溯事件了。 我們來一個表格瞭解一下Activity和ViewGroup和View各自擁有的方法:

類型

擁有方法

Activity

dispatchTouchEvent,onTouchEvent

GroupView

dispatchTouchEvent,onTouchEvent ,onInterceptTouchEvent,requestDisallowInterceptTouchEvent

View

dispatchTouchEvent,onTouchEvent ,onTouch,onClick

在來一張圖瞭解一下其分發流程(自己寫的,體諒一下):

image.png

一共分有三層,Activity——>ViewGroup——>View,其中ViewGroup比其他兩個多了一個onInterceptTouchEvent方法,箭頭代表了事件流的走向,箭頭上的值表示該方法的返回值,消費框表示事件被消費掉,不會向下或者向上傳遞,接下來我們寫代碼運行一下看效果。

測試部分

1.首先先自定義一個view(TextView)和一個ViewGroup(ReleativeLayout),然後重新其dispatchTouchEvent,onTouchEvent等方法,佈局關係基本是這樣的:

image.png

已ViewGroup爲例,部分代碼是這樣的:

image.png

我們先啥也不幹,就正常點擊一下,打印看看:

image.png

由於我們只是點擊然後迅速擡起,所以沒有Action_Move這個動作,所以正常的事件流程應該是這樣的: 事件依次從Activity的dispatchTouchEvent——>ViewGroup的dispatchTouchEvent——>ViewGroup的onInterceptTouchEvent——>View的dispatchTouchEvent——>View的onTouchEvent——>ViewGroup的onTouchEvent——>Activity的onTouchEvent流轉完成,由於沒有事件沒有被消費,所以事件一路傳遞下來,又一路回溯回去,所以最終事件就被Activity消費了(就是Activity的UP事件)。

2.我們剛纔是正常的事件分發流程圖,沒有給View即TextView設置click事件,那我們現在給TextView設置點擊事件看看,事件是怎麼分發的?

image.png

可以看到如果給View設置了click事件,那麼事件會最終分發給view,被view消費,這就是我們平時最常用的事件分發流程

3.其他的你們都可以在個個階段的事件分發中,返回不同的值去測試驗證,我們再說一個viewGroup的onInterceptTouchEvent()返回true和onTouchEvent也返回true看一下:

image.png

可以看到被攔截之後,後續move,up事件都交給自己處理,並且不再調用onIntercepetTouchEvent,而且事件也不再傳遞到子View

4.那如果我們不想讓TextView的點擊事件響應怎麼辦,按照上面的andorid事件分發流程圖,方法多了,我們可以在不同的階段進行控制,不讓事件向下分發,但我們試試onTouch()這個方法,此方法默認返回false,我們現在讓它返回true試試。測試結果是可以的,也就是說onTouch()方法返回true也是自己消費了,不會在向下傳遞到onTouchEvent()了,更不會傳遞到onClick()了,如圖:

image.png

image.png

ACTION_CANCEL的出現時機

ACTION_CANCEL的出現場景爲:手指點擊屏幕停頓,讓系統以爲view的onTouchEvent要消費此事件的時候滑動,在onInterceptTouchEvent()中攔截,交給ViewGroup來處理此次事件,這個時候就會額外觸發一個ACTION_CANCEL來傳遞給子view來恢復子view的狀態. 那麼我們怎麼做呢?

  • 先讓系統以爲view的onTouchEvent要消費事件:在view的onTouchEvent中return true,結果如下:

image.png

此時事件是被view的onTouchEvent消費了。

  • 在viewGrounp的onInterceptTouchEvent方法中對ACTION_MOVE動作進行攔截return true,把屬於view的事件給轉移到ViewGroup消費,結果如下:

image.png

ACTION_CANCEL這個動作目前是被觸發了,但大家有沒有發現後續move沒有再經過ViewGroup,明明是在ViewGroup的ACTION_MOVE中return true消費了呀? 原因是:還需要在ViewGroup的onTouchEvent返回true才行,不然viewGroup又會默認super,把ACTION_MOVE等傳遞給了Activity消費而不是自己消費,那我們現在改一下,運行如下:

image.png

這樣纔對嘛。 項目已上傳github.

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