天天看點

android 事件分發機制 概念了解android 事件分發機制

android 事件分發機制

參考資料

Android 事件分發機制源碼和執行個體解析

Android View 事件分發機制詳解

圖解 Android 事件分發機制

圖解View的事件分發機制

原理

  1. 分發事件 的起始點:

從 Activity 開始,Activity 源碼

Activity 有兩個方法 dispatchTouchEvent 和 onTouchEvent

Activity—dispatchTouchEvent
public boolean dispatchTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            onUserInteraction();
        }
        if (getWindow().superDispatchTouchEvent(ev)) {
            return true;
        }
        return onTouchEvent(ev);
    }

    getWindow().superDispathTouchEvent 就是用來分發事件到
    DecorView 中。如果整個 ViewTree 沒有消費事件,會調用
    Activity 的 onTouchEvent。

源碼中: 
  protected void onUserLeaveHint() {
    }

           
Activity—onTouchEvent
public boolean onTouchEvent(MotionEvent event) {
        if (mWindow.shouldCloseOnTouch(this, event)) {
            finish();
            return true;
        }

        return false;
    }
           
  1. 分發事件 的終點 也是 Activity 從源碼可知

應為 分發的方法是 acticity 自己定義的方法

dispatchTouchEvent,所有最後隻有他自己來處理該事件。

  1. 分發事件的過程

主要涉及 View 和 ViewGroup (在 xml 中 設定)

View 隻有 onTouchEvent 和 dispatchTouchEvent 兩個方法。

ViewGroup 有 onTouchEvent / dispatchTouchEvent 和 onInterceptTouchEvent 三個方法。

android 事件分發機制 概念了解android 事件分發機制
  1. 注意事項:

View 或 ViewGroup 有兩個核心的行為:攔截(intercept) 和 消費(consume)。這兩者是互相獨立的,攔截不一定消費。是否要攔截看 onIntercepTouchEvent。是否要消費看 onTouchEvent。

  1. 方法了解
dispatchTouchEvent,該方法封裝了事件分發的整個過程。是事
件分發的 排程者 和 指揮官 。的核心過程均在該方法中。下面的
onInterceptTouchEvent 和 onTouchEvent
的回調的調用就在該方法體中。是否傳遞事件到
onInterceptTouchEvent 和 onTouchEvent 由
dispatchTouchEvent 決定。
           
onInterceptTouchEvent,該方法決定了是否攔截事件。隻有
ViewGroup 有該回調。傳回 true 表示攔截,傳回 false
表示不攔截。自定義 View
的時候,可以重載該方法,通過一些特定的邏輯來決定是否攔截
事件。如果攔截,接下來會調用該 ViewGroup 的 onTouchEvent
來處理事件。
           
onTouchEvent,該方法處理了事件,并決定是否繼續消費後續
事件。該方法調用的前置條件:

該 View 攔截了事件
子 View 都不消費事件
沒有子 View


該方法正式處理 MotionEvent。傳回 true 表示消費,傳回
false 不消費。如果消費,接下來的事件還會傳遞到該 View 的
dispatchTouchEvent 中;如果不消費,後面的事件不會再傳過來。

onTouchListener 的 onTouch 回調,和 onTouchEvent
一樣,優先級比 onTouchEvent 高,如果有設定該監聽,并且
onTouch 傳回 true,就不會再調用 onTouchEvent 了。如果傳回
false,事件還是會傳遞到 onTouchEvent 中。
           
dispatchTransformedTouchEvent 關鍵部分

// Perform any necessary transformations and dispatch.
if (child == null) {
    handled = super.dispatchTouchEvent(transformedEvent);
} else {
    final float offsetX = mScrollX - child.mLeft;
    final float offsetY = mScrollY - child.mTop;
    transformedEvent.offsetLocation(offsetX, offsetY);
    if (! child.hasIdentityMatrix()) {
        transformedEvent.transform(child.getInverseMatrix());
    }

    handled = child.dispatchTouchEvent(transformedEvent);
}

也就是 dispatchTransformTouchEvent 完成了分發的最後過程:

a. 傳入的 child 不為空,轉化坐标為 child 的坐标系,調用
child.dispatchTouchEvent 向 child 分發事件
b. 傳入的 child 為空,調用 super.dispatchTouchEvent
分發事件到 onTouchEvent 中

           
  1. 特殊情況 子 View –requestDisallowInterceptTouchEvent
比較特殊的情況有,子 View 可以使用 requestDisallowInterceptTouchEvent 影響去父 View
的分發,可以決定父 View 是否要調用 onInterceptTouchEvent
。比如,requestDisallowInterceptTouchEvent(true),父 View
就不用調用 onInterceptTouchEvent
來判斷攔截,而就是不攔截。

該方法可以用來解決手勢沖突。比如子 View
先消費了事件,但是後面父 View
也滿足了手勢觸發的條件而攔截事件,導緻子 View
手勢執行一半後無法繼續響應。可以使用
requestDisallowInterceptTouchEvent(true),這樣後面的事
件,父 View 不會走 onInterceptTouchEvent
回調來判斷是否要攔截事件,而是直接把事件繼續傳下來。