1. 程式人生 > >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 三個方法。

圖片案例

  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
回撥來判斷是否要攔截事件,而是直接把事件繼續傳下來。