前语
信任 Android 开发都知道,View 是树形结构,一组 View 的集合便是 ViewGroup
,而 ViewGroup
中又能够包括 View 和其他 ViewGroup
,然后构成了树结构。那么问题来了,这棵树的根又是什么呢?接下来就让咱们一起来探求一下 Android 的顶级 View——DecorView
。
1. 从 Activity 探求 View 的布局
之所以从 Activity
来开端看,是因为一个 App 的界面都是由 Activity
加载各种各样的布局得到的,这儿的布局当然便是由 View 组成的啦,Activity
加载布局的代码信任大家都很了解:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.zmq_test);
}
进去 setContentView
的源码来康康它终究做了些什么:
public void setContentView(int layoutResID) {
// 1.getWindow()
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
先看看注释 1 处,咱们看看 getWindow
是什么东东:
public Window getWindow() {
return mWindow;
}
getWindow
直接回来了一个私有变量 mWindow
,那咱们就全局找一下 mWindow
是在哪里被初始化的吧?
final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, int ident,
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
NonConfigurationInstances lastNonConfigurationInstances,
Configuration config, String referrer, IVoiceInteractor voiceInteractor,
Window window, ActivityConfigCallback activityConfigCallback, IBinder assistToken,
IBinder shareableActivityToken) {
attachBaseContext(context);
mFragments.attachHost(null);
// 初始化 mWindow
mWindow = new PhoneWindow(this, window, activityConfigCallback);
...
}
咱们在 Activity
的 attch
办法中找到了 mWindow
初始化的踪迹。本来 mWindow
是 PhoneWindow
类,承继自 Window
,既然知道了 getWindow
回来的其实是 PhoneWindow
,那就去看看 PhoneWindow
的 setContentView
办法都做了些什么吧:
public void setContentView(int layoutResID) {
if (mContentParent == null) {
// 1.初始化
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
...
}
这段代码很明晰,假如放置内容的视图为空,就需要去履行 installDecor
办法:
private void installDecor() {
if (mDecor == null) {
// 1. 初始化 decor
mDecor = generateDecor(-1);
...
} else {
mDecor.setWindow(this);
}
if (mContentParent == null) {
// 2. 生成布局
mContentParent = generateLayout(mDecor);
...
}
}
installDecor
办法的代码太长啦,咱们挑要点部分来看一看,这儿现已能看到 DecorView
相关的蛛丝马迹了,mDecor
为空的时候就去生成一个目标,这个目标是不是便是咱们要寻觅的 DecorView
呢?
protected DecorView generateDecor(int featureId) {
...
return new DecorView(context, featureId, this, getAttributes());
}
公然!在 generateDecor
办法中找到了创立 DecorView
的代码,DecorView
承继自 FrameLayout
,也便是 View 的子类,更是 Activity
中的根 View。那么 Activity
到底是如何把 DecorView
作为根 View 布局的呢?咱们接着往下看,注释 2 处的 generateLayout
运用创立的 mDecor
做了什么吧:
protected ViewGroup generateLayout(DecorView decor) {
// 1.依据当时的 Activity 主题来设置一些特点.
TypedArray a = getWindowStyle();
...
if (a.getBoolean(R.styleable.Window_windowNoTitle, false)) {
requestFeature(FEATURE_NO_TITLE);
} else if (a.getBoolean(R.styleable.Window_windowActionBar, false)) {
// Don't allow an action bar if there is no title.
requestFeature(FEATURE_ACTION_BAR);
}
...
// 2.给 layoutResource 赋值不同的资源 id ,加载不同的布局。
int layoutResource;
int features = getLocalFeatures();
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;
}
removeFeature(FEATURE_ACTION_BAR);
} else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0
&& (features & (1 << FEATURE_ACTION_BAR)) == 0) {
layoutResource = R.layout.screen_progress;
} else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) {
if (mIsFloating) {
TypedValue res = new TypedValue();
getContext().getTheme().resolveAttribute(
R.attr.dialogCustomTitleDecorLayout, res, true);
layoutResource = res.resourceId;
} else {
layoutResource = R.layout.screen_custom_title;
}
removeFeature(FEATURE_ACTION_BAR);
} else if ((features & (1 << FEATURE_NO_TITLE)) == 0) {
if (mIsFloating) {
TypedValue res = new TypedValue();
getContext().getTheme().resolveAttribute(
R.attr.dialogTitleDecorLayout, res, true);
layoutResource = res.resourceId;
} else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) {
layoutResource = a.getResourceId(
R.styleable.Window_windowActionBarFullscreenDecorLayout,
R.layout.screen_action_bar);
} else {
layoutResource = R.layout.screen_title;
}
} 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();
// 3.加载资源
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
...
return contentParent;
}
generateLayout
办法中的代码有足足几百行,其实从办法姓名也能看出来,这个办法是用来生成布局的。首先会依据开发设置的一些主题来调整咱们的布局,然后便是给咱们的窗口 Window
进行装饰啦。能够看到,依据不同的状况会运用不同的布局。最终在注释 3 处,mDecor
目标会依据选定的 layoutResource
来加载布局,咱们随意点一个 layout 进去康康布局长什么样?
<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:id="@android:id/title_container"
android:layout_width="match_parent"
android:layout_height="?android:attr/windowTitleSize"
android:transitionName="android:title"
style="?android:attr/windowTitleBackgroundStyle">
</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>
我这儿进入的是 R.layout.screen_custom_title
,能够看到布局中供给了一个 ActionBar
,两个 FrameLayout
分别用来显现 title
和 content
部分的。实际上咱们在开发时所写的布局便是展示在content
中的,一般咱们不会运用 ActionBar
或者 title
,而是在使用中自己去完成标题。这也是为什么 Activity 中叫 setContentView
,因为咱们操作的是 content
部分。
咱们能够用一张图来表明 Activity
和 DecorView
之间的联系:
2. 总结
依据上面的剖析,信任你现已理解了 DecorView
作为根 View
是如何被创立以及加载的,为了更明晰简单用流程图表达下:
-
Activity
在attch
时,会创立PhoneWindow
目标,在onCreate
履行其setContentView
办法; -
setContentView
中会运用installDecor
来创立一个DecorView
目标作为根View
; - 得到了
DecorView
目标后,会经过generateLayout
办法获取对应的资源 id,DecorView
会依据该 id 来加载不同的布局; -
DecorView
作为一个顶级 View,一般状况下内部会包括一个LinearLayout
,并将屏幕划分红TitleView
和ContentView
两部分。