在Activity中加载布局的时候,我们都知道调用的是setContentView方法,那么具体是如何实现的呢?
本文基于参考其他人博客以及自己翻阅源码做一个记录。
随便找到一个Activity,点击setContentView
方法可以看到实现:
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);//1
initWindowDecorActionBar();//2
}
layoutResID
是我们传入的布局id,所以我们先主要关注1处。可以看到这里调用了getWindow().setContentView
,点进去getWindow
看一下:
public Window getWindow() {
return mWindow;
}
获取的是一个Window
对象,找到Window
类,它是一个抽象类,在类注释中我们可以看到下面一段:
The only existing implementation of this abstract class is
* android.view.PhoneWindow, which you should instantiate when needing a
* Window.
可以看到,Window
唯一的实现类就是PhoneWindow
。再点进去PhoneWindow
的setContentView
方法:
public class PhoneWindow extends Window implements MenuBuilder.Callback {
//...
//...
//...
@Override
public void setContentView(int layoutResID) {
if (mContentParent == null) {
installDecor();
} else {
mContentParent.removeAllViews();
}
mLayoutInflater.inflate(layoutResID, mContentParent);
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
}
@Override
public void setContentView(View view) {
setContentView(view, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
}
@Override
public void setContentView(View view, ViewGroup.LayoutParams params) {
if (mContentParent == null) {
installDecor();
} else {
mContentParent.removeAllViews();
}
mContentParent.addView(view, params);
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
}
//...
//...
//...
}
这里我们关注setContentView(int layoutResID)实现。
当mContentParent
不为空时,会先移除其上所以子view,然后通过mLayoutInflater.inflate(layoutResID, mContentParent)
将我们的布局文件加载上去,当mContentParent
为空时,会先调用installDecor()
方法,click it:
private void installDecor() {
if (mDecor == null) {
mDecor = generateDecor();
//...
}
}
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);
//...
}
}
}
}
只看主流程代码,逻辑很清晰,mContentParent
为空的时候,会执行generateLayout()
方法,同时需要传入一个mDecor
,那么mDecor
是什么东西呢,我们可以点进generateDecor
方法看一下:
protected DecorView generateDecor() {
return new DecorView(getContext(), -1);
}
嘿,又出来了一个DecorView
,这是个什么东西呢?查看它的源码:
private final class DecorView extends FrameLayout {}
可以看到,DecorView
继承FrameLayout
,是一个ViewGroup
。回到前面的generateLayout(mDecor)
,看一下我们往DecorView
里都有什么东西:
public class PhoneWindow extends Window implements MenuBuilder.Callback {
protected ViewGroup generateLayout(DecorView decor) {
// 此处省去一堆代码,设置窗口属性
int layoutResource;
// 此处省去一堆代码,根据不同的主题使用不同的布局资源
// 这里才是重点,向 DecorView 添加布局,并且从 DecorView 中查找出 contentParent
View in = mLayoutInflater.inflate(layoutResource, null);
decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
if (contentParent == null) {
throw new RuntimeException("Window couldn't find content container view");
}
// 此处省去一堆代码设置窗口背景和标题
return contentParent;
}
}
在这个方法里,我们contentParent
其实指向的就是ID_ANDROID_CONTENT
,也就是R.id.content
。到目前为止,我们可以用一张图来概括上边儿的流程:
找到mContentParent
之后,回到setContentView
方法中:
// 解析我们设置的布局资源并且设置 mContentParent 为父布局
mLayoutInflater.inflate(layoutResID, mContentParent);
至此,我们自己的布局就加载在了Activity上,