Android View的點擊事件傳遞

Android View的點擊事件傳遞

View的定義

view是Android中所有組件的父類,不管是Button或者TextView這樣的初學者所瞭解的簡單的View,還是ViewGroup,LinearLayout等,都是View的子類。

點擊事件

點擊事件就是MotionEvent,即手指接觸屏幕之後產生的一系列事件,一般需要關注的事件類型是:

ACTION_DOWN

就是手指剛剛接觸屏幕的瞬間產生的動作,但是很多時候這個動作一般不會讓某一個View來處理。

ACTION_MOVE

手指在屏幕上滑動,並且滑動的距離超過某個最小值(TouchSlop),才被判定爲滑動,其中TouchSlop在不同設備上的值往往是不一樣的,我們可以通過ViewConfiguration.get(mContext).getScaledTouchSlop()獲取這個常量。

ACTION_UP

手指離開屏幕的產生的動作 。
通過以上我們可以得出,手指在屏幕上的一次操作,會產生出一系列的點擊事件,比如簡單的點擊就會產生一個ACTION_DOWN和一個ACTION_UP,如果滑動的話,還需要在其中加入多個ACTION_MOVE 。

View的事件分發機制

View中點擊事件的傳遞規則

View針對MotionEvent有三個處理函數:

public boolean dispatchTouchEvent(MotionEvent ev)

用來對點擊事件進行分發,是View最早接收到MotionEvent 的函數,返回值代表是否消耗當前事件。

public boolean onInterceptTouchEvent(MotionEvent  ev)

在dispatchTouchEvent(ev)中調用,表示是否截斷當前接受到的事件。

public boolean onTouchEvent(MotionEvent  ev)

在dispatchTouchEvent(ev)中調用,用來處理點擊事件,結果表示是否消耗當前事件,如果不消耗,則在同一個事件序列中,該View無法再次接收到事件。其中TRUE表示消耗當前事件。
上面三個方法的關係可以用以下代碼表示:

public boolean dispatchTouchEvent(MotionEvent ev){
	boolean consume = false;
	if(onInterceptTouchEvent(ev)) {
		consume = onTouchEvent(ev);
	} else {
		consume = childView.dispatchTouchEvent(ev);
	}
	return consume;
}

從上面的代碼我們可以看出,View的點擊事件的傳遞規則:當上級View將點擊事件傳遞搭配當前View的時候,View的dispatchTouchEvent就會被首先調用,然後如果當前View的onInterceptTouchEvent()返回值爲TRUE,那麼就會該事件傳遞給onTouchEvent處理,不然就將其交給下一級View的dispatchTouchEvent處理。同時,值得注意的是,每一個MotionEvent只表示一個ACTION_MOVE ,ACTION_UP或者ACTION_DOWN,我們可以通過以下方法得到該MotionEvent所代表的事件:

//ev爲MotionEvent對象
ev.getAction()

同時這裏有一個疑問,加入當前View的onInterceptTouchEvent()返回值爲TRUE,但是onTouchEvent()的返回值爲FALSE(當前沒有消耗這個事件),那麼這個MotionEvent到底去哪裏了呢? 這種情況下這個MotionEvent會傳遞給上級View處理。
上述所說的只是大致的流程,其實還有很多細節的地方,有興趣的人可以去看看Android源碼。

其他總結

(一)同一序列事件是指從手指觸摸屏幕開始,直到手指離開屏幕的時候,所產生的點擊事件。

(二)在整個事件傳遞的過程中,Activity拿到事件對象,Activity把事件對象傳遞給PhoneWindow,PhoneWindow再傳遞給DecorView,DecorView通過遍歷再傳遞到我們的ViewGroup。

(三)如果在當前View中設置了onTouchlistener,或者onClickListener,那麼在整個View的點擊事件的傳遞規則優先級爲:onTouchlistener.onTouch()>onTouchEvent()>onClickListener.onClick()。如果想要截斷這個事件可以在某一處將其返回值設置爲TRUE。

(四)正常情況下,一個事件序列只能被一個Visw攔截且消耗。這一條的原因可以參考(五),因爲一旦一個元素攔截了某此事件,那麼同一個事件序列內的所有事件都會直接交給它處理,因此同一個事件序列中的事件不能分別由兩個View同時處理,但是通過特殊手段可以做到,比如一個Vew將本該自己處理的事件通過onTouchEvent強行傳遞給其他View處理。

(五)某個View一旦決定攔截,那麼這一個事件序列都只能由它來處理(如果事件序列能夠傳遞給它的話),並且它的onInterceprTouchEvent不會再被調用。這條也很好理解,就是說當一個View決定攔截一個事件後,那麼系統會把同一個事件序列內的其他方法都直接交給它來處理,因此就不用再調用這個View的onInterceptTouchEvent去詢問它是否要攔截了。

(六)某個View一旦開始處理事件,如果它不消耗ACTON_DOWN事件(onTouchEvent返回了false),那麼同一事件序列中的其他事件都不會再交給它來處理,並且事件將重新交由它的父元素去處理,即父元素的onTouchEvent會被調用。意思就是事件一旦交給一個View處理,那麼它就必須消耗掉,否則同一事件序列中剩下的事件就不再交給它來處理了,這就好比上級交給程序員一件事,如果這件事沒有處理好,短期內上級就不敢再把事情交給這個程序員做了,二者是類似的道理。

(七)如果View不消耗除ACTION_DOWN以外的其他事件,那麼這個點擊事件會消失,此時父元素的onTouchEvent並不會被調用,並且當前View可以持續收到後續的事件,最終這些消失的點擊事件會傳遞給Activity處理。

(八)ViewGroup默認不攔截任何事件。Android源碼中ViewGroup的onInterceptTouchEvent方法默認返回false

(九)View沒有onInterceptTouchEvent方法,一旦有點擊事件傳遞給它,那麼它的onTouchEvent方法就會被調用。所以如果我們要對某個事件進行截斷的時候,我們可以在它的onTouchEvent中進行處理,爲了實現很多自定義的效果,我們必須得常常去對一些控件或者佈局進行繼承,並且重寫其中的方法。

(十)view的onTouchEvent默認都會消耗事件(返回true),除非它是不可點擊的(clickable和longClickable同時爲false),View的longClickable屬性默認都爲false,clickable屬性要分情況,比如Button的clickable屬性默認爲true,而TextView 的clickable屬性默認爲false

(十一)view 的enable.屬性不影響onTouchEvent的默認返回值。哪怕一個View是disable狀態的,只要它的clickable或者longclickable有一個爲true,那麼它的onTouchEvent就返會true。

(十二)onclick會發生的前提實際當前的View是可點擊的,並且他收到了down和up的事件。

如果有任何疑問可以去看看
https://blog.csdn.net/huachao1001/article/details/51766225
寫的真的很容易理解。

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