Android setContentView的過程

我們在繼承Activity、實現onCreate方法的時候,寫下的第一行代碼可能就是去實現setContentView這個方法了。那么setContentView具體是怎么實現的呢,今天我們來分析一下setContentView的實現過程。

setContentView的具體流程

/**
 * Set the activity content from a layout resource.  The resource will be
 * inflated, adding all top-level views to the activity.
 *
 * @param layoutResID Resource ID to be inflated.
 *
 * @see #setContentView(android.view.View)
 * @see #setContentView(android.view.View, android.view.ViewGroup.LayoutParams)
 */
public void setContentView(@LayoutRes int layoutResID) {
    getWindow().setContentView(layoutResID);
    initWindowDecorActionBar();
}

通過上面的方法我們可以看到三個方法,分別是getWindow()、getWindow().setContentView(layoutResID)、還有initWindowDecorActionBar()。我們一個一個看,在getWindow()方法中

/**
 * Retrieve the current {@link android.view.Window} for the activity.
 * This can be used to directly access parts of the Window API that
 * are not available through Activity/Screen.
 *
 * @return Window The current window, or null if the activity is not
 *         visual.
 */
public Window getWindow() {
    return mWindow;
}

getWindow()方法中返回了一個Window對象,但是在這個方法中沒有體現Window對象實在什么時候創建的。我們通過搜索Activity中的mWindow對象可以看到在Activity的attach方法中實例化了mWindow對象。

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,
            Window window) {
        attachBaseContext(context);
        mFragments.attachHost(null /*parent*/);
        mWindow = new PhoneWindow(this, window);
        mWindow.setWindowControllerCallback(this);
        mWindow.setCallback(this);
        mWindow.setOnWindowDismissedCallback(this);
        mWindow.getLayoutInflater().setPrivateFactory(this);
        if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
            mWindow.setSoftInputMode(info.softInputMode);
        }
        if (info.uiOptions != 0) {
            mWindow.setUiOptions(info.uiOptions);
        }
        mWindow.setWindowManager(
                (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
                mToken, mComponent.flattenToString(),
                (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
        if (mParent != null) {
            mWindow.setContainer(mParent.getWindow());
        }

拿到mWindow對象之后調用了mWindow.setContentView的方法,點進去我們看到的是Window這個抽象類,并沒有看到具體的實現內容,在attach方法中我們看到的是mWindow = new PhoneWindow(this, window),其實PhoneWindow是Window類的實現類。我們到PhoneWindow中去查看setContentView方法。

    @Override
    public void setContentView(int layoutResID) {
        // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
        // decor, when theme attributes and the like are crystalized. Do not check the feature
        // before this happens.
        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();
        }
        mContentParentExplicitlySet = true;
    }

首先mContentParent是一個ViewGroup,我們先對mContentParent進行判空處理,如果為空那么我們需要獲取DecorView,如果mContentParent不為空,那么移除當前ViewGroup的所有子View。然后添加我們指定的XML文件,通過Callback回調來更新視圖。上面還有三處可能大家不是很清楚。第一個是mLayoutInflater從哪里來的,第二是Callback回調到哪里去了,第三個是 installDecor()方法做了什么。

    public PhoneWindow(Context context) {
        super(context);
        mLayoutInflater = LayoutInflater.from(context);
    }

mLayoutInflater再PhoneWindow實例化的時候被賦值,調用了 LayoutInflater.from()方法。

    public static LayoutInflater from(Context context) {
        LayoutInflater LayoutInflater =
                (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        if (LayoutInflater == null) {
            throw new AssertionError("LayoutInflater not found.");
        }
        return LayoutInflater;
    }

getCallback()方法對應的setCallback()方法是在Activity的attach方法中設置的??吹竭@里可以看出回調的位置為Activity。

installDecor()方法做了些什么

if (mDecor == null) {
            mDecor = generateDecor(-1);
            mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
            mDecor.setIsRootNamespace(true);
            if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
                mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
            }
        } else {
            mDecor.setWindow(this);
        }

    protected DecorView generateDecor(int featureId) {
        // System process doesn't have application context and in that case we need to directly use
        // the context we have. Otherwise we want the application context, so we don't cling to the
        // activity.
        Context context;
        if (mUseDecorContext) {
            Context applicationContext = getContext().getApplicationContext();
            if (applicationContext == null) {
                context = getContext();
            } else {
                context = new DecorContext(applicationContext, getContext().getResources());
                if (mTheme != -1) {
                    context.setTheme(mTheme);
                }
            }
        } else {
            context = getContext();
        }
        return new DecorView(context, featureId, this, getAttributes());
    }

mDecor是一個DecorView的對象,我們先去判斷mDecor是否為空,如果為空,那么回去創建一個mDecor對象,不為空則添加在Window中。

至此,整個setContentView的方法所走的流程就走完了。從這個流程中我們可以看出,跟View有相關的層級都是以Activity為基礎的。
Activity>PhoneWindow>DecorView>ViewGroup>View

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容