PS:如果你覺得你在努力,那麼告訴我你努力的結果是什麼!
上篇文章中敘述了 Android 事件分發的大致流程,下面從 Activity、ViewGroup、View 三個方面介紹事件的相關方法,小節如下:
- Activity
- ViewGroup
- View
Activity#
Activity 中主要兩個與事件傳遞相關的方法,dispatchTouchEvent () 和 onTouchEvent (),事件傳遞由 Activity 的 dispatchTouchEvent () 方法開始。
事件分發#
Activity 中的事件分發方法:dispatchTounchEvent (),其源碼如下:
//事件分發
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
//空方法
onUserInteraction();
}
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
//onTouchEvent()方法默認返回false
return onTouchEvent(ev);
}
上面代碼中顯然只處理了 ACTION_DOWN 事件,說明 ACTION_DOWN 事件才會觸發事件的分發,接著調用了 Window 類的 superDispatchTouchEvent (ev) 方法,這是一個抽象方法,那麼當調用這個方法的時候,就會去調用具體子類中的方法,Window 類的具體子類就是 PhoneWindow 類,裡面的具體實現的 superDispatchTouchEvent (ev) 方法如下:
//Window類裡面的抽象方法
public abstract boolean superDispatchTouchEvent(MotionEvent event);
//Window子類PhoneWindow類中superDispatchTouchEvent()方法的具體實現
@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
return mDecor.superDispatchTouchEvent(event);
}
顯然,這裡有調用了 mDecor.superDispatchTouchEvent (event),mDecor 是一個 PhoneWindow.DecorView 對象,這也是窗口的頂層視圖。它是一個真正 Activity 的 root view,它繼承了 FrameLayout,通過 super.dispatchTouchEvent,會把 touchevent 派發給各個 activity 的子 view,也就是我們在 Activity.onCreat 方法中 setContentView 時設置的 view,代碼參考如下:
//DecorView類聲明
private final class DecorView extends FrameLayout implements RootViewSurfaceTaker {
...
public boolean superDispatchTouchEvent(MotionEvent event) {
//這裡又調用了FrameLayout中的dispatchTouchEvent方法
return super.dispatchTouchEvent(event);
}
...
}
由於在 FrameLayout 並沒有重寫 dispatchTouchEvent (event) 方法,因此我們需要 FrameLayout 的父類也就是 ViewGroup 中該方法的實現,由該方法進行事件的具體分發,這裡具體事件分發過程有待研究。
事件處理#
Activity 中的事件處理方法:onTouchEvent (),其源碼如下:
//事件處理,默認返回false
public boolean onTouchEvent(MotionEvent event) {
if (mWindow.shouldCloseOnTouch(this, event)) {
finish();
return true;
}
//默認返回false
return false;
}
由於對於 onTouchEvent () 方法來說,事件傳遞是向父控件傳遞的,即使返回 false,事件也相當於被消費了。
注意:Activity 進行事件分發時,只有 return super.dispatchTouchEvent (ev) 時,事件才繼續向下傳遞,返回 true 或 false 都事件就被消費了,也就是終止了事件的傳播。
ViewGroup#
ViewGroup 中主要三個與事件傳遞相關的方法:dispatchTouchEvent ()、onInterceptTouchEvent () 和 onTouchEvent (),具體如下:
事件分發#
ViewGroup 中的事件分發方法:dispatchTouchEvent (),其源碼如下:
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
...
//dispatchTouchEvent()方法默認返回false
boolean handled = false;
//給方法決定是否攔截事件的分發
onInterceptTouchEvent(ev);
...
//默認情況下canceled和intercepted為false
if (!canceled && !intercepted) {
...
//該方法將事件傳遞給子View
dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
...
}
return handled;
}
這個方法的作用就是遍歷 ViewGroup 中的子 View, 將事件 (ACTION_DOWN) 交由子 View 進行處理,裡面主要調用了 onInterceptTouchEvent () 和 dispatchTransformedTouchEvent () 方法,onInterceptTouchEvent () 默認返回 false,下面是 dispatchTransformedTouchEvent () 方法的主要源碼如下:
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
View child, int desiredPointerIdBits){
...
if (child == null) {
handled = super.dispatchTouchEvent(event);
} else {
//進行子 View 的事件分發
handled = child.dispatchTouchEvent(event);
}
...
return handled;
}
顯然,dispatchTransformedTouchEvent () 方法主要進行子 View 的事件分發,如果沒有子 View,則調用父 View 的 dispatchTouchEvent (event) 方法。
事件攔截#
ViewGroup 中的事件分發方法 onInterceptTouchEvent () ,其源碼如下:
public boolean onInterceptTouchEvent(MotionEvent ev) {
...
//默認返回false
return false;
}
該方法默認返回 false,表示不攔截向子 View 的事件分發,該方法在 ViewGroup 的 dispatchTouchEvent () 方法中被調用。
事件處理#
ViewGroup 中沒有自己的 onTouchEvent () 事件處理方法,ViewGroup 繼承 View,其事件處理方法就是 View 的事件處理方法,其方法如下:
public boolean onTouchEvent(MotionEvent event) {
...
//默認返回 false
return false;
}
該方法進行相關事件的處理,如果返回 true , 表示事件被處理,具體使用情況將在下文中記錄。
View#
View 中主要兩個與事件分發相關的方法:dispatchTouchEvent () 和 onTouchEvent () 方法,具體如下:
事件分發#
View 中的事件分發方法是 dispatchTouchEvent (),主要代碼如下:
public boolean dispatchTouchEvent(MotionEvent event) {
...
//默認返回值為false
boolean result = false;
...
if (onFilterTouchEventForSecurity(event)) {
...
//調用了onTouchEvent方法
if (!result && onTouchEvent(event)) {
result = true;
}
}
...
return result;
}
上述代碼中,dispatchTouchEvent () 方法的默認返回值是 false,表示事件繼續分發,實際上 dispatchTouchEvent 方法的返回值與 onTouchEvent 方法的返回值有關,如果 onTouchEvent 返回 true,dispatchTouchEvent 的返回值 result 為 true,此時表示事件已消費,當然也可以這樣理解,ontouchEvent 的值為 true,本身就表示事件已消費了,下面是執行 ontouchEvent 方法執行的條件:
public boolean onFilterTouchEventForSecurity(MotionEvent event) {
//noinspection RedundantIfStatement
if ((mViewFlags & FILTER_TOUCHES_WHEN_OBSCURED) != 0
&& (event.getFlags() & MotionEvent.FLAG_WINDOW_IS_OBSCURED) != 0) {
//窗口被遮擋,丟棄touch事件(很少執行)
return false;
}
return true;
}
顯然,上述方法在一般情況下肯定返回 true,故肯定能執行到 onTouchEvent 方法,調用 View 的 dispatchTouchEvent 方法實際上可簡寫為如下代碼:
public boolean dispatchTouchEvent(MotionEvent event) {
...
//默認返回值為false
boolean result = false;
...
return onTouchEvent(event)
}
事件處理#
View 中的事件分發方法是 onTouchEvent (),主要代碼如下:
public boolean onTouchEvent(MotionEvent event) {
...
//此處返回true的條件是TouchDelegate默認為空,該值主要是關於View的觸摸區域的
if (mTouchDelegate != null) {
if (mTouchDelegate.onTouchEvent(event)) {
return true;
}
}
//如果設置了點擊事件該條件才會返回true,也就是事件消費咯
if (((viewFlags & CLICKABLE) == CLICKABLE ||
(viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) ||
(viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE) {
switch (action) {
//各個事件的處理
...
}
return true;
}
//默認返回false,表示不消費事件
return false;
}
事件傳至子 View 時有兩種結果,要麼當前 View 消費該事件,要麼不消費時間向上回傳,如果不攔截直至 Activity,也不做任何處理,最後將丟棄該事件。
這篇文章是 Android 事件分發機制第二篇,主要記錄了 Activity、ViewGroup 和 View 中與事件相關的方法,也就是 dispatchTouchEvent ()、onTouchEvent () 和 onInterceptTouchEvent () 方法在 Activity、ViewGroup 和 View 中的不同表現,從代碼的角度理解了一下 Android 中的事件關係,關於事件的分發流程將具體在下一篇文章中講述。