0,,參考
Android 8.0.0 源碼
1,問題
通常 在dispatchTouchEvent或onTouchEvent的方法 返回 true時,當前的View 就會按照 「ACTION_DOWN -> ACTION_MOVE -> ACTION_MOVE -> 無數個ACTION_MOVE -> ACTION_CANCEL 或 ACTION_UP」的順序執行
那麼,ACTION_MOVE的觸發,是在View中循環觸發的,還是通過 DecorView 分發下來的???
2,測試方案
public class BView extends LinearLayout {
public BView(Context context) {
super(context);
}
public BView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
private int start_b = 8;
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_MOVE) {
if (start_b-- > 0) {
int a = 10; // 啥也不敢,只是佔個位置
} else {
LogUtil.v("start debug"); // 從這裏開始debug, 就可以看到調用鏈,從哪裏來的
}
}
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
boolean defaultResult = super.onTouchEvent(event);
LogUtil.v("defaultResult = " + defaultResult);
return true; // 返回true,當前View處理事件,纔會出現Action_Move
}
}
如上圖,我們在 dispatchTouchEvent中,加入一個常量去控制。然後,在常量小於0的時候,debug一個斷點。這樣,就可以排除其它的調用。而是專注於看 ACTION_MOVE 觸發的來源。到底是,內部View的遞歸,還是 DecorView的dispatch
測試結果:
ACTION_MOVE的調用鏈 是 DecorView中的dispatch,調用鏈往上找依次是:
BView.dispatchTouchEvent()
LinearLayout.dispatchTouchEvent()
LinearLayout.dispatchTransformedTouchEvent()
FrameLayout.dispatchTouchEvent() // 這個就是熟悉的 android.R.id.content
FrameLayout.dispatchTransformedTouchEvent()
LinearLayout.dispatchTouchEvent()
LinearLayout.dispatchTransformedTouchEvent()
DecorView.dispatchTouchEvent() // 熟悉的DecorView
DecorView.superDispatchTouchEvent()
PhoneWindows.superDispatchTouchEvent() // 熟悉的PhoneWindows
Activity.dispatchTouchEvent() // 熟悉的Activity
DecorView.dispatchTouchEvent()
DecorView.dispatchPointerEvent()
ViewRootImpl.processPointerEvent() // 熟悉的ViewRootImpl
ViewRootImpl.processPointerEvent()
ViewRootImpl.deliver()
ViewRootImpl.onDeliverToNext() // 其實,一般到這裏就算結束了,不過還是多貼一些。
...
Choreographer.doCallbacks
Choreographer.doFrame
Choreographer.FrameDisplayEventReceiver.run // 熟悉的人都知道,這裏是View繪製的源頭
Handler.handleCallback
Handler.dispatchMessage // 這裏就是「同步屏障」的分發位置,和一般的Handler也很相似
Looper.loop
ActivityThread.main // 這裏就是,Java主線程入口了
RuntimeInit.run
ZygoteInit.main // 這裏就是程序的開始了,到這裏就徹底沒當前進程的調用鏈了
結論:ACTION_DOWN、ACTION_MOVE、ACTION_UP也好,所有的事件都是從ViewRootImpl那裏來的,理解成DecorView開始分發,也是可以的。
題外話 + 閒聊:
帶着這個結論,再去看ViewGroup.java 中的 dispatchTouchEvent 的實現,你會發現 裏面也是 利用全局變量,去記錄狀態。然後,處理不同的MotionEvent的不同情況,例如:mFirstTouchTarget。
這裏和我們平時寫代碼基本沒啥兩樣。
這裏也在想,其實ViewRootImpl 要求在原始線程中繪製View,其實也是逼不得已。