1、重要的方法
// ViewGroup:3個方法
public boolean dispatchTouchEvent(MotionEvent event)
public boolean onInterceptTouchEvent(MotionEvent event)
public boolean onTouchEvent(MotionEvent event)
// View:2個方法
public boolean dispatchTouchEvent(MotionEvent event)
public boolean onTouchEvent(MotionEvent event)
有觸摸,就有dispatchTouchEvent
方法的調用。
初始情況下,這些方法返回值都是false。
2、舉例:
定義兩個ViewGroup(MyViewGroupA, MyViewGroupB),一個View(MyView)
對于ViewGroup,重寫3個方法:
MyViewGroupB同MyViewGroupA.
對于View,重寫2個方法:
布局文件:
Activity中不作處理:
(1)先來看一下事件的傳遞:
運行程序,點擊MyView,查看日志如下:
此時可以看出,事件分發的傳遞順序:
MyViewGroupA - MyViewGroupB - MyView
事件處理的傳遞順序:
MyView - MyViewGroupB - MyViewGroupA
(2)再來看一下事件的攔截:
下面稍微改動一下,MyViewGroupB使用onInterceptTouchEvent
將事件攔截,即在MyViewGroupB中onInterceptTouchEvent
方法返回true
,打印日志如下:
可以看出,觸摸事件沒有傳遞給MyView。
如果使MyViewGroupA的onInterceptTouchEvent
方法返回true
,可以猜測事件只會在MyViewGroupA中進行處理,不會傳遞給MyViewGroupB,更不會傳遞給MyView。
運行程序,打印日志如下:
果然如此。
(3)最后看一下事件的處理:
事件處理在onTouchEvent
方法中進行。
首先還原上面兩處onInterceptTouchEvent
的返回值,此時運行打印的日志與Log_1相同,如下:
先將MyView的onTouchEvent
返回值改為true
,運行程序看一下打印的日志:
此時打印出的日志除了記錄下了MotionEvent.ACTION_DOWN
,還記錄了MotionEvent.ACTION_MOVE
和MotionEvent.ACTION_UP
,并且MotionEvent.ACTION_MOVE
還有可能隨著觸摸重復出現,但這些在這里暫且不討論,只看事件處理的傳播。
觀察黃框圈中的部分(和下面的兩部分相比,只是action的類型不同),事件傳遞跟以前一樣,但是事件處理到MyView就結束了,不會再傳給MyViewGroupA和MyViewGroupB。
如果把MyView的onTouchEvent
返回值改回原來的值,將MyViewGroupB的onTouchEvent
返回值改為true
,再來看一下打印的日志:
同樣,我們只關注黃框圈中的部分(剩下的部分后面講),事件傳遞跟以前一樣,但是事件處理到MyViewGroupB就結束了,不會再傳給MyViewGroupA。
其實黃框下面的日志也可以看出,事件同樣處理到MyViewGroupB就結束了。
(4)好了,現在我們把MyViewGroupB的onTouchEvent
返回值也改回去,此時的日志情況:
接下來看一下MyView分別setOnTouchListener
和setOnClickListener
的情況。
setOnTouchListener
先讓OnTouchListener.onTouch
方法返回false
,代碼如下:
日志如下:
可以看到,事件傳遞和事件處理的順序跟原來一樣,但是MyView事件處理中,先調用了OnTouchListener.onTouch
,之后才調用onTouchEvent
。
接著讓OnTouchListener.onTouch
方法返回true
,代碼如下:
日志如下:
可以看到,事件傳遞的順序跟原來一樣,但是MyView的事件處理,只調用了OnTouchListener.onTouch
,沒有調用onTouchEvent
,也沒有傳給MyViewGroupA和MyViewGroupB。
這種情況的出現就跟dispatchTouchEvent
方法有關了,需要涉及到源碼分析,這篇文章只是先描述一下出現的情況,所以就先簡單提一下,具體下篇再分析。
當OnTouchListener.onTouch
返回true
時,就會導致dispatchTouchEvent
返回true
,此時就不再繼續向下執行了,因此onTouchEvent
也就不會被調用了,事件處理也到此結束,不會繼續傳遞。
setOnClickListener
上面列舉的幾個情況,有個隱含的前提,就是MyViewGroupA、MyViewGroupB、MyView都是默認不可點擊的;
現在我們把MyView設置成可點擊的,比如在布局中設置android:clickable="true"
或代碼中setOnClickListener
,同時將OnTouchListener.onTouch
的返回值改為false
,代碼如圖所示:
再次點擊MyView,查看日志如下:
事件傳遞順序不變,但是事件處理沒有再傳給MyViewGroupA和MyViewGroupB。
接著還是讓OnTouchListener.onTouch
方法返回true
,再看一下打印的日志:
可以看到,OnClickListener.onClick
沒有執行。
因為OnClickListener.onClick
這個方法是在onTouchEvent
的performClick
方法中調用的,OnTouchListener.onTouch
方法返回true
之后,onTouchEvent
就沒有執行,導致OnClickListener.onClick
沒有調用,也就不會有相應的日志信息了。
由此可以看出,OnTouchListener.onTouch
優先OnClickListener.onClick
調用。
3、這篇文章從代碼演示的角度整理了一下我對Android事件傳遞的理解,最后曝照一張,權當總結,字不好,看官勿笑
參考:
Android事件分發機制完全解析,帶你從源碼的角度徹底理解(上)
Android事件分發機制完全解析,帶你從源碼的角度徹底理解(下)
Android事件分發機制詳解
Android群英傳-事件攔截機制分析