Android事件分發之源碼分析

原文首發於微信公衆號:躬行之(jzman-blog),歡迎關注交流!java

Android事件分發

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

  1. Activity
  2. ViewGroup
  3. View

閱讀本篇文章以前請先閱讀:微信

Activity

Activity中主要兩個與事件傳遞相關的方法,dispatchTouchEvent() 和 onTouchEvent(),事件傳遞由 Activity 的 dispatchTouchEvent() 方法開始。ide

事件分發

Activity中的事件分發方法:dispatchTounchEvent(),其源碼以下:post

//事件分發
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,代碼參考以下:ui

//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 中該方法的實現,由該方法進行事件的具體分發,這裏具體事件分發過程有待研究。this

事件處理

Activity 中的事件處理方法:onTouchEvent(),其源碼以下:spa

//事件處理,默認返回false
public boolean onTouchEvent(MotionEvent event) {
        if (mWindow.shouldCloseOnTouch(this, event)) {
            finish();
            return true;
        }
        //默認返回false
        return false;
    }
複製代碼

因爲對於onTouchEvent()方法來講,事件傳遞是向父控件傳遞的,即便返回false,事件也至關於被消費了。code

注意: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 中的事件關係,關於事件的分發流程將具體在下一篇文章中講述,能夠回覆關鍵字【加羣】邀你入交流羣。我的微信公衆號:jzman-blog 能夠一塊兒交流學習!

相關文章
相關標籤/搜索