Android 觸摸事件解析

摘錄自:wangkuiwu' Homepage

ps: 需要具體源碼分析的童鞋可以轉(zhuǎn)戰(zhàn)原文博客,內(nèi)容十分詳盡,此文為摘錄原文結(jié)論部分,簡明非扼要。

Activity中觸摸事件

相關(guān)API:

  • dispatchTouchEvent()和onTouchEvent()

總結(jié):

  • Activity中的dispatchTouchEvent會將觸摸事件傳遞給Activity所包含的視圖。具體的實現(xiàn)方式在通過調(diào)用到Activity所屬Window的superDispatchTouchEvent,進而調(diào)用到Window的DecorView的superDispatchTouchEvent,進一步的又調(diào)用到ViewGroup的dispatchTouchEvent()。如果Activity所包含的視圖攔截或者消費了該觸摸事件的話,就不會再執(zhí)行Activity的onTouchEvent();如果Activity所包含的視圖沒有攔截或者消費該觸摸事件的話,則會執(zhí)行Activity的onTouchEvent()。

  • Activity中的onTouchEvent是Activity自身對觸摸事件的處理。如果該Activity的android:windowCloseOnTouchOutside屬性為true,并且當(dāng)前觸摸事件是ACTION_DOWN,而且該觸摸事件的坐標(biāo)在Activity之外,同時Activity還包含了視圖的話;就會導(dǎo)致Activity被結(jié)束。

View中觸摸事件

相關(guān)API:

  • dispatchTouchEvent()和onTouchEvent()

  • OnTouchListener, OnClickListener, OnLongClickListener等接口

總結(jié):

  • View中的dispatchTouchEvent()會將事件傳遞給"自己的onTouch()", "自己的onTouchEvent()"進行處理。而且onTouch()的優(yōu)先級比onTouchEvent()的優(yōu)先級要高。

  • onTouch()與onTouchEvent()都是View中用戶處理觸摸事件的API。onTouch是OnTouchListener接口中的函數(shù),OnTouchListener接口需要用戶自己實現(xiàn)。onTouchEvent()是View自帶的接口,Android系統(tǒng)提供了默認的實現(xiàn);當(dāng)然,用戶可以重載該API。

  • onTouch()與onTouchEvent()有兩個不同之處:

  • onTouch()是View提供給用戶,讓用戶自己處理觸摸事件的接口。而onTouchEvent()是Android系統(tǒng)自己實現(xiàn)的接口。

  • onTouch()的優(yōu)先級比onTouchEvent()的優(yōu)先級更高。dispatchTouchEvent()中分發(fā)事件的時候,會先將事件分配給onTouch()進行處理,然后才分配給onTouchEvent()進行處理。 如果onTouch()對觸摸事件進行了處理,并且返回true;那么,該觸摸事件就不會分配在分配給onTouchEvent()進行處理了。只有當(dāng)onTouch()沒有處理,或者處理了但返回false時,才會分配給onTouchEvent()進行處理。

ViewGroup中觸摸事件

ViewGroup繼承于View,它中對觸摸事件的處理,很多都繼承于View。但是,ViewGroup又有自己對觸摸事件的特定處理.

相關(guān)API:

  • ViewGroup重載了dispatchTouchEvent()接口。

  • ViewGroup新增了onInterceptTouchEvent()接口。

總結(jié):

  • ViewGroup中的dispatchTouchEvent()會將觸摸事件進行遞歸遍歷傳遞。ViewGroup會遍歷它的所有孩子,對每個孩子都遞歸的調(diào)用dispatchTouchEvent()來分發(fā)觸摸事件。

  • 如果ViewGroup的某個孩子沒有接受(消費或者攔截)ACTION_DOWN事件;那么,ACTION_MOVE和ACTION_UP等事件也一定不會分發(fā)給這個孩子!

  • ViewGroup的onInterceptTouchEvent()默認返回false。

  • ViewGroup沒有覆蓋onTouchEvent()。因此,調(diào)用ViewGroup的onTouchEvent()的話;實際上調(diào)用的是它的父類View的onTouchEvent()。

事件分發(fā)案例

??默認分發(fā)觸摸事件??

定義:

自定義一個Activity,該Activity中的顯示內(nèi)容是包含一個自定義的ViewGroup,該ViewGroup中包含一個自定義的View。

  • 自定義的Activity-MyActivity

public boolean dispatchTouchEvent(MotionEvent ev): 調(diào)用系統(tǒng)默認的dispatchTouchEvent()

public boolean onTouchEvent(MotionEvent ev): 調(diào)用系統(tǒng)默認的onTouchEvent()

  • 自定義ViewGroup-MyViewGroup

public boolean dispatchTouchEvent(MotionEvent ev): 調(diào)用系統(tǒng)默認的dispatchTouchEvent()

public boolean onTouchEvent(MotionEvent ev): 調(diào)用系統(tǒng)默認的onTouchEvent()

public boolean onInterceptTouchEvent(MotionEvent ev):: 調(diào)用系統(tǒng)默認的onInterceptTouchEvent()

  • 自定義View-MyView

public boolean dispatchTouchEvent(MotionEvent ev): 調(diào)用系統(tǒng)默認的dispatchTouchEvent()

public boolean onTouchEvent(MotionEvent ev): 調(diào)用系統(tǒng)默認的onTouchEvent()

處理流程圖:

測試流程:

  1. MyActivity收到ACTION_DOWN,進入MyActivity.dispatchTouchEvent()。

  2. MyActivity.dispatchTouchEvent()對ACTION_DOWN觸摸事件進行分發(fā),將消息傳遞給MyViewGroup。即,進入MyViewGroup.dispatchTouchEvent()。

  3. MyViewGroup.dispatchTouchEvent()會調(diào)用MyViewGroup.onInterceptTouchEvent()檢查自己有沒有對觸摸事件進行攔截。即先進入MyViewGroup.onInterceptTouchEvent()。

  4. 緊接著,MyViewGroup會退出MyViewGroup.onInterceptTouchEvent()。因為MyViewGroup沒有對觸摸事件進行攔截,MyViewGroup會繼續(xù)分發(fā)事件。

  5. MyViewGroup將觸摸事件分發(fā)給MyView,即進入MyView.dispatchTouchEvent()。

  6. MyView會調(diào)用onTouchEvent()對觸摸事件進行處理,即進入MyView.onTouchEvent() 。

  7. 緊接著,MyView會退出MyView.onTouchEvent()。返回false給MyView.dispatchTouchEvent()。

  8. MyView收到MyView.onTouchEvent()的返回值之后,退出MyView.dispatchTouchEvent()。返回false給MyViewGroup的MyViewGroup.dispatchTouchEvent(),表示MyView沒有接受該觸摸事件。

  9. MyViewGroup則得知MyView沒有接受該觸摸事件之后,將自己當(dāng)作一個View,調(diào)用View.dispatchTouchEvent();View.dispatchTouchEvent()接著就會進入MyViewGroup.onTouchEvent()。

  10. 緊接著,就會退出MyViewGroup.onTouchEvent()。MyViewGroup.onTouchEvent()沒有消費該觸摸事件,因此返回false。

  11. 然后,View.dispatchTouchEvent()就會結(jié)束,并返回false。接著,MyViewGroup就會退出MyViewGroup.dispatchTouchEvent()。并返回false。

  12. MyActivity在得知MyViewGroup沒有接受該觸摸事件之后,就會調(diào)用進入MyActivity.onTouchEvent。

  13. 緊接著,就會退出MyActivity.onTouchEvent,并返回false。

  14. 至此,MyActivity.dispatchTouchEvent()才結(jié)束。因此,會退出MyActivity.dispatchTouchEvent(),并返回false。

??View接受觸摸事件??

定義:

MyView onTouchEvent() 函數(shù)返回true,其余處理同默認分發(fā)觸摸事件

處理流程圖:

測試流程

  1. MyActivity收到ACTION_DOWN,進入MyActivity.dispatchTouchEvent()。

  2. MyActivity.dispatchTouchEvent()對ACTION_DOWN觸摸事件進行分發(fā),將消息傳遞給MyViewGroup。即,進入MyViewGroup.dispatchTouchEvent()。

  3. MyViewGroup.dispatchTouchEvent()會調(diào)用MyViewGroup.onInterceptTouchEvent()檢查自己有沒有對觸摸事件進行攔截。即先進入MyViewGroup.onInterceptTouchEvent()。 (04) 緊接著,MyViewGroup會退出MyViewGroup.onInterceptTouchEvent()。因為MyViewGroup沒有對觸摸事件進行攔截,MyViewGroup會繼續(xù)分發(fā)事件.

  4. MyViewGroup將觸摸事件分發(fā)給MyView,即進入MyView.dispatchTouchEvent()。

  5. MyView會調(diào)用onTouchEvent()對觸摸事件進行處理,即進入MyView.onTouchEvent() 。 (07) 緊接著,MyView會退出MyView.onTouchEvent()。此時的,MyView.onTouchEvent()返回的是true;表示MyView消費了此次觸摸事件。

  6. MyView.dispatchTouchEvent()得知MyView.onTouchEvent()消費此次觸摸事件之后;也就返回true,表示MyView接受該此次觸摸事件。

  7. MyViewGroup則得知MyView接受了該觸摸事件之后,就退出MyViewGroup.dispatchTouchEvent(),并返回true。

  8. MyActivity得知MyViewGroup接受了該觸摸事件之后,就會調(diào)用退出MyActivity.dispatchTouchEvent(),并返回true。

結(jié)論:

  • 如果MyView接受了ACTION_DOWN,那么就不會再再執(zhí)行其他對象的onTouchEvent()函數(shù)的。即,不會執(zhí)行MyViewGroup的onTouchEvent()和MyActivity的onTouchEvent()。因為MyView接受了ACTION_DOWN,意味著這個事件已經(jīng)被消費了;就無須其他對象再來消費ACTION_DOWN了。

  • 如果MyView接受了ACTION_DOWN,那么MyView能繼續(xù)收到ACTION_MOVE和ACTION_UP這兩種觸摸觸事件。并且ACTION_MOVE和ACTION_UP的處理流程和ACTION_DOWN的流程基本一樣。

??ViewGroup攔截但不消費觸摸事件??

定義:

MyViewGroup onInterceptTouchEvent() 函數(shù)返回true,其余處理同默認分發(fā)觸摸事件

處理流程圖:

測試流程

  1. MyActivity收到ACTION_DOWN,進入MyActivity.dispatchTouchEvent()。

  2. MyActivity.dispatchTouchEvent()對ACTION_DOWN觸摸事件進行分發(fā),將消息傳遞給MyViewGroup。即,進入MyViewGroup.dispatchTouchEvent()。

  3. MyViewGroup.dispatchTouchEvent()會調(diào)用MyViewGroup.onInterceptTouchEvent()檢查自己有沒有對觸摸事件進行攔截。即先進入MyViewGroup.onInterceptTouchEvent()。

  4. 緊接著,MyViewGroup會退出MyViewGroup.onInterceptTouchEvent()。此時,MyViewGroup.onInterceptTouchEvent()返回true。表示MyViewGroup攔截了該觸摸事件。

  5. MyViewGroup在得知自己攔截了觸摸事件之后,將觸摸事件交給自己的onTouchEvent()進行處理,即進入MyViewGroup.onTouchEvent()。

  6. 緊接著,MyViewGroup會退出MyViewGroup.onTouchEvent()。而MyViewGroup自身并沒有消費該事件,因此MyViewGroup.onTouchEvent()返回false。

  7. 隨后,退出MyViewGroup.dispatchTouchEvent(),并返回false。表示MyViewGroup沒有接受該觸摸事件。

  8. MyActivity得知MyViewGroup沒有接受該觸摸事件之后,就會調(diào)用進入MyActivity.onTouchEvent()。

  9. 緊接著,MyActivity會退出MyActivity.onTouchEvent(),并返回false。表示MyActivity也沒有消費觸摸事件。

  10. 最后,MyActivity會退出MyActivity.dispatchTouchEvent(),并返回false。表示此次觸摸事件沒有被消費。

結(jié)論:

  • MyViewGroup攔截了ACTION_DOWN,并沒有消費該ACTION_DOWN。既然MyViewGroup攔截了ACTION_DOWN,那就意味著該事件就不會分發(fā)給MyViewGroup的子類。但是由于MyViewGroup沒有消費該事件,即它并沒有接受該事件;那么,ACTION_DOWN會繼續(xù)查找其他對象來消費它自己,這也意味著該觸摸事件仍然會發(fā)送MyActivity的onTouchEvent()。如果MyActivity中有和MyViewGroup同級別的GroupView的話,在得知MyViewGroup攔截了ACTION_DOWN,卻沒有消費該ACTION_DOWN之后;MyActivity仍然能夠向這個同級的GroupView分發(fā)消息。

  • MyViewGroup并沒有消費ACTION_DOWN,那么,MyViewGroup就不能接受到ACTION_MOVE和ACTION_UP這兩種觸摸觸事件。至于MyViewGroup的子類MyView,就更加不可能接受到ACTION_MOVE和ACTION_UP了。

??ViewGroup攔截并消費觸摸事件??

定義:

MyViewGroup onInterceptTouchEvent() 函數(shù)返回true; MyViewGroup onTouchEvent()返回true; 其余處理同默認分發(fā)觸摸事件

處理流程圖:

測試流程

  1. MyActivity收到ACTION_DOWN,進入MyActivity.dispatchTouchEvent()。

  2. MyActivity.dispatchTouchEvent()對ACTION_DOWN觸摸事件進行分發(fā),將消息傳遞給MyViewGroup。即,進入MyViewGroup.dispatchTouchEvent()。

  3. MyViewGroup.dispatchTouchEvent()會調(diào)用MyViewGroup.onInterceptTouchEvent()檢查自己有沒有對觸摸事件進行攔截。即先進入MyViewGroup.onInterceptTouchEvent()。

  4. 緊接著,MyViewGroup會退出MyViewGroup.onInterceptTouchEvent()。此時,MyViewGroup.onInterceptTouchEvent()返回true。表示MyViewGroup攔截了該觸摸事件。

  5. MyViewGroup在得知自己攔截了觸摸事件之后,將觸摸事件交給自己的onTouchEvent()進行處理,即進入MyViewGroup.onTouchEvent()。

  6. 緊接著,MyViewGroup會退出MyViewGroup.onTouchEvent(),并返回true。表示MyViewGroup消費了該事件。

  7. 隨后,MyViewGroup會退出MyViewGroup.dispatchTouchEvent(),并返回true。表示MyViewGroup接受了該觸摸事件。

  8. MyActivity得知MyViewGroup接受了該觸摸事件之后,就會退出MyActivity.dispatchTouchEvent(),并返回true。表示此次觸摸事件被消費了

結(jié)論:

  • MyViewGroup攔截并消費了ACTION_DOWN。那么,該事件就不會分發(fā)給MyViewGroup的子類,也不會調(diào)用MyActivity的onTouchEvent()。

  • MyViewGroup攔截并消費了ACTION_DOWN。那么,MyViewGroup就會接受到ACTION_MOVE和ACTION_UP這兩種觸摸觸事件。而且對于ACTION_MOVE和ACTION_UP事件,不會再執(zhí)行攔截操作,即不會調(diào)用MyViewGroup.onInterceptTouchEvent();而是直接調(diào)用MyViewGroup.onTouchEvent()對事件進行處理。

??ViewGroup沒攔截但是卻消費了觸摸事件??

定義:

MyViewGroup onTouchEvent() 函數(shù)返回true, 其余處理同默認分發(fā)觸摸事件

處理流程圖:

測試流程

  1. MyActivity收到ACTION_DOWN,進入MyActivity.dispatchTouchEvent()。

  2. MyActivity.dispatchTouchEvent()對ACTION_DOWN觸摸事件進行分發(fā),將消息傳遞給MyViewGroup。即,進入MyViewGroup.dispatchTouchEvent()。

  3. MyViewGroup.dispatchTouchEvent()會調(diào)用MyViewGroup.onInterceptTouchEvent()檢查自己有沒有對觸摸事件進行攔截。即先進入MyViewGroup.onInterceptTouchEvent()。 (04) 緊接著,MyViewGroup會退出MyViewGroup.onInterceptTouchEvent()。此時,MyViewGroup.onInterceptTouchEvent()返回true。表示MyViewGroup攔截了該觸摸事件。

  4. MyViewGroup在得知自己攔截了觸摸事件之后,將觸摸事件交給自己的onTouchEvent()進行處理,即進入MyViewGroup.onTouchEvent()。

  5. 緊接著,MyViewGroup會退出MyViewGroup.onTouchEvent(),并返回true。表示MyViewGroup消費了該事件。

  6. 隨后,MyViewGroup會退出MyViewGroup.dispatchTouchEvent(),并返回true。表示MyViewGroup接受了該觸摸事件。

  7. MyActivity得知MyViewGroup接受了該觸摸事件之后,就會退出MyActivity.dispatchTouchEvent(),并返回true。表示此次觸摸事件被消費了

結(jié)論:

  • MyViewGroup沒有攔截卻消費了ACTION_DOWN。由于MyViewGroup沒有攔截ACTION_DOWN,因此,該事件會繼續(xù)分發(fā)給MyViewGroup的子類MyView。由于MyViewGroup消費了ACTION_DOWN,因此該事件不會分發(fā)給MyActivity的onTouchEvent()。

  • MyViewGroup沒有攔截卻消費了ACTION_DOWN。那么,MyViewGroup仍然可以接受到ACTION_MOVE和ACTION_UP這兩種觸摸觸事件。但是對于MyView而言,由于MyView沒有接受該事件;因此,MyView不會收到ACTION_MOVE和ACTION_UP。

參考鏈接

摘錄自:wangkuiwu' Homepage

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容