android setContentView源码分析

view是如何添加到屏幕上的?

从setContentView出发->

1
2
3
4
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}

可以看到调用的是getWindow的setContentView方法,

那么getWindow是啥呢?

1
2
3
public Window getWindow() {
return mWindow;d
}

接着看Window

1
2
3
4
5
6
7
 <p>The only existing implementation of this abstract class is
* android.view.PhoneWindow, which you should instantiate when needing a
* Window.
*/
public abstract class Window {
...
}

注意上面的注释,该类唯一的实现是phoneWindow,到这里我们就知道了这个的getWindow实质返回的是一个PhoneWindow的实例。

接着看PhoneWindow的setContentView方法

1
2
3
4
5
6
7
8
9
@Override
public void setContentView(int layoutResID) {
if (mContentParent == null) {
installDecor();
}
...
mLayoutInflater.inflate(layoutResID, mContentParent);
...
}

只需要关注上面的几行代码,如果mContentParent是空,那么调用installDecor方法(如下)。这里的mContentParent是什么呢?看定义的地方它是一个ViewGroup,它里面具体是啥呢?我们继续往下跟。

1
2
3
4
5
6
7
8
9
10
private void installDecor() {
...
if (mDecor == null) {
mDecor = generateDecor(-1);
}
...
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);
}
}

这里又出现了mDecor,它又是个啥呢。跟踪generateDecor

1
2
3
4
protected DecorView generateDecor(int featureId) {
...
return new DecorView(context, featureId, this, getAttributes());
}

可以看到mDecor是一个DecorView对象

1
public class DecorView extends FrameLayout

它其实就是一个FrameLayout,那么它里面具体是啥呢?我们往下跟

回到installDecor方法,我们再跟下generateLayout方法是如何创建mContentParent的?

1
2
3
4
5
6
7
8
9
10
11
12
13
protected ViewGroup generateLayout(DecorView decor) {
// Apply data from current theme.
//这里设置一些主题相关的
...
// Inflate the window decor.
int layoutResource;
// 给layoutResource 赋值(实质是找一个系统布局资源)

mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
...··
return contentParent;
}

这里有一个layoutResource,这个实质就是一个系统的layout xml文件。会根据我们主题不同给它赋不同的值。

接着我们看onResourcesLoaded方法

1
2
3
4
5
6
void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
...
final View root = inflater.inflate(layoutResource, null);
...
addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
}

重点关注这两行代码,还记得我们之前分析过mDecor实质是一个frameLayout,这里会把系统的布局文件添加到DecorView里面。到这里我们就知道了DecorView里面具体是啥了。

我们再回到generateLayout方法,会调用PhoneWindow的findViewById方法找一个id,赋值给contentParent返回。这里的findViewById方法实际是getDecorView().findViewById。实质是在mDecor中根据id找它的一个子view。到这里我们就知道了mContentParent具体是个啥了。

我们回到PhoneWindow的setContentView方法

1
2
3
4
5
6
7
8
9
@Override
public void setContentView(int layoutResID) {
if (mContentParent == null) {
installDecor();
}
...
mLayoutInflater.inflate(layoutResID, mContentParent);
...
}

最终会把我们传进来的布局文件填充到mContentParent中。至此,view添加结束。

总结:

PhoneWindow —> DecorView(根据主题不同对应系统的不同布局文件) — >mContentParent(系统布局文件中中的一个子view)—> 我们自己写的布局文件会被加载到mContentParent中。