Activity如何加载布局的?我们定位到Activity.java
Avtivity调用setContentView,
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
public Window getWindow() {
return mWindow;
}
getWindow得到的是 mWindow,它是Window对象,Window是一个抽象类,它提供了各种窗口操作、设置背景的方法。
我们再全局搜索mWindow是在哪初始化的
final void attach(...) {
...
mWindow = new PhoneWindow(this, window);
mWindow.setWindowControllerCallback(this);
mWindow.setCallback(this);
mWindow.setOnWindowDismissedCallback(this);
mWindow.getLayoutInflater().setPrivateFactory(this);
...
}
可以看到mWindow是由PhoneWindow实现的
现在知道Activity的onContentView调用的是PhoneWindow的onContentView,我们再来看一下PhoneWindow的onContentView方法(这个类被隐藏了,小伙伴可以取sources目录全局搜索)
@Override
public void setContentView(int layoutResID) {
if (mContentParent == null) {
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
view.setLayoutParams(params);
final Scene newScene = new Scene(mContentParent, view);
transitionTo(newScene);
} else {
mContentParent.addView(view, params);
}
...
cb.onContentChanged();
}
现在逐一分析PhoneWindow的onContentView方法,先看installDecor()
private void installDecor() {
if (mDecor == null) {
mDecor = generateDecor(-1);
...
} else {
mDecor.setWindow(this);
}
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);
...
}
接着看下generateDecor(-1)
protected DecorView generateDecor(int featureId) {
Context context;
...
return new DecorView(context, featureId, this, getAttributes());
}
DecorView extends FrameLayout
DecorView(Context context, int featureId, PhoneWindow window,
WindowManager.LayoutParams params) {
super(context);
...
setWindow(window);//与phoneWindiw绑定
...
}
就是new了一个DecorView
接着分析installDecor方法
if (mContentParent == null)
mContentParent = generateLayout(mDecor);
generateLayout是什么作用呢?
protected ViewGroup generateLayout(DecorView decor) {
...
mDecor.startChanging();
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
...
mDecor.finishChanging();
return contentParent;
}
看一下mDecor的onResourcesLoaded(mLayoutInflater, layoutResource)方法;
void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
...
final View root = inflater.inflate(layoutResource, null);
addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
...
}
可以看到onResourcesLoaded方法目的是把 包含id为ID_ANDROID_CONTENT的布局文件,添加到DecoverView里。
generateLayout返回的contentParent其实就是ID为ID_ANDROID_CONTENT的FrameLayout
现在知道了installDecor();其实就是创建DecoverView和mContentParent,并将mContentParent添加到mDecor里面。
@Override
public void setContentView(int layoutResID) {
installDecor();//初始化DecorView,向DecoverView添加系统布局,获取其中id为content的帧布局
mContentParent.addView(view, params);//将我们的布局添加到帧布局中
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
}
关键点
mContentParent.addView(view, params);
将用户定义的view添加到mContentParent中,然后回调onContentChanged方法,可在Avtivity中重写这个接口,代表布局加载完成的回调
setContentView主要作用有两个
1、初始化DecorView,向DecoverView添加系统布局,获取其中id为content的帧布局
2、将我们的布局添加到帧布局中
我们窗口分布大致如下