Android事件分發傳遞回傳機制詳解

如果想參與實際開發項目,若不理解事件分發回傳機制的話,幾乎等於“”摸黑抓鰍”,因為幾乎每個項目都會出現滑動沖突問題;而要想解決滑動沖突問題,必須先瞭解甚至掌握事件分發傳遞機制。等到瞭解決滑動沖突時,至少沒有一種“斷層”的感覺。所以本專欄開篇先介紹事件機制,對安卓中的事件機制,做一個詳細的介紹與分析。

首先,View的幾個基本的繼承關系:

 

本博客案例的圖層:

新建三個類(分別按照上邊圖解的方式,寫出這幾個有關事件的方法)。

這個很簡單,就不羅列瞭。例如類似容器1的代碼:

 

public class ViewGroupOne extends LinearLayout {

	public ViewGroupOne(Context context, AttributeSet attrs) {
		super(context, attrs);
		// TODO Auto-generated constructor stub
	}

	public ViewGroupOne(Context context) {
		super(context);
		// TODO Auto-generated constructor stub
	}


	/**
	 * 分發事件
	 */
	@Override
	public boolean dispatchTouchEvent(MotionEvent event) {
		ActionUtiles.processEvent(event, "ViewGroupOne+分發+dispatchTouchEvent");
		return super.dispatchTouchEvent(event);
	}
	
	/**
	 * 攔截事件
	 */
	@Override
	public boolean onInterceptTouchEvent(MotionEvent event) {
		ActionUtiles.processEvent(event, "ViewGroupOne+攔截+onInterceptTouchEvent");
		return super.onInterceptTouchEvent(event);
	}
	
	/**
	 * 處理事件
	 */
	@Override
	public boolean onTouchEvent(MotionEvent event) {
		ActionUtiles.processEvent(event, "ViewGroupOne+處理+onTouchEvent");
		return true;
	}

}

筆者是吧事件抽取成工具類瞭,當然,每個方法裡都寫都寫一遍事件情況也無所謂,隻不過重復瞭一些代碼罷瞭。

 

接著寫一個復合上邊圖解的佈局:

 



    

        
        
    

註意:記得寫類的全稱。

 

好瞭,廢話不多說,直接運行程序說話。通多點擊手機屏幕。對所有的可能情況全部羅列如下:

一、默認情況,對生成的事件方法返回值什麼也不做。返回值都是默認情況:

事件1:點擊紅色位置。看log輸出

可以看到,整個事件是Activity先得到事件,調用它的dispatchTouchEvent分發事件,傳遞給ViewGroupOne,調用ViewGroupOne的dispatchTouchEvent方法,此時這個返回值默認return super.dispatchTouchEvent(event);把事件傳遞給自己的onInterceptTouchEvent(MotionEvent event)攔截事件,它的返回值也是默return super.onInterceptTouchEvent(event);認,即沒做攔截;自己的onTouchEvent(MotionEvent event)處理方法被調用,默認不處理,return super.onTouchEvent(event);。這樣事件沒有被消費,又返回給Activity的onTouchEvent(MotionEvent event) ,Activity也是默認返回值。事件銷毀。由up事件log日志,也可以看到:最後松手時,已經與ViewGroupOne沒有任何關系。

事件2:點擊綠色區域l。看log輸出:

這裡就會顯而易見瞭。多瞭ViewGroupTwo,就多往下傳遞一層,最裡面這層每做處理,最後還是回傳回來。到activity事件全部消失

事件3:點擊藍色區域:

這個肯定在意料之中,不用解釋也很清楚為何打印此log瞭。

二、增加處理:

處理1:藍色區域,MyTextView修改如下代碼:

 

@Override
	public boolean onTouchEvent(MotionEvent event) {
		ActionUtiles.processEvent(event, "MyTextView+處理+onTouchEvent");

		return true;//事件消費,不回傳
	}

此時要重新運行程序瞭再點擊藍色區域,看log:

 

MyTextView的onTouchEvent(MotionEvent event)返回true的意思是,事件由我來處理。它處理瞭事件,就不可能出現回傳瞭,出現這種log也是順理成章瞭。再看UP事件與分發攔截是一致的。最後在MyTextView中消失。這個時候可以對比一瞭,一都是默認,可以稱之為狹義的回傳機制,而二的處理1:我們可以稱之為狹義的攔截機制,每一次的往下分發,有可稱為狹義的傳遞機制(稱之為狹義,可能逼格略高些,是因為不能代表全部,卻也不失一般性;最起碼攔截與回傳分發大致是什麼很清楚瞭)。那接著就細致開來,從狹義走到廣義。

處理2:藍色區域,MyTextView修改如下代碼:

 

@Override
	public boolean onTouchEvent(MotionEvent event) {
		ActionUtiles.processEvent(event, "MyTextView+處理+onTouchEvent");

		return false;//和默認效果一樣,回傳機制
	}

再運行,打印看log:

 

 

和默認效果是一樣的。回看默認解析。

第一次總結:

TextView(沒有子組件類型)
onTouchEvent()
return true; //事件消費不回傳,自己消費掉事件
return false;或者 return super.onTouchEvent() ;//事件回傳給父組件

處理3:ViewGroupOne的dispatchTouchEvent方法做如下修改:

 

	@Override
	public boolean dispatchTouchEvent(MotionEvent event) {
		ActionUtiles.processEvent(event, "ViewGroupOne+分發+dispatchTouchEvent");
		return true;
	}

運行程序,點擊藍色位置看log:

 

其實你會發現。不管點擊屏幕哪個位置,都打印這樣的log

這是因為,ViewGroupOne的dispatchTouchEvent返回true瞭,相當於在ViewGroupOne的分發事件地方就攔截瞭事件(稱之為攔截,也因為沒有回傳,從UP日志裡也可以看出沒有回傳)。事件消失。

處理4:ViewGroupOne的dispatchTouchEvent方法做如下修改:

 

@Override
	public boolean dispatchTouchEvent(MotionEvent event) {
		ActionUtiles.processEvent(event, "ViewGroupOne+分發+dispatchTouchEvent");

		return false;
	}

 

返回false,表示自己的dispatchTouchEvent不做處理,即不做分發。就回傳,回傳給activity的onTouchEvent處理,最後事件消失

註意:ViewGroupOne的dispatchTouchEvent方法默認返回super.dispatchTouchEvent(event);的時候是要傳遞給自己的onTouchEvent方法的,問這個方式是否做攔截。上邊其實已經不知不覺介紹瞭這個情況。此時可以總結,隻有在ViewGroupOne的dispatchTouchEvent方法默認返回super.dispatchTouchEvent(event);的時候才會去調用自己的onInterceptTouchEvent(MotionEvent event) 攔截事件,是否攔截。其他都是回傳
 

處理5:ViewGroupOne的onInterceptTouchEvent方法做如下修改:

 

@Override
	public boolean onInterceptTouchEvent(MotionEvent event) {
		ActionUtiles.processEvent(event, "ViewGroupOne+攔截+onInterceptTouchEvent");
		return true;
	}

log情況

 

自己的onInterceptTouchEvent(MotionEvent event)攔截事件返回為true,表名自己要攔截,不向下傳遞。調用自己的onTouchEvent(MotionEvent event)是否處理。由於默認不處理,又回傳到activity事件消失。

處理6:ViewGroupOne的onInterceptTouchEvent(MotionEvent event)攔截事件返回false

 

@Override
	public boolean onInterceptTouchEvent(MotionEvent event) {
		ActionUtiles.processEvent(event, "ViewGroupOne+攔截+onInterceptTouchEvent");
		return false;
	}

 

又是默認的機制。

處理7:ViewGroupOne的onTouchEvent做如下處理:

註意:這個方法想要執行,前提是在ViewGroupOne的onInterceptTouchEvent方法返回true的時候

 

@Override
	public boolean onTouchEvent(MotionEvent event) {
		ActionUtiles.processEvent(event, "ViewGroupOne+處理+onTouchEvent");
		return super.onTouchEvent(event);
	}

 

返回true,自己處理事件,也就沒瞭回傳機制。事件消失

處理8:ViewGroupOne的onTouchEvent返回為false。不用打印也可以清楚,與默認效果是一樣的。

第二次總結:

2,ViewGroup
dispatchTouchEvent
return true;//自己消費,不往下(子組件)往上傳遞(不回傳)
return false;//自己不處理,回傳給父組件onTouchEvent方法處理
return super.dis…..;//問自己onInterceptTouchEvent是否攔截
問自己>onInterceptTouchEvent
true: 自己消費,調用自己onTouchEvent();不往下傳遞
false 或super: 默認往下傳遞

>onTouchEvent()
true: 自己消費(不回傳)
false 或 super: 繼續回傳

對於ViewGroupTwo和One是一摸一樣的。就不再多贅述。最後,再用一張草圖做一個收尾::

此草圖花費接近一小時時間。。。雖然潦草,但是最能說明問題,包含瞭所有可能的情況。。

 

對於攔截機制詳細介紹就完畢瞭,但是除瞭上邊這些情況外,還有許許多多的分支情況;但是大同小異,仔細分析一下,就能得出正確的結論。以後此專欄可能還會再次探討類似問題。若有問題或建議請留言更正、補充。

筆者花費幾個小時,您或許隻需要10分鐘。歡迎點贊或關註本專欄,Android進階知識不定期會更新哦。也可關註Android簡易實戰教程專欄,花5分鐘“欣賞”有趣的小案例。

You May Also Like