Android 事件分發

不詳細介紹事件分發的流程和邏輯。

這裏關注一下activity view對於touch/key事件接受處理的先後順序。


既然是activity和view的事件接受順序,那麼只考慮dispatchTouchEvent和onTouchEvent;

直接看結果,然後再看why(你總的先看一眼妹子好不好看,然後在去提親不是)

結果是:

1.dispatchTouchEvent:activity先響應,view後響應;

2.onTouchEvent:activity後響應,view先響應;

注意關鍵字‘先後’。


再看看why?

簡單的,demo打堆棧,截取部分:

at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2657)
        at com.android.internal.policy.DecorView.superDispatchTouchEvent(DecorView.java:444)
        at com.android.internal.policy.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1829)
        at android.app.Activity.dispatchTouchEvent(Activity.java:3394)
        at com.shixin398.myapplication.MainActivity.dispatchTouchEvent(MainActivity.java:31)
        at android.support.v7.view.WindowCallbackWrapper.dispatchTouchEvent(WindowCallbackWrapper.java:68)
        at com.android.internal.policy.DecorView.dispatchTouchEvent(DecorView.java:402)
        at android.view.View.dispatchPointerEvent(View.java:12567)
        at android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent(ViewRootImpl.java:5031)
        at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:4834)

viewroot中processPointerEvent可能大部分人很陌生,沒關係,不需要care;知道這個是進行touch事件派發的最早期入口就ok。

(搞事情,不要妄圖一下啥都搞明白,慢慢來)

隨後走到view中的dispatchPointerEvent,這個view實際是decorview,會走到decorview的dispatchTouchEvent。這個函數就比較熟悉了,經常需要複寫的一個函數。

12062    public final boolean dispatchPointerEvent(MotionEvent event) {
12063        if (sDebugDispatchInput) {
12064            Log.d(VIEW_LOG_TAG, "dispatchPointerEvent event is TouchEvent ? "
12065                    + event.isTouchEvent() + " ,event: " + event);
12066        }
12067        if (event.isTouchEvent()) {
12068            return dispatchTouchEvent(event);//當前view是decorview,多態麼,就走decorview的方法
12069        } else {
12070            return dispatchGenericMotionEvent(event);
12071        }
12072    }

到decoreview就看下源碼:

394    @Override
395    public boolean dispatchTouchEvent(MotionEvent ev) {
396        final Window.Callback cb = mWindow.getCallback();
397        return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
398                ? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev);
399    }

一、dispatchTouchEvent:activity先響應,view後響應

window.callback,關於activity和窗口的關係,現在也不需要詳細瞭解,知道這個callback對應當前的activity就ok。

那麼就走到activity的dispatchTouchEvent了。

所以1點的邏輯就是剛纔講述的邏輯了:1.dispatchTouchEvent:activity先響應,view後響應;

如果activity攔截了事件,則decorview中的view不會收到touch事件。若不攔截,則會繼續派發。

這個繼續派發,也會看到2的邏輯分析,如下。


二、onTouchEvent:activity後響應,view先響應

如果activity沒有攔截,那麼會派發的viewgroup。看下acitivity code便知:

3396    public boolean dispatchTouchEvent(MotionEvent ev) {
3397        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
3398            onUserInteraction();
3399        }
3400        if (getWindow().superDispatchTouchEvent(ev)) {//通過getwindow來把事件給窗口,window在給decorview,decorview就給activity裏的控件了(viewgroup/view)
3401            return true;
3402        }
3403        return onTouchEvent(ev);//最後是onTouchEvent,論證了2點
3404    }
代碼中:
if (getWindow().superDispatchTouchEvent(ev))

getwindow拿到的是phonewidnow,代碼如下:

1827    @Override
1828    public boolean superDispatchTouchEvent(MotionEvent event) {
1829        return mDecor.superDispatchTouchEvent(event);
1830    }

然後是decorview的,又看到熟悉的dispatchTouchEvent了:

474    public boolean superDispatchTouchEvent(MotionEvent event) {
475        return super.dispatchTouchEvent(event);
476    }
public class DecorView extends FrameLayout
public class FrameLayout extends ViewGroup 
調用super的,而super是framelayout,裏面沒有複寫dispatchTouchEvent,自然也就調用viewgroup中的dispatch了。


到viewgroup裏就不在詳細講述了,這個事件鏈很麻煩,真心不想寫。

知道viewgroup這裏dispatchtouchevent 經過層層遍歷遞歸,會最終調用到具體的view的OnTouchEvent就ok了。(可以看下任玉剛的書中寫的這部分,或者csdn一下也ok)。


到此,知道通過activity的下面邏輯,走到viewgroup的dispatchTouchevent,隨後遞歸派發touch事件。如果沒有沒有view響應,也就是return false了。

getWindow().superDispatchTouchEvent(ev)

纔會執行activity的onTouchEvent。所以dispatchTouchEvent:activity先響應,view後響應;onTouchEvent:activity後響應,view先響應;

關鍵函數:activity的dispatchTouchEvent,先if最後return onTouchEvent。

3396    public boolean dispatchTouchEvent(MotionEvent ev) {
3397        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
3398            onUserInteraction();
3399        }
3400        if (getWindow().superDispatchTouchEvent(ev)) {//通過getwindow來把事件給窗口,window在給decorview,decorview就給activity裏的控件了(viewgroup/view)
3401            return true;
3402        }
3403        return onTouchEvent(ev);//最後是onTouchEvent,論證了2點
3404    }


至此,結束。

只分析了dispatch 和onTouchEvent 在acitivity和view之間,調用的先後順序,以便在進行事件處理時,可以合理的在自定義view和acitivity之間進行事件的攔截等操作。




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