Android之View绘制流程开胃菜---setContentView(...)详细分析
版权声明:本文出自汪磊的博客,转载请务必注明出处。
1 为什么要分析setContentView方法
作为安卓开发者相信大部分都有意或者无意看过如下图示:PhoneWindow,DecorView这些究竟都是些神马玩意?图示的层级关系是怎么来的?我们自己所写的布局是怎么加载到窗体上的?以及在上一篇《Android事件传递机制详解及最新源码分析——Activity篇》中提到过我们在调用setContentView设置布局的时候其实都是被放置在id为content的FrameLayout 布局中的,这里又是什么鬼?带着这些问题我们一起探讨下setContentView方法究竟做了些什么。
2 分析setContentView方法(API23)
我们平时调用setContentView,例如:setContentView(R.layout.xxx);点进源码都是先调用Activity中的setContentView方法,我们就从Activity中的setContentView方法开始分析。
Activity的源码中有三个重载的setContentView方法,如下:
1 public void setContentView(@LayoutRes int layoutResID) {
2 getWindow().setContentView(layoutResID);
3 initWindowDecorActionBar();
4 }
5
6 public void setContentView(View view) {
7 getWindow().setContentView(view);
8 initWindowDecorActionBar();
9 }
10
11 public void setContentView(View view, ViewGroup.LayoutParams params) {
12 getWindow().setContentView(view, params);
13 initWindowDecorActionBar();
14 }
可以看到三个方法都是又调用了getWindow().setContentView(...);在上一篇文章中分析过getWindow()返回mWindow对象,mWindow定义是Windo类型,实际初始化的时候初始化为PhoneWindow,源码如下:
private Window mWindow;
mWindow = new PhoneWindow(this);
这里说明一下:Window 是抽象类,主要提供一些绘制窗口的一些公用方法,PhoneWindow是Window的具体继承实现类。
我们看看Window类中setContentView方法,源码如下:
public abstract void setContentView(@LayoutRes int layoutResID);
public abstract void setContentView(View view);
public abstract void setContentView(View view, ViewGroup.LayoutParams params);
看到了吧,这里只是三个抽象方法而已,具体逻辑需要子类自己去实现。
接下来,我们就就去PhoneWindow中找一下吧,源码如下:
@Override
public void setContentView(int layoutResID) {
// 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)) {
final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
getContext());
transitionTo(newScene);
} else {
mLayoutInflater.inflate(layoutResID, mContentParent);
}
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
}
@Override
public void setContentView(View view) {
setContentView(view, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
}
@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.addView(view, params);
}
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
}
看到了吧,在子类PhoneWindow中有具体实现,并且setContentView(View view)实际上也是调用的setContentView(View view, ViewGroup.LayoutParams params),只不过params参数默认传入为MATCH_PARENT。并且setContentView(int layoutResID)与setContentView(View view, ViewGroup.LayoutParams params)方法代码逻辑是一样的,这里我们选取setContentView(int layoutResID)方法加以分析即可。
到这里我们明白平时调用的setContentView(R.layout.xxx)方法实际上调用的是PhoneWindow中的setContentView(int layoutResID)方法,接下来我们着重分析此方法。
3 分析PhoneWindow中的setContentView(int layoutResID)方法(API23)
源码如下:
1 @Override
2 public void setContentView(int layoutResID) {
3 // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
4 // decor, when theme attributes and the like are crystalized. Do not check the feature
5 // before this happens.
6 if (mContentParent == null) {
7 installDecor();
8 } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
9 mContentParent.removeAllViews();
10 }
11
12 if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
13 final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
14 getContext());
15 transitionTo(newScene);
16 } else {
17 mLayoutInflater.inflate(layoutResID, mContentParent);
18 }
19 mContentParent.requestApplyInsets();
20 final Callback cb = getCallback();
21 if (cb != null && !isDestroyed()) {
22 cb.onContentChanged();
23 }
24 }
第6行代码判断mContentParent 是否为空,mContentParent 是PhoneWindow中定义的一个ViewGroup类型实例。第一次运行的时候mContentParent 为null,则进入判断执行第7行代码
installDecor(),我们看看installDecor()方法都做了什么源码如下:这里只列出主要代码
1 private void installDecor() {
2 if (mDecor == null) {
3 mDecor = generateDecor();
4 ...
5 }
6 if (mContentParent == null) {
7 mContentParent = generateLayout(mDecor);
8 ...
9 }
10 ....
11 }
第2行代码判断mDecor是否为null,为null则执行generateDecor()代码并对mDecor赋值,mDecor是DecorView的一个实例,DecorView是PhoneWindow的内部类,定义如下:
private final class DecorView extends FrameLayout implements RootViewSurfaceTaker
看到了吧,DecorView其实就是FrameLayout 的子类,对FrameLayout 进行装饰,增强其某些功能。
我们继续看generateDecor()源码:
1 protected DecorView generateDecor() {
2 return new DecorView(getContext(), -1);
3 }
很简单吧就是生成DecorView对象并且返回,这里没什么要多说的。
返回installDecor()方法我们继续向下分析。
第6行代码又是判断mContentParent 是否为null,是则执行generateLayout(mDecor)方法并将返回值赋值给mContentParent 。
那我们就继续看generateLayout(mDecor)源码:
1 protected ViewGroup generateLayout(DecorView decor) {
2 // Apply data from current theme.
3
4 TypedArray a = getWindowStyle();
5
6 if (false) {
7 System.out.println("From style:");
8 String s = "Attrs:";
9 for (int i = 0; i < R.styleable.Window.length; i++) {
10 s = s + " " + Integer.toHexString(R.styleable.Window[i]) + "="
11 + a.getString(i);
12 }
13 System.out.println(s);
14 }
15
16 mIsFloating = a.getBoolean(R.styleable.Window_windowIsFloating, false);
17 int flagsToUpdate = (FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR)
18 & (~getForcedWindowFlags());
19 if (mIsFloating) {
20 setLayout(WRAP_CONTENT, WRAP_CONTENT);
21 setFlags(0, flagsToUpdate);
22 } else {
23 setFlags(FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR, flagsToUpdate);
24 }
25
26 if (a.getBoolean(R.styleable.Window_windowNoTitle, false)) {
27 requestFeature(FEATURE_NO_TITLE);
28 } else if (a.getBoolean(R.styleable.Window_windowActionBar, false)) {
29 // Don't allow an action bar if there is no title.
30 requestFeature(FEATURE_ACTION_BAR);
31 }
32
33 if (a.getBoolean(R.styleable.Window_windowActionBarOverlay, false)) {
34 requestFeature(FEATURE_ACTION_BAR_OVERLAY);
35 }
36
37 if (a.getBoolean(R.styleable.Window_windowActionModeOverlay, false)) {
38 requestFeature(FEATURE_ACTION_MODE_OVERLAY);
39 }
40
41 if (a.getBoolean(R.styleable.Window_windowSwipeToDismiss, false)) {
42 requestFeature(FEATURE_SWIPE_TO_DISMISS);
43 }
44
45 if (a.getBoolean(R.styleable.Window_windowFullscreen, false)) {
46 setFlags(FLAG_FULLSCREEN, FLAG_FULLSCREEN & (~getForcedWindowFlags()));
47 }
48
49 if (a.getBoolean(R.styleable.Window_windowTranslucentStatus,
50 false)) {
51 setFlags(FLAG_TRANSLUCENT_STATUS, FLAG_TRANSLUCENT_STATUS
52 & (~getForcedWindowFlags()));
53 }
54
55 if (a.getBoolean(R.styleable.Window_windowTranslucentNavigation,
56 false)) {
57 setFlags(FLAG_TRANSLUCENT_NAVIGATION, FLAG_TRANSLUCENT_NAVIGATION
58 & (~getForcedWindowFlags()));
59 }
60
61 if (a.getBoolean(R.styleable.Window_windowOverscan, false)) {
62 setFlags(FLAG_LAYOUT_IN_OVERSCAN, FLAG_LAYOUT_IN_OVERSCAN&(~getForcedWindowFlags()));
63 }
64
65 if (a.getBoolean(R.styleable.Window_windowShowWallpaper, false)) {
66 setFlags(FLAG_SHOW_WALLPAPER, FLAG_SHOW_WALLPAPER&(~getForcedWindowFlags()));
67 }
68
69 if (a.getBoolean(R.styleable.Window_windowEnableSplitTouch,
70 getContext().getApplicationInfo().targetSdkVersion
71 >= android.os.Build.VERSION_CODES.HONEYCOMB)) {
72 setFlags(FLAG_SPLIT_TOUCH, FLAG_SPLIT_TOUCH&(~getForcedWindowFlags()));
73 }
74
75 a.getValue(R.styleable.Window_windowMinWidthMajor, mMinWidthMajor);
76 a.getValue(R.styleable.Window_windowMinWidthMinor, mMinWidthMinor);
77 if (a.hasValue(R.styleable.Window_windowFixedWidthMajor)) {
78 if (mFixedWidthMajor == null) mFixedWidthMajor = new TypedValue();
79 a.getValue(R.styleable.Window_windowFixedWidthMajor,
80 mFixedWidthMajor);
81 }
82 if (a.hasValue(R.styleable.Window_windowFixedWidthMinor)) {
83 if (mFixedWidthMinor == null) mFixedWidthMinor = new TypedValue();
84 a.getValue(R.styleable.Window_windowFixedWidthMinor,
85 mFixedWidthMinor);
86 }
87 if (a.hasValue(R.styleable.Window_windowFixedHeightMajor)) {
88 if (mFixedHeightMajor == null) mFixedHeightMajor = new TypedValue();
89 a.getValue(R.styleable.Window_windowFixedHeightMajor,
90 mFixedHeightMajor);
91 }
92 if (a.hasValue(R.styleable.Window_windowFixedHeightMinor)) {
93 if (mFixedHeightMinor == null) mFixedHeightMinor = new TypedValue();
94 a.getValue(R.styleable.Window_windowFixedHeightMinor,
95 mFixedHeightMinor);
96 }
97 if (a.getBoolean(R.styleable.Window_windowContentTransitions, false)) {
98 requestFeature(FEATURE_CONTENT_TRANSITIONS);
99 }
100 if (a.getBoolean(R.styleable.Window_windowActivityTransitions, false)) {
101 requestFeature(FEATURE_ACTIVITY_TRANSITIONS);
102 }
103
104 final Context context = getContext();
105 final int targetSdk = context.getApplicationInfo().targetSdkVersion;
106 final boolean targetPreHoneycomb = targetSdk < android.os.Build.VERSION_CODES.HONEYCOMB;
107 final boolean targetPreIcs = targetSdk < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH;
108 final boolean targetPreL = targetSdk < android.os.Build.VERSION_CODES.LOLLIPOP;
109 final boolean targetHcNeedsOptions = context.getResources().getBoolean(
110 R.bool.target_honeycomb_needs_options_menu);
111 final boolean noActionBar = !hasFeature(FEATURE_ACTION_BAR) || hasFeature(FEATURE_NO_TITLE);
112
113 if (targetPreHoneycomb || (targetPreIcs && targetHcNeedsOptions && noActionBar)) {
114 setNeedsMenuKey(WindowManager.LayoutParams.NEEDS_MENU_SET_TRUE);
115 } else {
116 setNeedsMenuKey(WindowManager.LayoutParams.NEEDS_MENU_SET_FALSE);
117 }
118
119 // Non-floating windows on high end devices must put up decor beneath the system bars and
120 // therefore must know about visibility changes of those.
121 if (!mIsFloating && ActivityManager.isHighEndGfx()) {
122 if (!targetPreL && a.getBoolean(
123 R.styleable.Window_windowDrawsSystemBarBackgrounds,
124 false)) {
125 setFlags(FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
126 FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS & ~getForcedWindowFlags());
127 }
128 }
129 if (!mForcedStatusBarColor) {
130 mStatusBarColor = a.getColor(R.styleable.Window_statusBarColor, 0xFF000000);
131 }
132 if (!mForcedNavigationBarColor) {
133 mNavigationBarColor = a.getColor(R.styleable.Window_navigationBarColor, 0xFF000000);
134 }
135 if (a.getBoolean(R.styleable.Window_windowLightStatusBar, false)) {
136 decor.setSystemUiVisibility(
137 decor.getSystemUiVisibility() | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
138 }
139
140 if (mAlwaysReadCloseOnTouchAttr || getContext().getApplicationInfo().targetSdkVersion
141 >= android.os.Build.VERSION_CODES.HONEYCOMB) {
142 if (a.getBoolean(
143 R.styleable.Window_windowCloseOnTouchOutside,
144 false)) {
145 setCloseOnTouchOutsideIfNotSet(true);
146 }
147 }
148
149 WindowManager.LayoutParams params = getAttributes();
150
151 if (!hasSoftInputMode()) {
152 params.softInputMode = a.getInt(
153 R.styleable.Window_windowSoftInputMode,
154 params.softInputMode);
155 }
156
157 if (a.getBoolean(R.styleable.Window_backgroundDimEnabled,
158 mIsFloating)) {
159 /* All dialogs should have the window dimmed */
160 if ((getForcedWindowFlags()&WindowManager.LayoutParams.FLAG_DIM_BEHIND) == 0) {
161 params.flags |= WindowManager.LayoutParams.FLAG_DIM_BEHIND;
162 }
163 if (!haveDimAmount()) {
164 params.dimAmount = a.getFloat(
165 android.R.styleable.Window_backgroundDimAmount, 0.5f);
166 }
167 }
168
169 if (params.windowAnimations == 0) {
170 params.windowAnimations = a.getResourceId(
171 R.styleable.Window_windowAnimationStyle, 0);
172 }
173
174 // The rest are only done if this window is not embedded; otherwise,
175 // the values are inherited from our container.
176 if (getContainer() == null) {
177 if (mBackgroundDrawable == null) {
178 if (mBackgroundResource == 0) {
179 mBackgroundResource = a.getResourceId(
180 R.styleable.Window_windowBackground, 0);
181 }
182 if (mFrameResource == 0) {
183 mFrameResource = a.getResourceId(R.styleable.Window_windowFrame, 0);
184 }
185 mBackgroundFallbackResource = a.getResourceId(
186 R.styleable.Window_windowBackgroundFallback, 0);
187 if (false) {
188 System.out.println("Background: "
189 + Integer.toHexString(mBackgroundResource) + " Frame: "
190 + Integer.toHexString(mFrameResource));
191 }
192 }
193 mElevation = a.getDimension(R.styleable.Window_windowElevation, 0);
194 mClipToOutline = a.getBoolean(R.styleable.Window_windowClipToOutline, false);
195 mTextColor = a.getColor(R.styleable.Window_textColor, Color.TRANSPARENT);
196 }
197
198 // Inflate the window decor.
199
200 int layoutResource;
201 int features = getLocalFeatures();
202 // System.out.println("Features: 0x" + Integer.toHexString(features));
203 if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
204 layoutResource = R.layout.screen_swipe_dismiss;
205 } else if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {
206 if (mIsFloating) {
207 TypedValue res = new TypedValue();
208 getContext().getTheme().resolveAttribute(
209 R.attr.dialogTitleIconsDecorLayout, res, true);
210 layoutResource = res.resourceId;
211 } else {
212 layoutResource = R.layout.screen_title_icons;
213 }
214 // XXX Remove this once action bar supports these features.
215 removeFeature(FEATURE_ACTION_BAR);
216 // System.out.println("Title Icons!");
217 } else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0
218 && (features & (1 << FEATURE_ACTION_BAR)) == 0) {
219 // Special case for a window with only a progress bar (and title).
220 // XXX Need to have a no-title version of embedded windows.
221 layoutResource = R.layout.screen_progress;
222 // System.out.println("Progress!");
223 } else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) {
224 // Special case for a window with a custom title.
225 // If the window is floating, we need a dialog layout
226 if (mIsFloating) {
227 TypedValue res = new TypedValue();
228 getContext().getTheme().resolveAttribute(
229 R.attr.dialogCustomTitleDecorLayout, res, true);
230 layoutResource = res.resourceId;
231 } else {
232 layoutResource = R.layout.screen_custom_title;
233 }
234 // XXX Remove this once action bar supports these features.
235 removeFeature(FEATURE_ACTION_BAR);
236 } else if ((features & (1 << FEATURE_NO_TITLE)) == 0) {
237 // If no other features and not embedded, only need a title.
238 // If the window is floating, we need a dialog layout
239 if (mIsFloating) {
240 TypedValue res = new TypedValue();
241 getContext().getTheme().resolveAttribute(
242 R.attr.dialogTitleDecorLayout, res, true);
243 layoutResource = res.resourceId;
244 } else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) {
245 layoutResource = a.getResourceId(
246 R.styleable.Window_windowActionBarFullscreenDecorLayout,
247 R.layout.screen_action_bar);
248 } else {
249 layoutResource = R.layout.screen_title;
250 }
251 // System.out.println("Title!");
252 } else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {
253 layoutResource = R.layout.screen_simple_overlay_action_mode;
254 } else {
255 // Embedded, so no decoration is needed.
256 layoutResource = R.layout.screen_simple;
257 // System.out.println("Simple!");
258 }
259
260 mDecor.startChanging();
261
262 View in = mLayoutInflater.inflate(layoutResource, null);
263 decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
264 mContentRoot = (ViewGroup) in;
265
266 ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
267 if (contentParent == null) {
268 throw new RuntimeException("Window couldn't find content container view");
269 }
270
271 if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) {
272 ProgressBar progress = getCircularProgressBar(false);
273 if (progress != null) {
274 progress.setIndeterminate(true);
275 }
276 }
277
278 if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
279 registerSwipeCallbacks();
280 }
281
282 // Remaining setup -- of background and title -- that only applies
283 // to top-level windows.
284 if (getContainer() == null) {
285 final Drawable background;
286 if (mBackgroundResource != 0) {
287 background = getContext().getDrawable(mBackgroundResource);
288 } else {
289 background = mBackgroundDrawable;
290 }
291 mDecor.setWindowBackground(background);
292
293 final Drawable frame;
294 if (mFrameResource != 0) {
295 frame = getContext().getDrawable(mFrameResource);
296 } else {
297 frame = null;
298 }
299 mDecor.setWindowFrame(frame);
300
301 mDecor.setElevation(mElevation);
302 mDecor.setClipToOutline(mClipToOutline);
303
304 if (mTitle != null) {
305 setTitle(mTitle);
306 }
307
308 if (mTitleColor == 0) {
309 mTitleColor = mTextColor;
310 }
311 setTitleColor(mTitleColor);
312 }
313
314 mDecor.finishChanging();
315
316 return contentParent;
317 }
我勒个去,这方法太挺长,不过别担心,总体逻辑不复杂。
第4行代码getWindowStyle()是什么鬼呢?这里就直接说了,我们在manifest文件配置的Activity的时候有时会指定theme,如:android:theme="@style/AppTheme",getWindowStyle()就是获取我们配置的theme信息。
接着6-199行代码都是根据我们通过getWindowStyle()获取的theme配置信息进行相应设置。
200行代码,定义layoutResource变量。
201调用getLocalFeatures()方法又是干什么呢?我们有时会通过代码对Activity设置一些Feature,如:requestWindowFeature(Window.FEATURE_NO_TITLE);这里getLocalFeatures()方法就是获取通过requestWindowFeature设置的一些值。
202-258根据获取的features不同对layoutResource进行不同的赋值,layoutResource主要纪录不同的布局文件。如果什么也没设置,也就是说Activity没有任何修饰,那么就赋值为
R.layout.screen_simple,我们看一下R.layout.screen_simple布局源码:
1 <?xml version="1.0" encoding="utf-8"?>
2
3 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
4 android:layout_width="match_parent"
5 android:layout_height="match_parent"
6 android:fitsSystemWindows="true"
7 android:orientation="vertical">
8 <ViewStub android:id="@+id/action_mode_bar_stub"
9 android:inflatedId="@+id/action_mode_bar"
10 android:layout="@layout/action_mode_bar"
11 android:layout_width="match_parent"
12 android:layout_height="wrap_content"
13 android:theme="?attr/actionBarTheme" />
14 <FrameLayout
15 android:id="@android:id/content"
16 android:layout_width="match_parent"
17 android:layout_height="match_parent"
18 android:foregroundInsidePadding="false"
19 android:foregroundGravity="fill_horizontal|top"
20 android:foreground="?android:attr/windowContentOverlay" />
21 </LinearLayout>
看到了吧,很简单,就包括一个actiob_Bar,还有一个id为content的FrameLayout,并且action_Bar部分使用了布局优化ViewStub 。
继续向下分析262行将layoutResource记录的布局转化为View。
263行代码将262行生成的view添加到decor中,这个decor就是我们上面分析过的mDecor。
264行将262行生成的View赋值给mContentRoot,用以纪录。
接下来266行通过findViewById找到ID为ID_ANDROID_CONTENT的View,这个ID_ANDROID_CONTENT又是什么鬼?通过查找最终在父类Window中找到,源码如下:
public static final int ID_ANDROID_CONTENT = com.android.internal.R.id.content;
看到了吧,就是id为content的View,以R.layout.screen_simple布局为例,最终找的就是id为content的FrameLayout。赋值给名为contentParent的ViewGroup。
最终在316行将contentParent作为generateLayout方法的返回值返回。到此generateLayout想要探讨的就都探讨完了。
我们马上回看上面分析的installDecor()方法第7行。将generateLayout方法返回值赋值给mContentParent,到这里,你应该知道mContentParent就是DecorView中布局为content的部分。
我们在回看一开始分析的setContentView方法,之前分析到第7行,继续向下看直到第17行,调用mLayoutInflater.inflate(layoutResID, mContentParent),至于inflate方法内部逻辑这里就不分析了,不是本文重点,直接说结论:mLayoutInflater.inflate(layoutResID, mContentParent)就是将layoutResID布局转化为View添加到mContentParent中。还记得mContentParent吗?它就是DecorView中id为content的View。到这里就知道了原来我们自己定义的布局最终都是加载到这里了。
4总结
经过上面分析相信你已经有了一些眉目,我们赶紧总结一下。
我们平时在Activity中调用的setContentView方法其实都是调用的PhoneWindow中的setContentView方法,其首先会判断mContentParent是否为null,如果为null,则执行installDecor()方法,在installDecor()方法中会对mDecor进行判断是否为null,为null则进行初始化,mDecor为DecorView类型,DecorView继承自FrameLayout。接下来继续判断mContentParent是否为null,为null则执行generateLayout方法,在generateLayout方法中最重要的逻辑就是根据我们设置的不同feature找到对应布局文件,并且inflate为View,通过addView方法加入到mDecor中,然后找到布局文件中ID为content的View作为generateLayout方法最终返回值返回。接下来回到installDecor方法将generateLayout返回值赋值给mContentParent,最后回到setContentView,将我们自己的布局文件layoutResID加载到mContentParent中。
相信经过上述分析你应该对本文一开始的那张图会有更深刻的认识。