setContentView做了什么

本文详细介绍了Android中Activity的setContentView方法的工作原理,从创建DecorView到加载布局资源,再到将用户XML布局添加到内容视图的过程。通过分析PhoneWindow和DecorView的源码,揭示了界面与Java代码如何关联,帮助理解Android UI系统内部机制。
摘要由CSDN通过智能技术生成

Activity是Android系统的一个展示型组件,负责向用户展示一个界面并且可以接收输入事件并进行处理。在开发应用时,我们会将要展示的界面写在xml里,在java代码里去处理自己的业务逻辑,那就有个疑问xml跟我们的java代码是怎么关联起来的呢,这就必须要看看onCreate的setContentView做了些什么。

/*Activity.java*/
public void setContentView(@LayoutRes int layoutResID) {
    getWindow().setContentView(layoutResID);
    initWindowDecorActionBar();
}

getWindow()获取到的是Activity持有的PhoneWindow对象mWindow,Activity将setContentView的任务交给了PhoneWinedow。

/*PhoneWindow.java*/
@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();
    }
    mContentParentExplicitlySet = true;
}

这里的mContentParent是我们的xml(如activity_main.xml)里的根布局(最外层的layout)的父View

/*PhoneWindow.java*/
private void installDecor() {
    mForceDecorInstall = false;
    if (mDecor == null) {
        mDecor = generateDecor(-1);
        ...
    } 
    ...
    if (mContentParent == null) {
        mContentParent = generateLayout(mDecor);
        ...
    }
    ...
}

mDecor是PhoneWindow持有的DecorView对象,DecorView继承了FrameLayout,是PhoneWindow持有的View树的根。我们自己写的xml(如activity_main.xml)的布局是这个View树的一部分,而非全部,比如我们用AndroidStudio新建一个Activity,发现除了我们自己xml写的view外还有标题栏等。这里用一个图来描述DecorView,如下图所示:

DecorView.png

 接着看generateDecor(-1)和generateLayout(mDecor)

/*PhoneWindow.java*/
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, this);
            if (mTheme != -1) {
                context.setTheme(mTheme);
            }
        }
    } else {
        context = getContext();
    }
    return new DecorView(context, featureId, this, getAttributes());
}
/*PhoneWindow.java*/
protected ViewGroup generateLayout(DecorView decor) {
    ...
    // Inflate the window decor.
    int layoutResource;
    ...
    layoutResource = R.layout.xxx;
    ...
    mDecor.startChanging();
    mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
    ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
    ...
    return contentParent;
    }
}

可以看出generateDecor是new了一个DecorView对象,generateLayout会根据窗口的features确定布局(DecorView.png中的R.id.decor_content_parent),contentParent即是R.id.content。

具体看一下mDecor.onResourcesLoaded(mLayoutInflater, layoutResource),这里会将前面根据Features确定的布局(R.id.decor_content_parent)添加到DecorView。

/*DecorView.java*/
void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
    ...
    final View root = inflater.inflate(layoutResource, null);
    ...
    addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
    ...
    mContentRoot = (ViewGroup) root;
    ...
}

installDecor结束后,才会通过mLayoutInflater.inflate(layoutResID, mContentParent);将我们自己xml的view加载到它的父ViewR.id.content中。

总体来看,setContentView做了这么几件事:

<1>new了DecorView对象

<2>根据Features等确定DecorView下一级的子布局(我画的图里的R.id.decor_content_parent)

<3>将上面的子布局(xml)加载到DecorView对象中

<4>将我们自己的xml(如activity_main.xml)加载到它的父View(R.id.content)中。

用一句话说,就是setContentView就是一个解析xml,构建PhoneWindow持有的mDecorView的过程。

最后,本文如有错误之处,欢迎评论区留言指正!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

安卓夜问

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值