基于 Android 9.0 源码。
SystemUI 的 StatusBar
和 NavigationBar
是在 PhoneWindowManager
中布局的。PhoneWindowManager
的 mLastSystemUiFlags
字段中保存着 SystemUI 的 visibility,visibility 指的是可见、半透明、透明等等。
mLastSystemUiFlags
字段是在 updateSystemUiVisibilityLw()
方法中赋值的,而新的 visibility 值是调用 updateSystemBarsLw()
方法返回的值。
private int updateSystemUiVisibilityLw() {
// If there is no window focused, there will be nobody to handle the events
// anyway, so just hang on in whatever state we're in until things settle down.
WindowState winCandidate = mFocusedWindow != null ? mFocusedWindow
: mTopFullscreenOpaqueWindowState;
if (winCandidate == null) {
return 0;
}
if (winCandidate.getAttrs().token == mImmersiveModeConfirmation.getWindowToken()) {
// The immersive mode confirmation should never affect the system bar visibility,
// otherwise it will unhide the navigation bar and hide itself.
winCandidate = isStatusBarKeyguard() ? mStatusBar : mTopFullscreenOpaqueWindowState;
if (winCandidate == null) {
return 0;
}
}
final WindowState win = winCandidate;
...
}
SystemUI 的 visibility 受 winCandidate
的影响,当 mFocusedWindow
(获得焦点的 Window)不为 null 时,winCandidate
是 mFocusedWindow
,否则是 mTopFullscreenOpaqueWindowState
。mTopFullscreenOpaqueWindowState
是布局在最上层,且不透明的全屏的应用的 Window。它是在 applyPostLayoutPolicyLw()
方法中赋值的。
如果当前的 Window 是 immersive mode confirmation window,则使用 mTopFullscreenOpaqueWindowState
,因为沉浸式确认窗口应该影响 system bar 的 visibility。
private int updateSystemUiVisibilityLw() {
...
int tmpVisibility = PolicyControl.getSystemUiVisibility(win, null)
& ~mResettingSystemUiFlags
& ~mForceClearedSystemUiFlags;
if (mForcingShowNavBar && win.getSurfaceLayer() < mForcingShowNavBarLayer) {
tmpVisibility &= ~PolicyControl.adjustClearableFlags(win, View.SYSTEM_UI_CLEARABLE_FLAGS);
}
...
}
tmpVisibility 由三个部分设置。第一和第二是在 PolicyControl.getSystemUiVisibility()
方法中。第三部分是需求强制取消的 flag,如 mResettingSystemUiFlags
、mForceClearedSystemUiFlags
,SYSTEM_UI_CLEARABLE_FLAGS
常量包含隐藏 StatusBar 和 NavigationBar 的 flag。
public static int getSystemUiVisibility(WindowState win, LayoutParams attrs) {
attrs = attrs != null ? attrs : win.getAttrs();
int vis = win != null ? win.getSystemUiVisibility()
: (attrs.systemUiVisibility | attrs.subtreeSystemUiVisibility);
if (sImmersiveStatusFilter != null && sImmersiveStatusFilter.matches(attrs)) {
vis |= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
| View.SYSTEM_UI_FLAG_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
vis &= ~(View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.STATUS_BAR_TRANSLUCENT);
}
if (sImmersiveNavigationFilter != null && sImmersiveNavigationFilter.matches(attrs)) {
vis |= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
vis &= ~(View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.NAVIGATION_BAR_TRANSLUCENT);
}
return vis;
}
为什么说该方法有两个部分,因为当 win != null
时,会调用 win 的 getSystemUiVisibility()
方法,这其实就是当一个 View 调用 setSystemUiVisibility()
方法设置的 flag,这是一部分。
另一部分是 sImmersiveStatusFilter
和 sImmersiveNavigationFilter
相关的逻辑,这是与强制沉浸模式(immersive mode)或沉浸模式无效相关。
fullscreenVisibility
主要由 mTopFullscreenOpaqueWindowState
影响,dockedVisibility
主要由 mTopDockedOpaqueWindowState
影响。第一个 WindowState 指的是最上层全屏覆盖(占满应用 Frame)的窗口,第二个 WindowState 指的是分屏状态下被固定的那一侧的窗口(按 Home 后不会回到 Home 的那一侧)。
private int updateSystemUiVisibilityLw() {
...
final int fullscreenVisibility = updateLightStatusBarLw(0 /* vis */,
mTopFullscreenOpaqueWindowState, mTopFullscreenOpaqueOrDimmingWindowState);
final int dockedVisibility = updateLightStatusBarLw(0 /* vis */,
mTopDockedOpaqueWindowState, mTopDockedOpaqueOrDimmingWindowState);
mWindowManagerFuncs.getStackBounds(
WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME, mNonDockedStackBounds);
mWindowManagerFuncs.getStackBounds(
WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, mDockedStackBounds);
...
}
然后调用 getStackBounds()
方法设置 mNonDockedStackBounds
和 mDockedStackBounds
。该方法会根据 windowingMode
和 activityMode
查找对应的 TaskStack 对象,然后用该对象的 getBounds()
设置传递的 bounds。
updateSystemBarsLw()
方法是计算新的 visibility 的方法,传入参数 winCandidate
、mLastSystemUiFlags
和 tmpVisibility
。