View 源码分析——setContentView
2018-03-12 本文已影响0人
wethereornot
分析一下 android 中布局的加载流程,每次新建 activity 时都要在 onCreate 中调用 setContentView(R.layout.activity_main);调用这个方法具体做了哪些事情呢?
- 分析onCreate中的setContentView:
// mainActivity中
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
跳转setContentView()方法,
需要关注的地方有两个:
1、 installDecor(); 用来初始化DecorView
2、 mLayoutInflater.inflate(layoutResID, mContentParent); 将自己的布局添加到mContentParent(mContentParent是什么?等会解释)
// PhoneWindow.java 中
public void setContentView(int layoutResID) {
// 刚初始化 mContentParent ==null
if (mContentParent == null) {
// 初始化DecorView
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 {
// 此处inflate自己布局并且添加到mContentParent布局中
mLayoutInflater.inflate(layoutResID, mContentParent);
}
// .....省略不重要的
}
看看installDecor()方法中做了什么
1、创建 DecorView
2、构建 mContentParent
// PhoneWindow中
private void installDecor() {
mForceDecorInstall = false;
if (mDecor == null) {
// 1、创建 DecorView
mDecor = generateDecor(-1);
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
mDecor.setIsRootNamespace(true);
if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
}
} else {
//如果创建了 则与当前的 window 对象绑定
mDecor.setWindow(this);
}
if (mContentParent == null) {
// 2、构建 mContentParent
mContentParent = generateLayout(mDecor);
}
}
具体是如何创建 mContentParent 呢?
protected ViewGroup generateLayout(DecorView decor) {
// 系统资源id
int layoutResource;
int features = getLocalFeatures();
// 这些 if else 判断是为了选择加载系统哪个布局的(只截取了一部分)
if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
layoutResource = R.layout.screen_swipe_dismiss;
setCloseOnSwipeEnabled(true);
} else if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {
if (mIsFloating) {
TypedValue res = new TypedValue();
getContext().getTheme().resolveAttribute(
R.attr.dialogTitleIconsDecorLayout, res, true);
layoutResource = res.resourceId;
} else {
layoutResource = R.layout.screen_title_icons;
}
// XXX Remove this once action bar supports these features.
removeFeature(FEATURE_ACTION_BAR);
// System.out.println("Title Icons!");
} else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0
&& (features & (1 << FEATURE_ACTION_BAR)) == 0) {
// Special case for a window with only a progress bar (and title).
// XXX Need to have a no-title version of embedded windows.
layoutResource = R.layout.screen_progress;
// System.out.println("Progress!");
} else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) {
// Special case for a window with a custom title.
// If the window is floating, we need a dialog layout
if (mIsFloating) { .......}
// mDecor要改变的标记位
mDecor.startChanging();
// 它将 layoutResource 布局文件解析成 View 添加到了 DecorView 之中
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
// findViewById 找到添加的布局作为父布局contentParent
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
......
// 将此 contentParent 返回
return contentParent;
}
下面看几个R.layout.screen_simple的布局
//layoutResource = R.layout.screen_title_icons;
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:fitsSystemWindows="true"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- Popout bar for action modes -->
<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"/>
<RelativeLayout android:id="@android:id/title_container"
style="?android:attr/windowTitleBackgroundStyle"
android:layout_width="match_parent"
android:layout_height="?android:attr/windowTitleSize">
<!-- The title background has 9px left padding. -->
<ImageView android:id="@android:id/left_icon"
android:visibility="gone"
android:layout_marginEnd="9dip"
android:layout_width="16dip"
android:layout_height="16dip"
android:scaleType="fitCenter"
android:layout_alignParentStart="true"
android:layout_centerVertical="true" />
<ProgressBar android:id="@+id/progress_circular"
style="?android:attr/progressBarStyleSmallTitle"
android:visibility="gone"
android:max="10000"
android:layout_centerVertical="true"
android:layout_alignParentEnd="true"
android:layout_marginStart="6dip"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<!-- There are 6dip between this and the circular progress on the right, we
also make 6dip (with the -3dip margin_left) to the icon on the left or
the screen left edge if no icon. This also places our left edge 3dip to
the left of the title text left edge. -->
<ProgressBar android:id="@+id/progress_horizontal"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="-3dip"
android:layout_toStartOf="@android:id/progress_circular"
android:layout_toEndOf="@android:id/left_icon"
android:layout_centerVertical="true"
android:visibility="gone"
android:max="10000" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:layout_toStartOf="@id/progress_circular"
android:layout_toEndOf="@android:id/left_icon"
>
<TextView android:id="@android:id/title"
style="?android:attr/windowTitleStyle"
android:layout_width="0dip"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@null"
android:fadingEdge="horizontal"
android:scrollHorizontally="true"
android:gravity="center_vertical"
android:layout_marginEnd="2dip"
/>
<!-- 2dip between the icon and the title text, if icon is present. -->
<ImageView android:id="@android:id/right_icon"
android:visibility="gone"
android:layout_width="16dip"
android:layout_height="16dip"
android:layout_weight="0"
android:layout_gravity="center_vertical"
android:scaleType="fitCenter"
/>
</LinearLayout>
</RelativeLayout>
<FrameLayout android:id="@android:id/content"
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="1"
android:foregroundGravity="fill_horizontal|top"
android:foreground="?android:attr/windowContentOverlay" />
</LinearLayout>
// layoutResource = R.layout.screen_title;
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:fitsSystemWindows="true">
<!-- Popout bar for action modes -->
<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:layout_width="match_parent"
android:layout_height="?android:attr/windowTitleSize"
style="?android:attr/windowTitleBackgroundStyle">
<TextView android:id="@android:id/title"
style="?android:attr/windowTitleStyle"
android:background="@null"
android:fadingEdge="horizontal"
android:gravity="center_vertical"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
<FrameLayout android:id="@android:id/content"
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="1"
android:foregroundGravity="fill_horizontal|top"
android:foreground="?android:attr/windowContentOverlay" />
</LinearLayout>
// 可以看出每个布局中都有一个被LinearLayout 包裹的 id为 id="@android:id/content" 的FrameLayout 控件
- 至此可以看出 setContentView 的大体流程来
- 在Activity生命周期attach方法里面创建出PhoneWindow
- 调用PhoneWindow的setContentView方法
- 在PhoneWindow里面创建DecorView,DecorView会去加载系统的一个布局(FrameLayout)
- 将页面自己写的布局填充到DecorView布局里面id为R.id.content的View,也就是FrameLayout里面
-
View层级图
系统布局.png