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,如下图所示:
接着看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的过程。
最后,本文如有错误之处,欢迎评论区留言指正!