「这是我参与2022首次更文挑战的第23天,活动详情查看:2022首次更文挑战」
我们接着看Activity,这篇来分析一下setContentView()
一、setContentView()的作用
我们会在onCreate()方法中做初始化工作,首先就要调用setContentView()加载布局,那来一起学习一下setContentView()的原理
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
一层层点进源码,我们会发现,最终调用的是Activity的setContentView(),但是里面就两行代码,我们还需要接着看
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
调用了getWindow()的setContentView()方法
private Window mWindow;
public Window getWindow() {
return mWindow;
}
所以getWindow()是Window,我们找到mWindow的初始化
final void attach(...) {
mWindow = new PhoneWindow();
}
原来mWindow就是PhoneWindow,所以我们找一下PhoneWindow的setContentView(其实Window只有PhoneWindow这一个子类,并且Windows和Activity是一一对应的关系)
@Override
public void setContentView(int layoutResID) {
if (mContentParent == null) {
installDecor();
}
...
mLayoutInflater.inflate(layoutResID, mContentParent);
...
}
在PhoneWindow的setContentView()中有两个方法
- installDecor()
- mLayoutInflater.inflate()
我们首先看看installDecor()具体逻辑
private void installDecor() {
if (mDecor == null) {
mDecor = generateDecor();
}
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);
}
}
首先调用generateDecor()方法创建了DecorView实例,然后通过generateLayout()返回contentParent,最终赋值给mContentParent
protected ViewGroup generateLayout(DecorView decor){
...
View in = mLayoutInflater.inflate(layoutResource, null);
decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
...
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
...
return contentParent;
}
我们在这里可以得出installDecor()是将系统的一个布局文件转化成View,并将这个view添加到DecorView中,并且PhoneWindow持有DecorView,每个Activity都对应一个PhoneWidow。mContentParent是放置窗体的容器,也就是我们所加入的View视图树
我们总结一下setContentView()的调用顺序
- Activity.setCotentView
- PhoneWindows.setContentView
- PhoneWindows.installDecor
- ActivityThread.handleResumeActivity
虽然setContentView()完成了layout的布局,但是其实还未显示,真正添加进来是在ActivityThread的handleResumeActivity()
ActivityClientRecord r = performResumeActivity(token, clearHide);
...
if (r.window == null && !a.mFinished && willBeVisible) {
...
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
ViewManager wm = a.getWindowManager();
...
if (a.mVisibleFromClient) {
a.mWindowAdded = true;
wm.addView(decor, l);
}
}
二、setContentView()用法
- setContentView()可以设置Activity界面
- 在Activity中动态切换显示的View
- 在onContentChanged中进行Activity View成员变量的初始化操作