本篇文章主要从系统和应用两个层次分析 Window 的添加过程,以及论述重要组件之间的关系:
1.SystemUI (如 StatusBar) 和 Activity 中 Window 的创建和添加
2.Activity / PhoneWindow / DecorView / StatusBar / ViewRootImpl 之间的关系
涉及代码如下:
frameworks/base/packages/SystemUI/src/com/android/systemui/SystemBars.java
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
frameworks/base/core/java/android/app/ContextImpl.java
frameworks/base/core/java/android/app/ActivityThread.java
frameworks/base/core/java/android/app/Activity.java
frameworks/base/core/java/android/view/WindowManagerImpl.java
frameworks/base/core/java/android/view/Window.java
frameworks/base/core/java/com/android/internal/policy/PhoneWindow.java
frameworks/base/core/java/com/android/internal/policy/DecorView.java
frameworks/base/packages/SystemUI/AndroidManifest.xml
frameworks/base/packages/SystemUI/res/values/config.xml
frameworks/base/packages/SystemUI/res/layout/super_status_bar.xml
frameworks/base/packages/SystemUI/res/layout/status_bar.xml
frameworks/base/core/res/res/layout/screen_simple.xml
SystemUI 包括很多子类, 状态栏 StatusBar 是最常见的一种。本文以 StatusBar 为例进行分析。
SystemUI 运行的进程名是 SystemUIApplication。
frameworks/base/packages/SystemUI/AndroidManifest.xml
<application
android:name=".SystemUIApplication"
android:persistent="true"
android:allowClearUserData="false"
android:allowBackup="false"
android:hardwareAccelerated="true"
android:label="@string/app_label"
android:icon="@drawable/icon"
android:process="com.android.systemui"
android:supportsRtl="true"
android:theme="@style/Theme.SystemUI"
android:defaultToDeviceProtectedStorage="true"
android:directBootAware="true"
android:appComponentFactory="androidx.core.app.CoreComponentFactory">
...
<service android:name="SystemUIService" android:exported="true"/>
...
</application>
SystemUI 调用链:
SystemUIApplication.onCreate -> SystemUIService.onCreate -> SystemUIApplication.startServicesIfNeeded -> mServices[i].start() -> SystemBars.start
frameworks/base/packages/SystemUI/src/com/android/systemui/SystemBars.java
public class SystemBars extends SystemUI {
private SystemUI mStatusBar;
@Override
public void start() {
createStatusBarFromConfig();
}
private void createStatusBarFromConfig() {
// 从config.xml中读取className-"com.android.systemui.statusbar.phone.StatusBar"
final String clsName = mContext.getString(R.string.config_statusBarComponent);
// 反射创建StatusBar对象
Class<?> cls = mContext.getClassLoader().loadClass(clsName);
mStatusBar = (SystemUI) cls.newInstance();
mStatusBar.mContext = mContext;
mStatusBar.mComponents = mComponents;
// 将StatusBar注入系统UI的根组件
if (mStatusBar instanceof StatusBar) {
SystemUIFactory.getInstance().getRootComponent()
.getStatusBarInjector()
.createStatusBar((StatusBar) mStatusBar);
}
// 创建状态栏View, 并将其添加到WindowManager
mStatusBar.start();
}
}
SystemBars.start 做的事情:
1.读取配置文件中的属性 config_statusBarComponent,得到字符串 className 为 StatusBar 的字符串
2.利用反射创建 StatusBar 对象,并调用 StatusBar 的 start 方法用来创建状态栏 View 并将其添加到 WindowManager 中
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
public class StatusBar extends SystemUI implements DemoMode,
ActivityStarter, OnUnlockMethodChangedListener,
OnHeadsUpChangedListener, CommandQueue.Callbacks, ZenModeController.Callback,
ColorExtractor.OnColorsChangedListener, ConfigurationListener,
StatusBarStateController.StateListener, ShadeController,
ActivityLaunchAnimator.Callback, AmbientPulseManager.OnAmbientChangedListener,
AppOpsController.Callback {
protected WindowManager mWindowManager;
protected IWindowManager mWindowManagerService;
// 状态栏最外层View
protected StatusBarWindowView mStatusBarWindow;
// CollapsedStatusBarFragment的根View(即StatusBarWindowView的childView)
protected PhoneStatusBarView mStatusBarView;
@Override
public void start() {
......
// 获取WindowManagerImpl
mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
// 获取IWindowManager.Stub.Proxy
mWindowManagerService = WindowManagerGlobal.getWindowManagerService();
......
// 创建状态栏View, 并将其添加到WindowManager
createAndAddWindows(result);
......
}
......
}
// 创建状态栏View, 并将其添加到WindowManager
public void createAndAddWindows(@Nullable RegisterStatusBarResult result) {
// 根据布局文件super_status_bar.xml创建StatusBarWindowView.
makeStatusBarView(result);
mStatusBarWindowController = Dependency.get(StatusBarWindowController.class);
// 将StatusBarWindowView添加到WindowManager.
mStatusBarWindowController.add(mStatusBarWindow, getStatusBarHeight());
}
// 获取状态栏高度, App就是借鉴这个方法
public int getStatusBarHeight() {
if (mNaturalBarHeight < 0) {
final Resources res = mContext.getResources();
mNaturalBarHeight =
res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
}
return mNaturalBarHeight;
}
这里要提一下:
getStatusBarHeight 这个方法,App 开发中获取状态栏高度就是借鉴的这个方法,只不过是反射获取该属性。基本思路就是看源码中 StatusBar 对应的布局,然后读取其高度属性。
protected void makeStatusBarView(@Nullable RegisterStatusBarResult result) {
......
// 根据布局文件super_status_bar.xml创建StatusBarWindowView
inflateStatusBarWindow(context);
mStatusBarWindow.setService(this);
mStatusBarWindow.setOnTouchListener(getStatusBarWindowTouchListener());
......
// status_bar_container位置填充CollapsedStatusBarFragment
// CollapsedStatusBarFragment对应的布局文件为status_bar.xml
FragmentHostManager.get(mStatusBarWindow).
addTagListener(CollapsedStatusBarFragment.TAG, (tag, fragment) -> {
// Fragment创建完成时, 初始化
CollapsedStatusBarFragment statusBarFragment =
(CollapsedStatusBarFragment) fragment;
statusBarFragment.initNotificationIconArea(mNotificationIconAreaController);
PhoneStatusBarView oldStatusBarView = mStatusBarView;
mStatusBarView = (PhoneStatusBarView) fragment.getView();
mStatusBarView.setBar(this);
mStatusBarView.setPanel(mNotificationPanel);
mStatusBarView.setScrimController(mScrimController);
mStatusBarView.setBouncerShowing(mBouncerShowing);
if (oldStatusBarView != null) {
float fraction = oldStatusBarView.getExpansionFraction();
boolean expanded = oldStatusBarView.isExpanded();
mStatusBarView.panelExpansionChanged(fraction, expanded);
}
......
mStatusBarWindow.setStatusBarView(mStatusBarView);
......
}).getFragmentManager()
.beginTransaction()
.replace(R.id.status_bar_container, new CollapsedStatusBarFragment(),
CollapsedStatusBarFragment.TAG)
.commit();
......
// 创建导航栏(仅针对CarStatusBar)
createNavigationBar(result);
......
}
protected void inflateStatusBarWindow(Context context) {
// 从super_status_bar.xml布局文件创建StatusBarWindowView
mStatusBarWindow = (StatusBarWindowView) mInjectionInflater.injectable(
LayoutInflater.from(context)).inflate(R.layout.super_status_bar, null);
}
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
// Adds the status bar view to the window manager.
public void add(ViewGroup statusBarView, int barHeight) {
mLp = new WindowManager.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
barHeight,
WindowManager.LayoutParams.TYPE_STATUS_BAR,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
| WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
| WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
PixelFormat.TRANSLUCENT);
mLp.token = new Binder();
mLp.gravity = Gravity.TOP;
mLp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
mLp.setTitle("StatusBar");
mLp.packageName = mContext.getPackageName();
mLp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
mStatusBarView = statusBarView;
mBarHeight = barHeight;
mWindowManager.addView(mStatusBarView, mLp);
mLpChanged.copyFrom(mLp);
onThemeChanged();
}
总结 StatusBar.start 做的事情:
一句话总结就是创建状态栏 View 并将其添加到 WindowManager 中。
具体过程为:
frameworks/base/core/java/android/app/ActivityThread.java
private Activity performLaunchActivity(ActivityClientRecord r,
Intent customIntent) {
......
try {
......
// 将Activity attach到Application
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
r.referrer, r.voiceInteractor, window, r.configCallback,
r.assistToken);
......
}
......
return activity;
}
frameworks/base/core/java/android/app/Activity.java
final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, int ident,
......) {
attachBaseContext(context);
......
//创建PhoneWindow
mWindow = new PhoneWindow(this, window, activityConfigCallback);
mWindow.setWindowControllerCallback(this);
mWindow.setCallback(this);
mWindow.setOnWindowDismissedCallback(this);
mWindow.getLayoutInflater().setPrivateFactory(this);
......
mUiThread = Thread.currentThread();
mMainThread = aThread;
mInstrumentation = instr;
mToken = token;
......
//设置WindowManagerImpl
mWindow.setWindowManager(
(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
mToken, mComponent.flattenToString(),
(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
//设置父Window
if (mParent != null) {
mWindow.setContainer(mParent.getWindow());
}
// 获取本地创建的WindowManagerImpl
//(这个和上面setWindowManager设置进来不是同一个实例)
mWindowManager = mWindow.getWindowManager();
......
}
在 Activity 启动过程中我们知道执行完 attach 方法后,就要执行回调 Activity 的 onCreate 方法了,我们会在 onCreate 方法中使用 setContentView(layoutResID),来完成布局文件的加载,那么我们来看下 setContentView 这个方法的执行流程。
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
@Override
public void setContentView(int layoutResID) {
if (mContentParent == null) {
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
......
} else {
// 将布局layoutResID添加到DecorView中id为R.id.content的位置
mLayoutInflater.inflate(layoutResID, mContentParent);
}
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
mContentParentExplicitlySet = true;
}
@Override
public void handleResumeActivity(IBinder token, ......) {
......
final ActivityClientRecord r =
performResumeActivity(token, finalStateRequest, reason);
final Activity a = r.activity;
......
//当Window还未添加到WindowManager, 且还未finish当前Activity或
//未启动新Activity时, 需要先添加到WindowManager
boolean willBeVisible = !a.mStartedActivity;
if (!willBeVisible) {
willBeVisible = ActivityTaskManager.getService().
willActivityBeVisible(a.getActivityToken());
}
if (r.window == null && !a.mFinished && willBeVisible) {
// 拿到PhoneWindow
r.window = r.activity.getWindow();
// 拿到DecorView
View decor = r.window.getDecorView();
// 设置DecorView不可见
decor.setVisibility(View.INVISIBLE);
// 获取本地创建的WindowManagerImpl
ViewManager wm = a.getWindowManager();
// 设置Window各属性(如type为TYPE_BASE_APPLICATION)
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
l.softInputMode |= forwardBit;
if (r.mPreserveWindow) {
a.mWindowAdded = true;
r.mPreserveWindow = false;
// 获取DecorView的ViewPootImpl(每个View都有一个ViewRootImpl)
ViewRootImpl impl = decor.getViewRootImpl();
// 通知子View已经被重建(DecorView还是旧实例,
// Activity是新实例, 因此需要更新callbacks)
// 1.通常情况下, ViewRootImpl通过
//WindowManagerImpl#addView->ViewRootImpl#setView
//设置Activity的callbacks回调
// 2.但是如果DecorView复用时, 需要主动告诉ViewRootImpl callbacks可能发生变化
if (impl != null) {
impl.notifyChildRebuilt();
}
}
// 当Activity可见时, 添加DecorView到WindowManagerImpl
//或回调Window属性变化方法
if (a.mVisibleFromClient) {
if (!a.mWindowAdded) {
a.mWindowAdded = true;
wm.addView(decor, l);
} else {
a.onWindowAttributesChanged(l);
}
}
} else if (!willBeVisible) {
r.hideForNow = true;
}
// Get rid of anything left hanging around.
cleanUpPendingRemoveWindows(r, false /* force */);
if (!r.activity.mFinished && willBeVisible &&
r.activity.mDecor != null && !r.hideForNow) {
// 调用Activity.onConfigurationChanged方法
if (r.newConfig != null) {
performConfigurationChangedForActivity(r, r.newConfig);
r.newConfig = null;
}
// 更新DecorView的LayoutParams
WindowManager.LayoutParams l = r.window.getAttributes();
if ((l.softInputMode &
WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION)
!= forwardBit) {
l.softInputMode = (l.softInputMode &
(~WindowManager.LayoutParams.
SOFT_INPUT_IS_FORWARD_NAVIGATION)) |
forwardBit;
if (r.activity.mVisibleFromClient) {
ViewManager wm = a.getWindowManager();
View decor = r.window.getDecorView();
wm.updateViewLayout(decor, l);
}
}
r.activity.mVisibleFromServer = true;
mNumVisibleActivities++;
// 设置Activity可见
if (r.activity.mVisibleFromClient) {
r.activity.makeVisible();
}
}
r.nextIdle = mNewActivities;
mNewActivities = r;
Looper.myQueue().addIdleHandler(new Idler());
}
在 Activity 创建过程中,会创建和添加 PhoneWindow.
Activity 中 PhoneWindow 的创建和添加过程:
1.handleLaunchActivity 过程
2.handleResumeActivity 过程
我们已经分析了 Activity 中 PhoneWindow 的创建和添加过程,在 Activity#onCreate 中会调用PhoneWindow#installDecor 创建 DecorView 和子 View。接下来我们来分析下 PhoneWindow 和 DecorView 的组成。
frameworks/base/core/java/com/android/internal/policy/PhoneWindow.java
// 创建DecorView及其子View
private void installDecor() {
mForceDecorInstall = false;
if (mDecor == null) {
// 创建DecorView
mDecor = generateDecor(-1);
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
mDecor.setIsRootNamespace(true);
......
} else {
mDecor.setWindow(this);
}
if (mContentParent == null) {
// 根据设置的Window相关属性, 设置PhoneWindow特性
//给PhoneWindow的根DecorView添加子View, 并返回ContentView
mContentParent = generateLayout(mDecor);
......
// 对于R.layout.screen_simple没有该元素
// 但对于R.layout.screen_action_bar包含该元素
final DecorContentParent decorContentParent =
(DecorContentParent) mDecor.findViewById(R.id.decor_content_parent);
// 如果根View为ActionBarOverlayLayout, 则设置
// ActionBarOverlayLayout的title/icon/logo/menu等
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);
}
PanelFeatureState st =
getPanelState(FEATURE_OPTIONS_PANEL, false);
if (!isDestroyed() && (st == null || st.menu == null)
&& !mIsStartingWindow) {
invalidatePanelMenu(FEATURE_ACTION_BAR);
}
} else {
// 如果有title元素, 更新title
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);
}
}
}
......
}
}
protected DecorView generateDecor(int featureId) {
......
Context context;
if (mUseDecorContext) {
Context applicationContext =
getContext().getApplicationContext();
if (applicationContext == null) {
context = getContext();
} else {
context = new DecorContext(applicationContext, getContext());
if (mTheme != -1) {
context.setTheme(mTheme);
}
}
} else {
context = getContext();
}
return new DecorView(context, featureId, this, getAttributes());
}
protected ViewGroup generateLayout(DecorView decor) {
// Apply data from current theme.
TypedArray a = getWindowStyle();
mIsFloating =
a.getBoolean(R.styleable.Window_windowIsFloating, false);
int flagsToUpdate = (FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR)
& (~getForcedWindowFlags());
if (mIsFloating) {
setLayout(WRAP_CONTENT, WRAP_CONTENT);
setFlags(0, flagsToUpdate);
} else {
setFlags(FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR,
flagsToUpdate);
}
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);
}
......
final Context context = getContext();
......
WindowManager.LayoutParams params = getAttributes();
......
// Inflate the window decor.
int layoutResource;
int features = getLocalFeatures();
// System.out.println("Features: 0x" + Integer.toHexString(features));
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) {
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) {
TypedValue res = new TypedValue();
getContext().getTheme().resolveAttribute(
R.attr.dialogCustomTitleDecorLayout, res, true);
layoutResource = res.resourceId;
} else {
layoutResource = R.layout.screen_custom_title;
}
// XXX Remove this once action bar supports these features.
removeFeature(FEATURE_ACTION_BAR);
} else if ((features & (1 << FEATURE_NO_TITLE)) == 0) {
// If no other features and not embedded, only need a title.
// If the window is floating, we need a dialog layout
if (mIsFloating) {
......
} 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;
}
// System.out.println("Title!");
} else 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();
// DecorView添加子View. 从父到子依次是:DecorView ->
//DecorCaptionView(不一定包含) -> root(即layoutResource对应的View)
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
// 获取R.id.content对应的ContentView
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
......
// 仅最顶层Window执行
if (getContainer() == null) {
mDecor.setWindowBackground(mBackgroundDrawable);
final Drawable frame;
if (mFrameResource != 0) {
frame = getContext().getDrawable(mFrameResource);
} else {
frame = null;
}
mDecor.setWindowFrame(frame);
mDecor.setElevation(mElevation);
mDecor.setClipToOutline(mClipToOutline);
if (mTitle != null) {
setTitle(mTitle);
}
if (mTitleColor == 0) {
mTitleColor = mTextColor;
}
setTitleColor(mTitleColor);
}
mDecor.finishChanging();
return contentParent;
}
frameworks/base/core/java/com/android/internal/policy/DecorView.java
void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
if (mBackdropFrameRenderer != null) {
loadBackgroundDrawablesIfNeeded();
mBackdropFrameRenderer.onResourcesLoaded(
this, mResizingBackgroundDrawable, ......);
}
// 创建DecorCaptionView(即包含系统按钮如最大化,关闭等的标题)
mDecorCaptionView = createDecorCaptionView(inflater);
final View root = inflater.inflate(layoutResource, null);
if (mDecorCaptionView != null) {
// 从父到子依次是:DecorView -> DecorCaptionView -> root
if (mDecorCaptionView.getParent() == null) {
addView(mDecorCaptionView,
new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
}
mDecorCaptionView.addView(root,
new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));
} else {
// 从父到子依次是:DecorView -> root
addView(root, 0,
new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
}
mContentRoot = (ViewGroup) root;
initializeElevation();
}
private DecorCaptionView createDecorCaptionView(LayoutInflater inflater) {
DecorCaptionView decorCaptionView = null;
for (int i = getChildCount() - 1; i >= 0 &&
decorCaptionView == null; i--) {
View view = getChildAt(i);
if (view instanceof DecorCaptionView) {
// The decor was most likely saved from a relaunch - so reuse it.
decorCaptionView = (DecorCaptionView) view;
removeViewAt(i);
}
}
final WindowManager.LayoutParams attrs = mWindow.getAttributes();
final boolean isApplication = attrs.type == TYPE_BASE_APPLICATION ||
attrs.type == TYPE_APPLICATION ||
attrs.type == TYPE_DRAWN_APPLICATION;
final WindowConfiguration winConfig =
getResources().getConfiguration().windowConfiguration;
if (!mWindow.isFloating() && isApplication &&
winConfig.hasWindowDecorCaption()) {
if (decorCaptionView == null) {
decorCaptionView = inflateDecorCaptionView(inflater);
}
decorCaptionView.setPhoneWindow(mWindow, true /*showDecor*/);
} else {
decorCaptionView = null;
}
// Tell the decor if it has a visible caption.
enableCaption(decorCaptionView != null);
return decorCaptionView;
}
总结 PhoneWindow.installDecor:
该方法在 Activity#setContentView(layoutResID) 里调用,用来在 PhoneWindow 创建 DecorView 及其子 View。
PhoneWindow#installDecor 具体步骤:
1.generateDecor:创建 DecorView (是一个 FrameLayout )
2.generateLayout:根据设置的 Window 相关属性,设置 PhoneWindow 特性。给 PhoneWindow 的根 DecorView 添加子 View,并返回 ContentView
DecorCaptionView 即包含系统按钮如最大化,关闭等的标题,此处不做详细介绍。
frameworks/base/core/res/res/layout/screen_simple.xml
<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>
上图是根据源码总结出来的各个类的关系图。主要包含一下三部分:
1.StatusBar
状态栏 (系统 UI ),其根 View 为 StatusBarWindowView,通过 WindowManagerImpl.addView 添加到窗口管理器
2.DecorView
(1) 应用程序 View. 通过 WindowManagerImpl.addView 添加到窗口管理器. 一个页面对应一个 Activity,一个 Activity 包含一个 PhoneWindow,PhoneWindow 的根 View 即为 DecorView,DecorView 为 FrameLayout
(2) 根据创建 Activity 时设置的 Window 属性的不同,选择不同的 layout 布局,并将该 layout 布局添加到 DecorView 中。最简单的为 R.layout.screen_simple
(3) 上述 layout 布局 R.layout.screen_simple 为 LinearLayout,包含2个元素: @id/action_mode_bar_stub 和 @android:id/content。即标题栏和内容体。在 Activity#onCreate -> setContentView(layoutResID) 调用时,会将layoutResID 填充到 @android:id/content
3.NavigationBar
通常指的手机底部的虚拟按键。
上面我们已经知道 StatusBar 和 DecorView 都会添加到 WindowManagerImpl,通过窗口管理器进行管理。因此开发者可以定制状态栏和应用程序的 UI 样式及他们之间的显示关系。
下面以沉浸式状态栏实现为例:
我们实现这样的效果需要遵循几个步骤:
(1) 状态栏透明
(2) DecorView 占满整个屏幕
(3) NavigationBar 的控制
实现代码如下:
private static void transparentStatusBar(final Activity activity) {
Window window = activity.getWindow();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
// Android 5.0及以上, 设置DecorView全屏和系统状态栏透明
window.addFlags(WindowManager.
LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
int option = View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
window.getDecorView().setSystemUiVisibility(option);
window.setStatusBarColor(Color.TRANSPARENT);
} else {
// Android 4.4 设置系统状态栏透明(不能实现DecorView全屏)
// 由于Android 4.4不存在PhoneWindow#setStatusBarColor这个方法.
// 因此设置透明状态栏需要在DecorView添加一个AlphaStatusBarView
addStatusBarAlpha(activity, 0)
window.addFlags(WindowManager.
LayoutParams.FLAG_TRANSLUCENT_STATUS);
}
}
// 仅仅用于Android 4.4
private static void addStatusBarAlpha(final Activity activity,
final int alpha) {
ViewGroup parent = (ViewGroup)
activity.getWindow().getDecorView()
View fakeStatusBarView = parent.findViewWithTag(TAG_ALPHA);
if (fakeStatusBarView != null) {
if (fakeStatusBarView.getVisibility() == View.GONE) {
fakeStatusBarView.setVisibility(View.VISIBLE);
}
fakeStatusBarView.setBackgroundColor(
Color.argb(alpha, 0, 0, 0));
} else {
parent.addView(createAlphaStatusBarView(
parent.getContext(), alpha));
}
}
// 仅仅用于Android 4.4
private static View createAlphaStatusBarView(final Context context,
final int alpha) {
View statusBarView = new View(context);
statusBarView.setLayoutParams(new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, getStatusBarHeight()));
statusBarView.setBackgroundColor(Color.argb(alpha, 0, 0, 0));
statusBarView.setTag(TAG_ALPHA);
return statusBarView;
}
系统 UI 的可见性 :
系统 UI (如 StatusBar 和 NavigationBar),可以在 Activity 中通过 DecorView#setSystemUiVisibility 控制。
(1) 设置 DecorView 全屏
View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
(2) 隐藏 NavigationBar
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
(3) 其他参考 View.SYSTEM_UI_FLAG_XXX