android 事件分發機制 概念理解
阿新 • • 發佈:2019-01-11
android 事件分發機制
參考資料
原理
- 分發事件 的起始點:
從 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;
}
- 分發事件 的終點 也是 Activity 從原始碼可知
應為 分發的方法是 acticity 自己定義的方法
dispatchTouchEvent,所有最後只有他自己來處理該事件。
- 分發事件的過程
主要涉及 View 和 ViewGroup (在 xml 中 設定)
View 只有 onTouchEvent 和 dispatchTouchEvent 兩個方法。
ViewGroup 有 onTouchEvent / dispatchTouchEvent 和 onInterceptTouchEvent 三個方法。
- 注意事項:
View 或 ViewGroup 有兩個核心的行為:攔截(intercept) 和 消費(consume)。這兩者是相互獨立的,攔截不一定消費。是否要攔截看 onIntercepTouchEvent。是否要消費看 onTouchEvent。
- 方法理解
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 中
- 特殊情況 子 View –requestDisallowInterceptTouchEvent
比較特殊的情況有,子 View 可以使用 requestDisallowInterceptTouchEvent 影響去父 View
的分發,可以決定父 View 是否要呼叫 onInterceptTouchEvent
。比如,requestDisallowInterceptTouchEvent(true),父 View
就不用呼叫 onInterceptTouchEvent
來判斷攔截,而就是不攔截。
該方法可以用來解決手勢衝突。比如子 View
先消費了事件,但是後面父 View
也滿足了手勢觸發的條件而攔截事件,導致子 View
手勢執行一半後無法繼續響應。可以使用
requestDisallowInterceptTouchEvent(true),這樣後面的事
件,父 View 不會走 onInterceptTouchEvent
回撥來判斷是否要攔截事件,而是直接把事件繼續傳下來。