前言
NavigationBar 和 StatusBar 都属于 SystemBar,也叫做 decor,就是说给 App 装饰的意思。在Android12中,一般的window的是在 DisplayPolicy的layoutWindowLw方法中布局的,而导航栏和状态栏分别是在DisplayPolicy的layoutNavigationBar和layoutStatusBar方法中布局的。
一、简单认识Android12中的DisplayFrames
mDisplayId 是跟物理屏幕相关的,DEFAULT_DISPLAY 的值是 0,mDisplayWidth 是物理屏幕宽度,mDisplayHeight 是物理屏幕高度。
public class DisplayFrames {
public final int mDisplayId;
public final InsetsState mInsetsState;
/**
* The current visible size of the screen; really; (ir)regardless of whether the status bar can
* be hidden but not extending into the overscan area.
*/
public final Rect mUnrestricted = new Rect();
/**
* During layout, the frame that is display-cutout safe, i.e. that does not intersect with it.
*/
public final Rect mDisplayCutoutSafe = new Rect();
public int mDisplayWidth;
public int mDisplayHeight;
public int mRotation;
public DisplayFrames(int displayId, InsetsState insetsState, DisplayInfo info,
WmDisplayCutout displayCutout, RoundedCorners roundedCorners,
PrivacyIndicatorBounds indicatorBounds) {
mDisplayId = displayId;
mInsetsState = insetsState;
onDisplayInfoUpdated(info, displayCutout, roundedCorners, indicatorBounds);
}
/**
* Update {@link DisplayFrames} when {@link DisplayInfo} is updated.
*
* @param info the updated {@link DisplayInfo}.
* @param displayCutout the updated {@link DisplayCutout}.
* @param roundedCorners the updated {@link RoundedCorners}.
* @return {@code true} if the insets state has been changed; {@code false} otherwise.
*/
public boolean onDisplayInfoUpdated(DisplayInfo info, @NonNull WmDisplayCutout displayCutout,
@NonNull RoundedCorners roundedCorners,
@NonNull PrivacyIndicatorBounds indicatorBounds) {
mRotation = info.rotation;
final InsetsState state = mInsetsState;
final Rect safe = mDisplayCutoutSafe;
final DisplayCutout cutout = displayCutout.getDisplayCutout();
if (mDisplayWidth == info.logicalWidth && mDisplayHeight == info.logicalHeight
&& state.getDisplayCutout().equals(cutout)
&& state.getRoundedCorners().equals(roundedCorners)
&& state.getPrivacyIndicatorBounds().equals(indicatorBounds)) {
return false;
}
mDisplayWidth = info.logicalWidth;
mDisplayHeight = info.logicalHeight;
final Rect unrestricted = mUnrestricted;
unrestricted.set(0, 0, mDisplayWidth, mDisplayHeight);
safe.set(Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE);
state.setDisplayFrame(unrestricted);
state.setDisplayCutout(cutout);
state.setRoundedCorners(roundedCorners);
state.setPrivacyIndicatorBounds(indicatorBounds);
if (!cutout.isEmpty()) {
if (cutout.getSafeInsetLeft() > 0) {
safe.left = unrestricted.left + cutout.getSafeInsetLeft();
}
if (cutout.getSafeInsetTop() > 0) {
safe.top = unrestricted.top + cutout.getSafeInsetTop();
}
if (cutout.getSafeInsetRight() > 0) {
safe.right = unrestricted.right - cutout.getSafeInsetRight();
}
if (cutout.getSafeInsetBottom() > 0) {
safe.bottom = unrestricted.bottom - cutout.getSafeInsetBottom();
}
state.getSource(ITYPE_LEFT_DISPLAY_CUTOUT).setFrame(
unrestricted.left, unrestricted.top, safe.left, unrestricted.bottom);
state.getSource(ITYPE_TOP_DISPLAY_CUTOUT).setFrame(
unrestricted.left, unrestricted.top, unrestricted.right, safe.top);
state.getSource(ITYPE_RIGHT_DISPLAY_CUTOUT).setFrame(
safe.right, unrestricted.top, unrestricted.right, unrestricted.bottom);
state.getSource(ITYPE_BOTTOM_DISPLAY_CUTOUT).setFrame(
unrestricted.left, safe.bottom, unrestricted.right, unrestricted.bottom);
} else {
state.removeSource(ITYPE_LEFT_DISPLAY_CUTOUT);
state.removeSource(ITYPE_TOP_DISPLAY_CUTOUT);
state.removeSource(ITYPE_RIGHT_DISPLAY_CUTOUT);
state.removeSource(ITYPE_BOTTOM_DISPLAY_CUTOUT);
}
return true;
}
public void dumpDebug(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
proto.end(token);
}
public void dump(String prefix, PrintWriter pw) {
pw.println(prefix + "DisplayFrames w=" + mDisplayWidth + " h=" + mDisplayHeight
+ " r=" + mRotation);
}
}
二、构建SystemBar对应的视图窗口区域坐标对象
1、系统主要是在DisplayPolicy的layoutWindowLw() 方法中构建SystemBar对应的视图窗口区域坐标对象的。
frameworks/base/services/core/java/com/android/server/wm/DisplayPolicy.java
public class DisplayPolicy {
public void layoutWindowLw(WindowState win, WindowState attached, DisplayFrames displayFrames) {
Log.d("SystemBar", "DisplayPolicy_layoutWindowLw");
if (win == mNavigationBar && !INSETS_LAYOUT_GENERALIZATION) {
Log.d("SystemBar", "DisplayPolicy_layoutNavigationBar");
mNavigationBarPosition = layoutNavigationBar(displayFrames,
mBarContentFrames.get(TYPE_NAVIGATION_BAR));
return;
}
if ((win == mStatusBar && !canReceiveInput(win)) && !INSETS_LAYOUT_GENERALIZATION) {
Log.d("SystemBar", "DisplayPolicy_layoutStatusBar");
layoutStatusBar(displayFrames, mBarContentFrames.get(TYPE_STATUS_BAR));
return;
}
...代码省略...
}
}
layoutWindowLw方法中首先会判断win的类型,然后调用layoutNavigationBar方法和layoutStatusBar方法构建NavigationBar和StatusBar所对应的视图窗口区域坐标。
三、导航栏视图窗口区域坐标对象的构建
构建导航栏视图窗口区域坐标的layoutNavigationBar方法r如下所示。
public class DisplayPolicy {
private int layoutNavigationBar(DisplayFrames displayFrames, Rect contentFrame) {
if (mNavigationBar == null) {
return NAV_BAR_INVALID;
}
final int uiMode = mDisplayContent.getConfiguration().uiMode;
final Rect navigationFrame = sTmpNavFrame;
// Force the navigation bar to its appropriate place and size. We need to do this directly,
// instead of relying on it to bubble up from the nav bar, because this needs to change
// atomically with screen rotations.
final int rotation = displayFrames.mRotation;
final int displayHeight = displayFrames.mDisplayHeight;
final int displayWidth = displayFrames.mDisplayWidth;
final int navBarPosition = navigationBarPosition(displayWidth, displayHeight, rotation);
getRotatedWindowBounds(displayFrames, mNavigationBar, navigationFrame);
final Rect cutoutSafeUnrestricted = sTmpRect;
cutoutSafeUnrestricted.set(displayFrames.mUnrestricted);
cutoutSafeUnrestricted.intersectUnchecked(displayFrames.mDisplayCutoutSafe);
if (navBarPosition == NAV_BAR_BOTTOM) {
// It's a system nav bar or a portrait screen; nav bar goes on bottom.
int temp_bottom = Math.min(cutoutSafeUnrestricted.bottom, navigationFrame.bottom);
int navigationBarFrameHeight = getNavigationBarFrameHeight(rotation, uiMode);
navigationFrame.top = temp_bottom - navigationBarFrameHeight;
Log.d("SystemBar","layoutNavigationBar temp_bottom=" + temp_bottom
+ " navigationBarFrameHeight=" + navigationBarFrameHeight
+ " navigationFrame.top=" + navigationFrame.top
);
} else if (navBarPosition == NAV_BAR_RIGHT) {
// Landscape screen; nav bar goes to the right.
navigationFrame.left = Math.min(cutoutSafeUnrestricted.right, navigationFrame.right)
- getNavigationBarWidth(rotation, uiMode, navBarPosition);
} else if (navBarPosition == NAV_BAR_LEFT) {
// Seascape screen; nav bar goes to the left.
navigationFrame.right = Math.max(cutoutSafeUnrestricted.left, navigationFrame.left)
+ getNavigationBarWidth(rotation, uiMode, navBarPosition);
}
// Compute the final frame.
final WindowFrames windowFrames = mNavigationBar.getLayoutingWindowFrames();
windowFrames.setFrames(navigationFrame /* parentFrame */,
navigationFrame /* displayFrame */);
mNavigationBar.computeFrameAndUpdateSourceFrame(displayFrames);
sTmpRect.set(windowFrames.mFrame);
sTmpRect.intersect(displayFrames.mDisplayCutoutSafe);
contentFrame.set(sTmpRect);
if (DEBUG_LAYOUT) Slog.i(TAG, "mNavigationBar frame: " + navigationFrame);
return navBarPosition;
}
}
四、状态栏视图窗口区域坐标对象的构建
1、构建状态栏视图窗口区域坐标的layoutStatusBar方法r如下所示。
private void layoutStatusBar(DisplayFrames displayFrames, Rect contentFrame) {
// decide where the status bar goes ahead of time
if (mStatusBar == null) {
return;
}
// apply any status bar insets
getRotatedWindowBounds(displayFrames, mStatusBar, sTmpStatusFrame);
final WindowFrames windowFrames = mStatusBar.getLayoutingWindowFrames();
windowFrames.setFrames(sTmpStatusFrame /* parentFrame */,
sTmpStatusFrame /* displayFrame */);
// Let the status bar determine its size.
mStatusBar.computeFrameAndUpdateSourceFrame(displayFrames);
// For layout, the status bar is always at the top with our fixed height.
int statusBarBottom = displayFrames.mUnrestricted.top
+ mStatusBarHeightForRotation[displayFrames.mRotation];
if (displayFrames.mDisplayCutoutSafe.top > displayFrames.mUnrestricted.top) {
// Make sure that the zone we're avoiding for the cutout is at least as tall as the
// status bar; otherwise fullscreen apps will end up cutting halfway into the status
// bar.
displayFrames.mDisplayCutoutSafe.top = Math.max(displayFrames.mDisplayCutoutSafe.top,
statusBarBottom);
}
sTmpRect.set(windowFrames.mFrame);
sTmpRect.intersect(displayFrames.mDisplayCutoutSafe);
sTmpRect.top = windowFrames.mFrame.top; // Ignore top display cutout inset
sTmpRect.bottom = statusBarBottom; // Use collapsed status bar size
contentFrame.set(sTmpRect);
}