本文主要讲述app如何在onCreate
方法中调用setContentView
来加载布局,对于想知道app从启动到执行setContentView
之间的执行逻辑的小伙伴可以查看Android加载流程解析(从app启动到执行onCreate)。
首先查看setContentView
方法
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID); //1
initWindowDecorActionBar(); //2
}
从名字可以看出,1执行了视图的加载,2初始化了ActionBar
,查看1,可以追踪到PhoneWindow
的setContentView
方法:
public void setContentView(int layoutResID) {
if (mContentParent == null) {
installDecor(); //1
}
...
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
...
} else {
mLayoutInflater.inflate(layoutResID, mContentParent); //2
}
...
}
从名字可以推断1执行了一些初始化的工作,2才是真正动态加载视图的操作,inflate动态加载layoutResID
到root
视图mContentParent
中,那么**这个mContentParent
如何来的,是一个什么样的视图?**留着这个疑问先继续往下查看1都做了什么工作:
private void installDecor() {
if (mDecor == null) {
mDecor = generateDecor(-1);
...
} else {
mDecor.setWindow(this);
}
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);
...
}
...
}
这里需要关注mDecor
和mContentParent
两个对象
// This is the top-level view of the window, containing the window decor.
private DecorView mDecor;
查看mDecor
的描述,可以看出这是window的顶层视图,注意DecorView
继承自FrameLayout
,也是一个ViewGroup
// This is the view in which the window contents are placed. It is either
// mDecor itself, or a child of mDecor where the contents go.
ViewGroup mContentParent;
查看mContentParent
的描述,可以看出这是视图存放的父类布局,可以是顶层布局mDecor
自己,也可以是下面的子视图
protected ViewGroup generateLayout(DecorView decor) {
...
int layoutResource;
// System.out.println("Features: 0x" + Integer.toHexString(features));
if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
layoutResource = R.layout.screen_swipe_dismiss;
setCloseOnSwipeEnabled(true);
} else if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {
...
}
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
...
return contentParent;
}
中间省略了很多layoutResource
的赋值逻辑,从上面可以看出来,mDecor
调用onResourcesLoaded
来展示顶层布局,查看onResourcesLoaded
void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
...
mDecorCaptionView = createDecorCaptionView(inflater);
final View root = inflater.inflate(layoutResource, null);
if (mDecorCaptionView != null) {
if (mDecorCaptionView.getParent() == null) {
addView(mDecorCaptionView,
new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
}
mDecorCaptionView.addView(root,
new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));
} else {
// Put it below the color views.
addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
}
mContentRoot = (ViewGroup) root;
...
}
该方法动态加载layoutResource
布局,同时将获得的view
(root)绑定到自身(DecorView
),mDecorCaptionView
也只是root
和DecorView
的中间层,本质上root
绑定到了DecorView
。
通过mDecor.onResourcesLoaded(mLayoutInflater, layoutResource)
,mDecor
渲染了基本布局,该布局中存在一个id为content
的FrameLayout
容器(这个容器就用来放置apk视图的区域,平时我们新建项目的main_acvitity
就是放在这里),如下图
执行完onResourcesLoaded
后,PhoneWindow
新建了contentParent
//ID_ANDROID_CONTENT: com.android.internal.R.id.content
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
到这恍然大悟了,原来这个contentParent
就是apk视图的父视图,最后generateLayout
返回该父视图并赋值给mContentParent
mContentParent = generateLayout(mDecor)
接下来就是对DecorView
中的一些默认视图做一些初始化操作,就不过多叙述了,跳出installDecor
,查看下一个关键点
mLayoutInflater.inflate(layoutResID, mContentParent)
到这就很明确了,前面的installDecor
就是确认了全局根视图,并做了初始化的操作,同时返回apk视图的位置mContentParent(ViewGroup)
,接下来的任务就是动态将apk的视图(layoutResID
)动态加载并绑定到对应的父容器中(mContentParent
)