Android View相關-事件分發機制流程

經過前兩篇的學習,相信已經對事件分發的一個流程產生了一定的理解(纔怪),那麼這一章我們來看看整個事件分發從Activity到被消費的過程。好了,腦袋放空,我們開始。

Activity中的View結構

我們在這裏先插一嘴Activity中的View結構。在開發過程中,我們通常使用setContentView來設置Activity的視圖,Android中可見頁面都是附着於窗口也就是Window上的,而DecorView是Window的最頂層視圖。在Android Framework層,與窗口處理有關的類,主要是Window類和其實現類PhoneWindow,DecorView則是PhoneWindow的內部類,繼承了FrameLayout。

//PhoneWindow.java
public class PhoneWindow extends Window implements MenuBuilder.Callback {
}
private final class DecorView extends FrameLayout implements RootViewSurfaceTaker {
}

而我們調用setContentView的本質是將View添加至DecorView,可以看到Activity源碼中:

public void setContentView(@LayoutRes int layoutResID) {
    getWindow().setContentView(layoutResID);
    initWindowDecorActionBar();
}

最終是調用了Window的setContentView,我們知道Window的實現類是PhoneWindow,來看下PhoneWindow中的實現:

@Override
public void setContentView(int layoutResID) {
    if (mContentParent == null) {
        installDecor();
    } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
        mContentParent.removeAllViews();
    }
    if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
        final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,getContext());
        transitionTo(newScene);
    } else {
        mLayoutInflater.inflate(layoutResID, mContentParent);
    }
    mContentParent.requestApplyInsets();
    final Callback cb = getCallback();
    if (cb != null && !isDestroyed()) {
        cb.onContentChanged();
    }
}

可以看到在12行,將Activity的View添加到DecorView中(mContentParent是DecorView的一個子View,具體可以查看PhoneWindow的generateLayout方法)。我們可以將添加頁面至窗口的流程做一個總結:

那麼Activity視圖被添加到Window的過程我們已經搞明白了,當然這跟本章內容弄並沒有太大關係

不過多學一點東西還是有必要的,那麼我們開始今天的主題,事件分發的總流程。

事件分發流程

我們知道,事件分發最初是到達Activity,然後依次下發,大致上:

Activity ——> PhoneWindow ——> DecorView——> ViewGroup ——> … ——> View

是這樣一個流程,由於我們無法操作PhoneWindow以及DecorView,下面我們就來分析一下Activity、ViewGroup、View這三者的的流程,我們先來分析這三者與事件分發有關的方法:

  • Activity:dispatchTouchEvent()、onTouchEvent()
  • ViewGroup:dispatchTouchEvent()、onInterceptTouchEvent()、onTouchEvent()
  • View:dispatchTouchEvent()、onTouchEvent()

我們知道Activity中管理視圖的根View是DecorView,我們先從DecorView來看:

//DecorView
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    final Callback cb = getCallback();
    return cb != null && !isDestroyed() && mFeatureId < 0 ? cb.dispatchTouchEvent(ev)
        : super.dispatchTouchEvent(ev);
}
//Window
public final Callback getCallback() {
        return mCallback;
}

DecorView首先對mCallback進行了判斷,如果mCallback不爲空那麼調用mCallback的dispatchTouchEvent,我們來看看mCallback是怎麼進行賦值的:

//Window
public void setCallback(Callback callback) {
    mCallback = callback;
}
//Activity
final void attach(Context context, ActivityThread aThread,
                  Instrumentation instr, IBinder token, int ident,
                  Application application, Intent intent, ActivityInfo info,
                  CharSequence title, Activity parent, String id,
                  NonConfigurationInstances lastNonConfigurationInstances,
                  Configuration config, String referrer, IVoiceInteractor voiceInteractor) {
    ...
        mWindow = new PhoneWindow(this);
    mWindow.setCallback(this);
    mWindow.setOnWindowDismissedCallback(this);
    mWindow.getLayoutInflater().setPrivateFactory(this);
    ...
}

從上面代碼可以看出DecorView的dispathcTouchEvent最終使用的,仍是Activity的dispathcTouchEvent,而我們知道,DecorView是FrameLayout的子類,也是一個ViewGroup,我們回憶一下之前的ViewGroup以及View的事件分發執行流程,就可以很輕易的得出Activity、ViewGroup、View這三者事件分發的一個流程情況:

  • View消費事件:

  • ViewGroup消費事件:

  • Activity消費事件:

Activity的整個事件分發流程就是這樣了,這是一個Java當中典型的責任鏈模式,如果理解了這個設計模式,那麼對於理解Android事件分發機制會大有幫助。關於責任鏈模式,可以參考博文Java模式之責任鏈模式

好了,以上就是本篇文章的全部內容了,如果您對文章有任何疑問或者文章內有錯誤或者遺漏的部分,歡迎在評論區指正,謝謝觀看~

我的個人博客,歡迎訪問,交流~

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