Android Avtivity setContentView源码分析

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/immrwk/article/details/82381191

前言

平时我们开发过程中,给Activity设置布局的时候,直接调用的setContentView,然后我们在xml布局文件中写的布局就可以显示出来,那这个过程到底是怎么样的呢,今天我们来分析一下,setContentView都做了什么。

Activity setContentView

首先我们看Activity的setContentView方法,这个方法有几个重载:

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

public void setContentView(View view) {
    getWindow().setContentView(view);
    initWindowDecorActionBar();
}

public void setContentView(View view, ViewGroup.LayoutParams params) {
    getWindow().setContentView(view, params);
    initWindowDecorActionBar();
}

第三个方法是在第二个方法的基础上加了ViewGroup.LayoutParams的参数,我们重点看第一个和第三个方法就好,这个两个方法共做了两件事,一是调用了window的setContentView方法,第二个是执行了initWindowDecorActionBar
我们知道window是一个抽象类,他的实现类是PhoneWindow,window是在Activity的attach()中初始化为PhoneWindow的

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 voic
        Window window) {
    attachBaseContext(context);
    mFragments.attachHost(null /*parent*/);
    //初始化window
    mWindow = new PhoneWindow(this, window);
    mWindow.setWindowControllerCallback(this);
    mWindow.setCallback(this);
    mWindow.setOnWindowDismissedCallback(this);
...

}

PhoneWindow setContentView

接下来我们看看PhoneWindow的setContentView方法做了什么,主要有两个方法:

1. 参数为layoutResId

@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 {
        // 将layoutResID对应的布局文件inflate到父布局mContentParent上
        mLayoutInflater.inflate(layoutResID, mContentParent);
    }
    mContentParent.requestApplyInsets();
    final Callback cb = getCallback();
    if (cb != null && !isDestroyed()) {
        cb.onContentChanged();
    }
    mContentParentExplicitlySet = true;
}

首先判断mContentParent是否为空,为空则执行installDecor();

installDecor()的内容很长,我们挑重点来看

private void installDecor() {
    mForceDecorInstall = false;
    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);
    }
    if (mContentParent == null) {
        mContentParent = generateLayout(mDecor);
    }
    ...
}

首先调用的是generateDecor(-1)生成了一个DecorView

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());
}

然后第13、14行的时候执行generateLayout()

protected ViewGroup generateLayout(DecorView decor) {
    // Apply data from current theme.
    // 用TypedArray获取窗口的属性进行相关设置
    TypedArray a = getWindowStyle();

    ...

    // Inflate the window decor.
    //根据设定好的features值选择不同的窗口修饰布局文件,得到layoutResource值
    int layoutResource;
    int features = getLocalFeatures();

    ...

    // 将layoutResource添加进Decor对象中
    mDecor.startChanging();
    mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);

    // 获取contentParent
    ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
    if (contentParent == null) {
        throw new RuntimeException("Window couldn't find content container view");
    }
    ...
    return contentParent;
}

由此可见,installDecor方法主要就是为我们生成了mDecor和mContentParent对象。

下面的hasFeature(FEATURE_CONTENT_TRANSITIONS)是判断有没有设置过度动画,如果有,则启用过度动画来将布局添加到mContentParent中,否则直接将ayoutResID对应的布局文件inflate到父布局mContentParent上。

2. 参数为view和LayoutParams

@Override
public void setContentView(View view, ViewGroup.LayoutParams params) {
    // 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)) {
        view.setLayoutParams(params);
        final Scene newScene = new Scene(mContentParent, view);
        transitionTo(newScene);
    } else {
        mContentParent.addView(view, params);
    }
    mContentParent.requestApplyInsets();
    final Callback cb = getCallback();
    if (cb != null && !isDestroyed()) {
        cb.onContentChanged();
    }
    mContentParentExplicitlySet = true;
}

这个方法和上面的大体相同,主要区别在于不用inflate resId,而是直接通过addView将布局view添加到mContentParent中。

最后通过一张图,来简单展示下一个Activity布局大致逻辑:

这里写图片描述

猜你喜欢

转载自blog.csdn.net/immrwk/article/details/82381191
今日推荐