activity加载UI直观的起始位置是activity的setContentView方法,进入该方法看到实现如下:
public void setContentView(int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
getWindow获得的是Window对象的实例,Window是一个抽象类,提供了一些通用的API。这个抽象类目前只有一个实现那就是PhoneWindow,PhoneWindow的setContentView方法如下:
@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();
}
}
通过代码可以看出,如果mContentParent == null,就需要调用 installDecor()创建一个DecorView,
DecorView是PhoneWindow的一个内部类,继承自FrameLayout。
private void installDecor() {
if (mDecor == null) {
mDecor = generateDecor();
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
mDecor.setIsRootNamespace(true);
if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
}
}
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);
……
} else {
……
}
……
}
}
installDecor中先是创建了一个DecorView接着调用generateLayout方法,向其内部添加了一个布局。generateLayout方法方法比较长,比较核心的东西是根据样式来设置flag加载不同的布局。
protected ViewGroup generateLayout(DecorView decor) {
……
……
if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {
layoutResource = R.layout.screen_simple_overlay_action_mode;
} else {
// Embedded, so no decoration is needed.
layoutResource = R.layout.screen_simple;
// System.out.println("Simple!");
}
mDecor.startChanging();
View in = mLayoutInflater.inflate(layoutResource, null);
decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
mContentRoot = (ViewGroup) in;
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
……
mDecor.finishChanging();
return contentParent;
}
这里的layoutResource 可以是多种, R.layout.screen_title、 R.layout.screen_action_bar、R.layout.screen_progress等等。这也是为什么我们设置全屏Flag时需要在setContentView之前的原因,如果setContentView之后,这些布局已经加载进来了,再设置Flag不会起到该有的作用。为了便于理解,取了其中比较简单的布局 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>
这个布局中包含两个子View,一个是ViewStub ,用来根据需要加载actionBar的。另外一个是FrameLayout,这就是添加我们布局的位置。
上文中所出现的源码均来自API24,不同版本可能略有不同,但大致流程一致。