banner
jzman

jzman

Coding、思考、自觉。
github

Android事件分發源碼分析

PS:如果你覺得你在努力,那麼告訴我你努力的結果是什麼!

上篇文章中敘述了 Android 事件分發的大致流程,下面從 Activity、ViewGroup、View 三個方面介紹事件的相關方法,小節如下:

  1. Activity
  2. ViewGroup
  3. 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 中的事件關係,關於事件的分發流程將具體在下一篇文章中講述。

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。