基于 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。