View的事件分發_ACTION_MOVE的坑

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,其實也是逼不得已。

 

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章