一、首先调用Activity的setContentView方法
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
- getWindow()就是PhoneWindow,PhoneWindow这个类在Activity的attach方法初始化。
- Activity的setContentView方法,其实调的就是PhoneWindow的setContentView方法
二、调用PhoneWindow的setContentView()方法
@Override
public void setContentView(int layoutResID) {
if (mContentParent == null) {
// 初始化DecorView
installDecor();
}
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
getContext());
transitionTo(newScene);
} else {
// 自己设置的布局填充到mContentParent这里
mLayoutInflater.inflate(layoutResID, mContentParent);
}
}
这方法里面做了两件比较重要的事情
1、初始化PhoneWindow里面的DecorView
2、就是把Activity里面设置的布局填充到mContentParent
三、调用PhoneWindow里面的installDecor()方法
private void installDecor() {
mForceDecorInstall = false;
if (mDecor == null) {
// 创建DecorView
mDecor = generateDecor(-1);
} else {
mDecor.setWindow(this);
}
if (mContentParent == null) {
// 找到mContentParent
mContentParent = generateLayout(mDecor);
}
}
1.创建DecorView
2.在DecorView里面找到mContentParent 这个View
四、调用PhoneWindow里面的generateLayout方法,也就是怎样获取mContentParent
// 其实就是DecorView里面id为R.id.content的布局,
// 也就是R.layout.screen_simple的布局里面的FrameLayout
protected ViewGroup generateLayout(DecorView decor) {
// 填充PhoneWindow的Decor
// 系统资源ID
int layoutResource;
int features = getLocalFeatures();
// 根据用户设置的主题做一系列判断,然后找到DecorView的布局ID
if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
layoutResource = R.layout.screen_swipe_dismiss;
setCloseOnSwipeEnabled(true);
} else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {
layoutResource = R.layout.screen_simple_overlay_action_mode;
} else {
// 找到这里
layoutResource = R.layout.screen_simple;
}
mDecor.startChanging();
// 这个方法就是将找到的布局填充到DecorView
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
// 在Window里面 public static final int ID_ANDROID_CONTENT = com.android.internal.R.id.content;
// findViewById(ID_ANDROID_CONTENT)这个是调用DecorView的方法
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
return contentParent;
}
// DecorView里面的方法
void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
mDecorCaptionView = createDecorCaptionView(inflater);
// 填充系统布局
final View root = inflater.inflate(layoutResource, null);
// 将找到的系统布局,添加到DecorView
if (mDecorCaptionView != null) {
if (mDecorCaptionView.getParent() == null) {
addView(mDecorCaptionView,
new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
}
mDecorCaptionView.addView(root,
new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));
} else {
addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
}
mContentRoot = (ViewGroup) root;
initializeElevation();
}
五、系统资源 R.layout.screen_simple的布局
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:orientation="vertical">
<ViewStub android:id="@+id/action_mode_bar_stub"
android:inflatedId="@+id/action_mode_bar"
android:layout="@layout/action_mode_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="?attr/actionBarTheme" />
<FrameLayout
android:id="@android:id/content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:foregroundInsidePadding="false"
android:foregroundGravity="fill_horizontal|top"
android:foreground="?android:attr/windowContentOverlay" />
</LinearLayout>
六、setContentView调用的整个流程
1. 在Activity生命周期attach方法里面创建出PhoneWindow
2. 调用PhoneWindow的setContentView方法
3. 在PhoneWindow里面创建DecorView,DecorView会去加载系统的一个布局
4. 将页面自己写的布局填充到DecorView布局里面id为R.id.content的View,也就是FrameLayout里面
Activity里面的时序图
AppCompatActivity里面的时序图