[TOC]

概述

Android开发的同学们对setContentView肯定都不陌生,但凡写到Activity,都离不开这个函数,那么么setContentView到底干了些什么?他是怎么让我们XML里面写的布局是怎么渲染成变成View的。

Activity 与 AppCompatActivity的区别

当我们在老版本Android SDK开发的时候新建的Project的默认继承的是Activity,而在5.0之后默认继承的就是AppCompatActivity。二者到底有什么区别呢??

AppCompatActivity其实就是有使用了Support包中 ActionBar特性的Activity的父类。

AppCompatActivity继承自FragmentActivity继承自Activity。

setContentView

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

代码是非常简洁的,直接调用的委托对象的setContentView。那我们来看一下委托对象的生成。

1
2
3
4
5
6
public AppCompatDelegate getDelegate() {
if (mDelegate == null) {
mDelegate = AppCompatDelegate.create(this, this);
}
return mDelegate;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 真正到了这里
private static AppCompatDelegate create(Context context, Window window,
AppCompatCallback callback) {
if (Build.VERSION.SDK_INT >= 24) {
return new AppCompatDelegateImplN(context, window, callback);
} else if (Build.VERSION.SDK_INT >= 23) {
return new AppCompatDelegateImplV23(context, window, callback);
} else if (Build.VERSION.SDK_INT >= 14) {
return new AppCompatDelegateImplV14(context, window, callback);
} else if (Build.VERSION.SDK_INT >= 11) {
return new AppCompatDelegateImplV11(context, window, callback);
} else {
return new AppCompatDelegateImplV9(context, window, callback);
}
}

而代理类实现的setContentView是在AppCompatDelegateImplV9中实现的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
 @Override
public void setContentView(View v) {
// 确保SubDecor被install
ensureSubDecor();
// 获取当前页面的content布局
ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
// 添加VIew
contentParent.removeAllViews();
contentParent.addView(v);
// 给Windows回调onContentChanged
mOriginalWindowCallback.onContentChanged();
}

@Override
public void setContentView(int resId) {
//确保SubDecor被install
ensureSubDecor();
// 获取当前页面的content布局
ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
// 添加VIew
contentParent.removeAllViews();
LayoutInflater.from(mContext).inflate(resId, contentParent);
// 给Windows回调onContentChanged
mOriginalWindowCallback.onContentChanged();
}

3、createSubDecor

setContentView的第一步就是确保SubDecor被install,下面源码中有注释。所以我们先看一下ensureSubDecor的逻辑实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
private void ensureSubDecor() {
if (!mSubDecorInstalled) {
mSubDecor = createSubDecor();

// If a title was set before we installed the decor, propagate it now
CharSequence title = getTitle();
if (!TextUtils.isEmpty(title)) {
if (mDecorContentParent != null) {
mDecorContentParent.setWindowTitle(title);
} else if (peekSupportActionBar() != null) {
peekSupportActionBar().setWindowTitle(title);
} else if (mTitleView != null) {
mTitleView.setText(title);
}
}

applyFixedSizeWindow();

onSubDecorInstalled(mSubDecor);

mSubDecorInstalled = true;

// Invalidate if the panel menu hasn't been created before this.
// Panel menu invalidation is deferred avoiding application onCreateOptionsMenu
// being called in the middle of onCreate or similar.
// A pending invalidation will typically be resolved before the posted message
// would run normally in order to satisfy instance state restoration.
PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);
if (!mIsDestroyed && (st == null || st.menu == null)) {
invalidatePanelMenu(FEATURE_SUPPORT_ACTION_BAR);
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
private ViewGroup createSubDecor() {
TypedArray a = mContext.obtainStyledAttributes(R.styleable.AppCompatTheme);

if (!a.hasValue(R.styleable.AppCompatTheme_windowActionBar)) {
a.recycle();
// 使用了AppCompatActivity但是没有设置一个Theme.AppCompat的主题,则会报这个Exception。
throw new IllegalStateException(
"You need to use a Theme.AppCompat theme (or descendant) with this activity.");
}
// 给Window设置一些主题属性的逻辑
if (a.getBoolean(R.styleable.AppCompatTheme_windowNoTitle, false)) {
requestWindowFeature(Window.FEATURE_NO_TITLE);
} else if (a.getBoolean(R.styleable.AppCompatTheme_windowActionBar, false)) {
// Don't allow an action bar if there is no title.
requestWindowFeature(FEATURE_SUPPORT_ACTION_BAR);
}
if (a.getBoolean(R.styleable.AppCompatTheme_windowActionBarOverlay, false)) {
requestWindowFeature(FEATURE_SUPPORT_ACTION_BAR_OVERLAY);
}
if (a.getBoolean(R.styleable.AppCompatTheme_windowActionModeOverlay, false)) {
requestWindowFeature(FEATURE_ACTION_MODE_OVERLAY);
}
mIsFloating = a.getBoolean(R.styleable.AppCompatTheme_android_windowIsFloating, false);
a.recycle();

// 创建DecorView
// Now let's make sure that the Window has installed its decor by retrieving it
mWindow.getDecorView();

final LayoutInflater inflater = LayoutInflater.from(mContext);
ViewGroup subDecor = null;
// 根据标记来决定inflate哪个layout
if (!mWindowNoTitle) {
if (mIsFloating) {
// If we're floating, inflate the dialog title decor
subDecor = (ViewGroup) inflater.inflate(
R.layout.abc_dialog_title_material, null);

// Floating windows can never have an action bar, reset the flags
mHasActionBar = mOverlayActionBar = false;
} else if (mHasActionBar) {
/**
* This needs some explanation. As we can not use the android:theme attribute
* pre-L, we emulate it by manually creating a LayoutInflater using a
* ContextThemeWrapper pointing to actionBarTheme.
*/
TypedValue outValue = new TypedValue();
mContext.getTheme().resolveAttribute(R.attr.actionBarTheme, outValue, true);

Context themedContext;
if (outValue.resourceId != 0) {
themedContext = new ContextThemeWrapper(mContext, outValue.resourceId);
} else {
themedContext = mContext;
}

// Now inflate the view using the themed context and set it as the content view
subDecor = (ViewGroup) LayoutInflater.from(themedContext)
.inflate(R.layout.abc_screen_toolbar, null);

mDecorContentParent = (DecorContentParent) subDecor
.findViewById(R.id.decor_content_parent);
mDecorContentParent.setWindowCallback(getWindowCallback());

/**
* Propagate features to DecorContentParent
*/
if (mOverlayActionBar) {
mDecorContentParent.initFeature(FEATURE_SUPPORT_ACTION_BAR_OVERLAY);
}
if (mFeatureProgress) {
mDecorContentParent.initFeature(Window.FEATURE_PROGRESS);
}
if (mFeatureIndeterminateProgress) {
mDecorContentParent.initFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
}
}
} else {
if (mOverlayActionMode) {
subDecor = (ViewGroup) inflater.inflate(
R.layout.abc_screen_simple_overlay_action_mode, null);
} else {
subDecor = (ViewGroup) inflater.inflate(R.layout.abc_screen_simple, null);
}

if (Build.VERSION.SDK_INT >= 21) {
// If we're running on L or above, we can rely on ViewCompat's
// setOnApplyWindowInsetsListener
ViewCompat.setOnApplyWindowInsetsListener(subDecor,
new OnApplyWindowInsetsListener() {
@Override
public WindowInsetsCompat onApplyWindowInsets(View v,
WindowInsetsCompat insets) {
final int top = insets.getSystemWindowInsetTop();
final int newTop = updateStatusGuard(top);

if (top != newTop) {
insets = insets.replaceSystemWindowInsets(
insets.getSystemWindowInsetLeft(),
newTop,
insets.getSystemWindowInsetRight(),
insets.getSystemWindowInsetBottom());
}

// Now apply the insets on our view
return ViewCompat.onApplyWindowInsets(v, insets);
}
});
} else {
// Else, we need to use our own FitWindowsViewGroup handling
((FitWindowsViewGroup) subDecor).setOnFitSystemWindowsListener(
new FitWindowsViewGroup.OnFitSystemWindowsListener() {
@Override
public void onFitSystemWindows(Rect insets) {
insets.top = updateStatusGuard(insets.top);
}
});
}
}

if (subDecor == null) {
throw new IllegalArgumentException(
"AppCompat does not support the current theme features: { "
+ "windowActionBar: " + mHasActionBar
+ ", windowActionBarOverlay: "+ mOverlayActionBar
+ ", android:windowIsFloating: " + mIsFloating
+ ", windowActionModeOverlay: " + mOverlayActionMode
+ ", windowNoTitle: " + mWindowNoTitle
+ " }");
}

if (mDecorContentParent == null) {
mTitleView = (TextView) subDecor.findViewById(R.id.title);
}

// Make the decor optionally fit system windows, like the window's decor
ViewUtils.makeOptionalFitsSystemWindows(subDecor);

final ContentFrameLayout contentView = (ContentFrameLayout) subDecor.findViewById(
R.id.action_bar_activity_content);
// 获取PhoneWindow中的content布局对象
final ViewGroup windowContentView = (ViewGroup) mWindow.findViewById(android.R.id.content);
if (windowContentView != null) {
// There might be Views already added to the Window's content view so we need to
// migrate them to our content view
while (windowContentView.getChildCount() > 0) {
final View child = windowContentView.getChildAt(0);
windowContentView.removeViewAt(0);
contentView.addView(child);
}

// Change our content FrameLayout to use the android.R.id.content id.
// Useful for fragments.
windowContentView.setId(View.NO_ID);
contentView.setId(android.R.id.content);

// The decorContent may have a foreground drawable set (windowContentOverlay).
// Remove this as we handle it ourselves
if (windowContentView instanceof FrameLayout) {
((FrameLayout) windowContentView).setForeground(null);
}
}
// 这个地方我们把subDecor设置给PhoneWindow.这个也就是我们为什么要保证phoneWindows被install的原因。
// Now set the Window's content view with the decor
mWindow.setContentView(subDecor);

contentView.setAttachListener(new ContentFrameLayout.OnAttachListener() {
@Override
public void onAttachedFromWindow() {}

@Override
public void onDetachedFromWindow() {
dismissPopups();
}
});

return subDecor;
}

这个方法比较长,我们来重点分析一下这个方法。我们先看一下。给Window设置主题属性的逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
@Override
public boolean requestWindowFeature(int featureId) {
featureId = sanitizeWindowFeatureId(featureId);

if (mWindowNoTitle && featureId == FEATURE_SUPPORT_ACTION_BAR) {
return false; // Ignore. No title dominates.
}
if (mHasActionBar && featureId == Window.FEATURE_NO_TITLE) {
// Remove the action bar feature if we have no title. No title dominates.
mHasActionBar = false;
}

switch (featureId) {
case FEATURE_SUPPORT_ACTION_BAR:
throwFeatureRequestIfSubDecorInstalled();
// 仅仅是对变量赋值
mHasActionBar = true;
return true;
case FEATURE_SUPPORT_ACTION_BAR_OVERLAY:
throwFeatureRequestIfSubDecorInstalled();
mOverlayActionBar = true;
return true;
case FEATURE_ACTION_MODE_OVERLAY:
throwFeatureRequestIfSubDecorInstalled();
mOverlayActionMode = true;
return true;
case Window.FEATURE_PROGRESS:
throwFeatureRequestIfSubDecorInstalled();
mFeatureProgress = true;
return true;
case Window.FEATURE_INDETERMINATE_PROGRESS:
throwFeatureRequestIfSubDecorInstalled();
mFeatureIndeterminateProgress = true;
return true;
case Window.FEATURE_NO_TITLE:
throwFeatureRequestIfSubDecorInstalled();
mWindowNoTitle = true;
return true;
}

return mWindow.requestFeature(featureId);
}

private void throwFeatureRequestIfSubDecorInstalled() {
if (mSubDecorInstalled) {
// 这个又解释了一个原因,我们如果在setContentView之后再次去设置requestWindowFeature,会抛出Exception。
// 其实这个问题大家应该也不会陌生
throw new AndroidRuntimeException(
"Window feature must be requested before adding content");
}
}

下面,我们再来看一下这个方法mWindow.getDecorView()

Android里的Window这个类的实现子类其实是PhoneWindow,所以我们直接取PhoneWindow中去查getDecorView这个函数。

1
2
3
4
5
6
7
@Override
public final View getDecorView() {
if (mDecor == null || mForceDecorInstall) {
installDecor();
}
return mDecor;
}

方法很简单,就是installDecor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
private void installDecor() {
mForceDecorInstall = false;
if (mDecor == null) {
// 我们重点看一下这个方法。从名字也可以看出,生成DecorView
mDecor = generateDecor(-1);
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
mDecor.setIsRootNamespace(true);
if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
}
} else {
mDecor.setWindow(this);
}
if (mContentParent == null) {
// 我们同时也来看看这个问题
mContentParent = generateLayout(mDecor);

// Set up decor part of UI to ignore fitsSystemWindows if appropriate.
mDecor.makeOptionalFitsSystemWindows();

final DecorContentParent decorContentParent = (DecorContentParent) mDecor.findViewById(
R.id.decor_content_parent);

if (decorContentParent != null) {
mDecorContentParent = decorContentParent;
mDecorContentParent.setWindowCallback(getCallback());
if (mDecorContentParent.getTitle() == null) {
mDecorContentParent.setWindowTitle(mTitle);
}

final int localFeatures = getLocalFeatures();
for (int i = 0; i < FEATURE_MAX; i++) {
if ((localFeatures & (1 << i)) != 0) {
mDecorContentParent.initFeature(i);
}
}

mDecorContentParent.setUiOptions(mUiOptions);

if ((mResourcesSetFlags & FLAG_RESOURCE_SET_ICON) != 0 ||
(mIconRes != 0 && !mDecorContentParent.hasIcon())) {
mDecorContentParent.setIcon(mIconRes);
} else if ((mResourcesSetFlags & FLAG_RESOURCE_SET_ICON) == 0 &&
mIconRes == 0 && !mDecorContentParent.hasIcon()) {
mDecorContentParent.setIcon(
getContext().getPackageManager().getDefaultActivityIcon());
mResourcesSetFlags |= FLAG_RESOURCE_SET_ICON_FALLBACK;
}
if ((mResourcesSetFlags & FLAG_RESOURCE_SET_LOGO) != 0 ||
(mLogoRes != 0 && !mDecorContentParent.hasLogo())) {
mDecorContentParent.setLogo(mLogoRes);
}

// Invalidate if the panel menu hasn't been created before this.
// Panel menu invalidation is deferred avoiding application onCreateOptionsMenu
// being called in the middle of onCreate or similar.
// A pending invalidation will typically be resolved before the posted message
// would run normally in order to satisfy instance state restoration.
PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);
if (!isDestroyed() && (st == null || st.menu == null) && !mIsStartingWindow) {
invalidatePanelMenu(FEATURE_ACTION_BAR);
}
} else {
mTitleView = findViewById(R.id.title);
if (mTitleView != null) {
if ((getLocalFeatures() & (1 << FEATURE_NO_TITLE)) != 0) {
final View titleContainer = findViewById(R.id.title_container);
if (titleContainer != null) {
titleContainer.setVisibility(View.GONE);
} else {
mTitleView.setVisibility(View.GONE);
}
mContentParent.setForeground(null);
} else {
mTitleView.setText(mTitle);
}
}
}

if (mDecor.getBackground() == null && mBackgroundFallbackResource != 0) {
mDecor.setBackgroundFallback(mBackgroundFallbackResource);
}

// Only inflate or create a new TransitionManager if the caller hasn't
// already set a custom one.
if (hasFeature(FEATURE_ACTIVITY_TRANSITIONS)) {
if (mTransitionManager == null) {
final int transitionRes = getWindowStyle().getResourceId(
R.styleable.Window_windowContentTransitionManager,
0);
if (transitionRes != 0) {
final TransitionInflater inflater = TransitionInflater.from(getContext());
mTransitionManager = inflater.inflateTransitionManager(transitionRes,
mContentParent);
} else {
mTransitionManager = new TransitionManager();
}
}

mEnterTransition = getTransition(mEnterTransition, null,
R.styleable.Window_windowEnterTransition);
mReturnTransition = getTransition(mReturnTransition, USE_DEFAULT_TRANSITION,
R.styleable.Window_windowReturnTransition);
mExitTransition = getTransition(mExitTransition, null,
R.styleable.Window_windowExitTransition);
mReenterTransition = getTransition(mReenterTransition, USE_DEFAULT_TRANSITION,
R.styleable.Window_windowReenterTransition);
mSharedElementEnterTransition = getTransition(mSharedElementEnterTransition, null,
R.styleable.Window_windowSharedElementEnterTransition);
mSharedElementReturnTransition = getTransition(mSharedElementReturnTransition,
USE_DEFAULT_TRANSITION,
R.styleable.Window_windowSharedElementReturnTransition);
mSharedElementExitTransition = getTransition(mSharedElementExitTransition, null,
R.styleable.Window_windowSharedElementExitTransition);
mSharedElementReenterTransition = getTransition(mSharedElementReenterTransition,
USE_DEFAULT_TRANSITION,
R.styleable.Window_windowSharedElementReenterTransition);
if (mAllowEnterTransitionOverlap == null) {
mAllowEnterTransitionOverlap = getWindowStyle().getBoolean(
R.styleable.Window_windowAllowEnterTransitionOverlap, true);
}
if (mAllowReturnTransitionOverlap == null) {
mAllowReturnTransitionOverlap = getWindowStyle().getBoolean(
R.styleable.Window_windowAllowReturnTransitionOverlap, true);
}
if (mBackgroundFadeDurationMillis < 0) {
mBackgroundFadeDurationMillis = getWindowStyle().getInteger(
R.styleable.Window_windowTransitionBackgroundFadeDuration,
DEFAULT_BACKGROUND_FADE_DURATION_MS);
}
if (mSharedElementsUseOverlay == null) {
mSharedElementsUseOverlay = getWindowStyle().getBoolean(
R.styleable.Window_windowSharedElementsUseOverlay, true);
}
}
}
}

generateLayout函数过多,此处不贴出代码,值只分析下过程:

  • 设置一些Window的属性;
  • 根据Window属性选择一个layoutResource,这些layoutResource有一个共性是都有一个@android:id/content的布局,因为在AppCompatDelegateImplV9的createSubDecor函数里会用到这个content;
  • 选出layoutResource之后会进入一句关键的代码:mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);layoutResource就被inflate出来并且添加到DecorView中了。备注,添加View的时候使用的LayoutParams是MATCH_PARENT;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
mStackId = getStackId();

if (mBackdropFrameRenderer != null) {
loadBackgroundDrawablesIfNeeded();
mBackdropFrameRenderer.onResourcesLoaded(
this, mResizingBackgroundDrawable, mCaptionBackgroundDrawable,
mUserCaptionBackgroundDrawable, getCurrentColor(mStatusColorViewState),
getCurrentColor(mNavigationColorViewState));
}

mDecorCaptionView = createDecorCaptionView(inflater);
final View root = inflater.inflate(layoutResource, null);// inflate出View
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 {
// Put it below the color views.
addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
}
mContentRoot = (ViewGroup) root;
initializeElevation();
}

此时就开始创建真正的subDecor了,也有四个可选的layout,根据之前设置的属性来选择,然后去inflate出来。

1
2
3
// 这个地方我们把subDecor设置给PhoneWindow.这个也就是我们为什么要保证phoneWindows被install的原因。
// Now set the Window's content view with the decor
mWindow.setContentView(subDecor);

开始设置PhoneWindow的contentView,再把代码切到PhoneWindow中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@Override
public void setContentView(View view, ViewGroup.LayoutParams params) {
// Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
// decor, when theme attributes and the like are crystalized. Do not check the feature
// before this happens.
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之前是@android:id/content,现在是View.NO_ID;
// 现在的@android:id/content是SubDecor中的action_bar_activity_content
// SubDecor 已经被添加到了PhoneWindow中,并且@android:id/content是SubDecor中的action_bar_activity_content。
mContentParent.addView(view, params);
}
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
mContentParentExplicitlySet = true;
}

经过上面的步骤之后,我们的ensureSubdecor的逻辑已基本执行完毕。他额主要作用就是给PhoneWindow添加SubDecor。

我们回到主线任务:

1
LayoutInflater.from(mContext).inflate(resId, contentParent);

下面,我们就要开始主要分析一下上面的代码逻辑。

看看setContentView是怎么添加到contentParent的布局中。

概要总结

所以整个setContentView内部做了如下事:

在Activity中调用setContentView(实际调用PhoneWindow#setContentView)

首先,判断DecorView是否初始化。没有的话。新建DecorView实例

设置界面主题(requestFeature)

确定主题界面(layoutResource = R.layout.xxx)

在主题界面抽取内容ViewGroup(mContentParent = findViewById)

将我们自己创建的布局界面和Android提供的内容mContentParent打包进Scene

通过LayoutInflater解析布局,将布局转化为View

将view添加到mContentParent中

将整个界面装载到系统界面中