Activity#setContentView
当我们在Activity中使用setContentView的时候,其实非常简单的交给了Window去处理,在Android中,Window的子类只有一个PhoneWindow
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
PhoneWindow#setContentView
我们的xml文件在变成可见的控件之前,系统会调用installDecor()
方法初始化一个DecorView作为整个Activity的根布局
@Override
public void setContentView(int layoutResID) {
//检查是否存在根布局,最开始的时候为null
if (mContentParent == null) {
//创建DecorView
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 {
//使用LayoutInflater将xml转换为View,并添加到我们的根布局中,这里就将我们的布局添加到Activity上了
mLayoutInflater.inflate(layoutResID, mContentParent);
}
mContentParent.requestApplyInsets();
final Window.Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
mContentParentExplicitlySet = true;
}
DecorView的初始化installDecor
generateDecor()
方法中new出我们的DecorView以后,则是填充系统布局了,这一步在generateLayout中完成
private void installDecor() {
mForceDecorInstall = false;
if (mDecor == null) {
//new DecorView
mDecor = generateDecor(-1);
....
} else {
mDecor.setWindow(this);
}
if (mContentParent == null) {
//往DecorView里面填充系统的布局
mContentParent = generateLayout(mDecor);
....
}
}
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#generateLayout
可以看到,首先根据各种Window Flag设置不同的属性,这也是为什么requestFeature必须在setContentView之前调用的原因,然后则是系统根据不同的flag去加载各种不同的默认布局文件,这些文件中,都有一个id为Window#ID_ANDROID_CONTENT的ViewGroup,这个布局则是用来填充我们的布局文件啦~~~,回到PhoneWindow#setContentView中,mContentParent则是这个ViewGroup,
protected ViewGroup generateLayout(DecorView decor) {
//首先设置各种Window Flag
...
if (a.getBoolean(R.styleable.Window_windowNoTitle, false)) {
requestFeature(FEATURE_NO_TITLE);
} else if (a.getBoolean(R.styleable.Window_windowActionBar, false)) {
// Don't allow an action bar if there is no title.
requestFeature(FEATURE_ACTION_BAR);
}
...
//然后根据不同的Flat加载不同的系统默认布局
int layoutResource;
int features = getLocalFeatures();
// System.out.println("Features: 0x" + Integer.toHexString(features));
if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {
layoutResource = R.layout.screen_title_icons;
}
....
//将系统默认布局添加到Docorview上面
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
//id为ID_ANDROID_CONTENT的ViewGroup则用来包裹我们的布局
ViewGroup contentParent = (ViewGroup) findViewById(ID_ANDROID_CONTENT);
return contentParent;
}