计算窗口位置大小和 relayout 流程
应用程序侧通过 performTraversal() 调用 mWindowSession.relayoutWindow() 请求 WMS 进行 relayout, 从而获得窗口大小和绘图表面。
函数调用链:
relayoutWindow()
WindowSurfacePlacer.performSurfacePlacement()
RootWindowContainer.applySurfaceChangesTransaction()
DisplayContent.applySurfaceChangesTransaction()
DisplayContent.performLayout()
WindowPolicy.layoutWindowLw()
WindowLayout.computeFrames()
过程中各个类的作用:
- WindowSurfacePlacer 类负责窗口布局循环次数控制;
- RootWindowContainer 类窗口层级遍历计算大小,及之后动画、壁纸变化、焦点变化等;
- DisplayContent 类负责窗口层级遍历;
- DisplayPolicy 类负责应用窗口策略;
- WindowLayout 类最终负责窗口的位置大小计算;
relayoutWinow
- 在 mWindowMap 中查找 WIndowState;
- 设置 WindowState 的布局参数、请求宽高、可见性、透明度等
- 设置所属 DisplayContent 和当前 WindowState 的 LayoutNeeded 为 true
- 创建 SurfaceControl
- 强制立即执行大遍历计算窗口大小
- 处理Surface变化导致的焦点、IME Target、壁纸变化
- 把计算后的 frame、inset 和 Surface 等返回给客户端
窗口条件计算大小的情况:请求的宽高变化,设置了 LAYOUT_CHANGED 或者 SYSTEM_UI_VISIBILITY_CHANGED
public int relayoutWindow(Session session, IWindow client, LayoutParams attrs,
int requestedWidth, int requestedHeight, int viewVisibility, int flags, int seq,
int lastSyncSeqId, ClientWindowFrames outFrames,
MergedConfiguration outMergedConfiguration, SurfaceControl outSurfaceControl,
InsetsState outInsetsState, InsetsSourceControl.Array outActiveControls,
Bundle outSyncIdBundle) {
if (outActiveControls != null) {
outActiveControls.set(null);
}
int result = 0;
boolean configChanged = false;
...
synchronized (mGlobalLock) {
// 查找 WindowState, mWindowMap 以 IWindow 为 key, WindowState 为 value
final WindowState win = windowForClientLocked(session, client, false);
// ...
final DisplayContent displayContent = win.getDisplayContent();
final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
WindowStateAnimator winAnimator = win.mWinAnimator;
// 如果请求宽高变化,则保存并设置 mLayoutNeeded 为 true (则遍历过程中会重新计算这个窗口的位置大小)
if (viewVisibility != View.GONE) {
win.setRequestedSize(requestedWidth, requestedHeight);
}
int attrChanges = 0;
int flagChanges = 0;
int privateFlagChanges = 0;
if (attrs != null) {
...
flagChanges = win.mAttrs.flags ^ attrs.flags;
privateFlagChanges = win.mAttrs.privateFlags ^ attrs.privateFlags;
attrChanges = win.mAttrs.copyFrom(attrs);
final boolean layoutChanged =
(attrChanges & WindowManager.LayoutParams.LAYOUT_CHANGED) != 0;
if (layoutChanged || (attrChanges
& WindowManager.LayoutParams.SYSTEM_UI_VISIBILITY_CHANGED) != 0) {
win.mLayoutNeeded = true;
}
if (layoutChanged && win.providesDisplayDecorInsets()) {
configChanged = displayPolicy.updateDecorInsetsInfo();
}
...
}
if ((attrChanges & WindowManager.LayoutParams.ALPHA_CHANGED) != 0) {
winAnimator.mAlpha = attrs.alpha;
}
win.setWindowScale(win.mRequestedWidth, win.mRequestedHeight);
// ...
final int oldVisibility = win.mViewVisibility;
// If the window is becoming visible, visibleOrAdding may change which may in turn
// change the IME target.
// 变为可见,可能导致 IME target 变化
final boolean becameVisible =
(oldVisibility == View.INVISIBLE || oldVisibility == View.GONE)
&& viewVisibility == View.VISIBLE;
boolean imMayMove = (flagChanges & (FLAG_ALT_FOCUSABLE_IM | FLAG_NOT_FOCUSABLE)) != 0
|| becameVisible;
boolean focusMayChange = win.mViewVisibility != viewVisibility
|| ((flagChanges & FLAG_NOT_FOCUSABLE) != 0)
|| (!win.mRelayoutCalled);
boolean wallpaperMayMove = win.mViewVisibility != viewVisibility
&& win.hasWallpaper();
wallpaperMayMove |= (flagChanges & FLAG_SHOW_WALLPAPER) != 0;
if ((flagChanges & FLAG_SECURE) != 0 && winAnimator.mSurfaceController != null) {
winAnimator.mSurfaceController.setSecure(win.isSecureLocked());
}
final boolean wasVisible = win.isVisible();
win.mRelayoutCalled = true;
win.mInRelayout = true;
win.setViewVisibility(viewVisibility);
win.setDisplayLayoutNeeded();
// ...
// 当前窗口可见并且Activity可见(如果appToken 是 ActivityRecord)
final boolean shouldRelayout = viewVisibility == View.VISIBLE &&
(win.mActivityRecord == null || win.mAttrs.type == TYPE_APPLICATION_STARTING
|| win.mActivityRecord.isClientVisible());
// 如果不可见,开启退出动画
if (!shouldRelayout && winAnimator.hasSurface() && !win.mAnimatingExit) {
result |= RELAYOUT_RES_SURFACE_CHANGED;
//...
tryStartExitingAnimation(win, winAnimator);
}
// 如果可见但是没有SurfaceControl, 则要在计算大小前创建 SurfaceControl
if (shouldRelayout && outSurfaceControl != null) {
try {
result = createSurfaceControl(outSurfaceControl, result, win, winAnimator);
} catch (Exception e) {
// ...
return 0;
}
}
// 强制立即执行大遍历计算窗口大小
mWindowPlacerLocked.performSurfacePlacement(true /* force */);
// 处理焦点、IME Target、壁纸变化
if (shouldRelayout) {
result = win.relayoutVisibleWindow(result);
if ((result & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
focusMayChange = true;
}
if (win.mAttrs.type == TYPE_INPUT_METHOD
&& displayContent.mInputMethodWindow == null) {
displayContent.setInputMethodWindowLocked(win);
imMayMove = true;
}
win.adjustStartingWindowFlags();
} else {
winAnimator.mEnterAnimationPending = false;
winAnimator.mEnteringAnimation = false;
if (outSurfaceControl != null) {
if (viewVisibility == View.VISIBLE && winAnimator.hasSurface()) {
// 把 Surface 传给客户端, 客户端可以进行绘制流程
winAnimator.mSurfaceController.getSurfaceControl(outSurfaceControl);
} else {
// 不可见则release surface
outSurfaceControl.release();
}
}
}
}
if (focusMayChange) {
if (updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /*updateInputWindows*/)) {
imMayMove = false;
}
}
boolean toBeDisplayed = (result & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0;
if (imMayMove) {
displayContent.computeImeTarget(true /* updateImeTarget */);
if (toBeDisplayed) {
displayContent.assignWindowLayers(false /* setLayoutNeeded */);
}
}
if (wallpaperMayMove) {
displayContent.pendingLayoutChanges |=
WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
}
configChanged |= displayContent.updateOrientation();
if (toBeDisplayed && win.mIsWallpaper) {
displayContent.mWallpaperController.updateWallpaperOffset(win, false /* sync */);
}
// 返回 frame 给客户端
if (outFrames != null && outMergedConfiguration != null) {
win.fillClientWindowFramesAndConfiguration(outFrames, outMergedConfiguration,
false /* useLatestConfig */, shouldRelayout);
win.onResizeHandled();
}
// 返回 outInsetsState 给客户端
if (outInsetsState != null) {
outInsetsState.set(win.getCompatInsetsState(), true /* copySources */);
}
...
}
return result;
}
WindowSurfacePlacer
控制连续遍历次数。检查所有屏幕的 mLayoutNeeded 属性是否为 true 来判断是否需要重新遍历计算位置大小。
大遍历从跟容器 RootWindowContainer 开始遍历所有屏幕。如果遍历一次后,还需要重新计算,则把 DisplayContent 的 mLayoutNeeded 属性设置为 true.
final void performSurfacePlacement(boolean force) {
// 延迟 layout
if (mDeferDepth > 0 && !force) {
mDeferredRequests++;
return;
}
// 最多循环执行6次
int loopCount = 6;
do {
// true 表示已请求遍历;
mTraversalScheduled = false;
performSurfacePlacementLoop();
mService.mAnimationHandler.removeCallbacks(mPerformSurfacePlacement);
loopCount--;
} while (mTraversalScheduled && loopCount > 0);
mService.mRoot.mWallpaperActionPending = false;
}
private void performSurfacePlacementLoop() {
// 等待完成配置变化
final DisplayContent defaultDisplay = mService.getDefaultDisplayContentLocked();
if (defaultDisplay.mWaitingForConfig) {
return;
}
// 还没有完成初始化
if (!mService.mDisplayReady) {
return;
}
mInLayout = true;
// 移除需要强制移除的窗口
if (!mService.mForceRemoves.isEmpty()) {
while (!mService.mForceRemoves.isEmpty()) {
final WindowState ws = mService.mForceRemoves.remove(0);
ws.removeImmediately();
}
Object tmp = new Object();
synchronized (tmp) {
tmp.wait(250);
}
}
try {
// 从根容器开始遍历,计算窗口位置大小
mService.mRoot.performSurfacePlacement();
mInLayout = false;
// 遍历一次完成后,如果需要重新 relayout 则把 DisplayContent 的 mLayoutNeeded 设为 true
// 检查所有屏幕,是否需要重新 relayout
if (mService.mRoot.isLayoutNeeded()) {
if (++mLayoutRepeatCount < 6) {
requestTraversal();
} else {
// 已经连续执行6次遍历
mLayoutRepeatCount = 0;
}
} else {
mLayoutRepeatCount = 0;
}
...
} catch (RuntimeException e) {
mInLayout = false;
}
}
RootWindowContainer
- 遍历窗口层级,计算窗口位置大小;
- 触发应用过渡动画
- 处理壁纸变化
- 处理焦点变化
- 销毁不可见窗口的Surface
- 开始动画
void performSurfacePlacementNoTrace() {
int i;
// 更新焦点
if (mWmService.mFocusMayChange) {
mWmService.mFocusMayChange = false;
mWmService.updateFocusedWindowLocked(
UPDATE_FOCUS_WILL_PLACE_SURFACES, false /*updateInputWindows*/);
}
...
mWmService.openSurfaceTransaction(); // 开始事务
// 对所有屏幕执行计算位置大小
applySurfaceChangesTransaction();
mWmService.closeSurfaceTransaction("performLayoutAndPlaceSurfaces"); // 关闭事务
...
// 触发应用过渡动画
checkAppTransitionReady(surfacePlacer);
// 延迟开始最近任务动画直到壁纸绘制
final RecentsAnimationController recentsAnimationController =
mWmService.getRecentsAnimationController();
if (recentsAnimationController != null) {
recentsAnimationController.checkAnimationReady(defaultDisplay.mWallpaperController);
}
mWmService.mAtmService.mBackNavigationController
.checkAnimationReady(defaultDisplay.mWallpaperController);
// 处理壁纸变化
for (int displayNdx = 0; displayNdx < mChildren.size(); ++displayNdx) {
final DisplayContent displayContent = mChildren.get(displayNdx);
if (displayContent.mWallpaperMayChange) {
displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
}
}
// 处理焦点变化
if (mWmService.mFocusMayChange) {
mWmService.mFocusMayChange = false;
mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES,
false /*updateInputWindows*/);
}
if (isLayoutNeeded()) {
defaultDisplay.pendingLayoutChanges |= FINISH_LAYOUT_REDO_LAYOUT;
}
// 如果位置大小变化,则通知客户端重新 layout
handleResizingWindows();
...
// 如果窗口不可见,则销毁其 Surface
i = mWmService.mDestroySurface.size();
if (i > 0) {
do {
i--;
WindowState win = mWmService.mDestroySurface.get(i);
win.mDestroying = false;
final DisplayContent displayContent = win.getDisplayContent();
if (displayContent.mInputMethodWindow == win) {
displayContent.setInputMethodWindowLocked(null);
}
if (displayContent.mWallpaperController.isWallpaperTarget(win)) {
displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
}
win.destroySurfaceUnchecked();
} while (i > 0);
mWmService.mDestroySurface.clear();
}
for (int displayNdx = 0; displayNdx < mChildren.size(); ++displayNdx) {
final DisplayContent displayContent = mChildren.get(displayNdx);
if (displayContent.pendingLayoutChanges != 0) {
displayContent.setLayoutNeeded();
}
}
...
// 开始动画
mWmService.scheduleAnimationLocked();
}
// 对所有屏幕执行 applySurfaceChangesTransaction()
private void applySurfaceChangesTransaction() {
...
final int count = mChildren.size();
for (int j = 0; j < count; ++j) {
final DisplayContent dc = mChildren.get(j);
dc.applySurfaceChangesTransaction();
}
...
}
DisplayContent
两次遍历窗口层级中的窗口,分别对非attached窗口、attached窗口进行计算大小;
计算大小的条件为:
- 不是不可见或将要不可见;
- 还没有进行计算过大小;
- mLayoutNeeded 为 false; flags = LAYOUT_CHANGED || SYSTEM_UI_VISIBILITY_CHANGED,
void applySurfaceChangesTransaction() {
final WindowSurfacePlacer surfacePlacer = mWmService.mWindowPlacerLocked;
...
// 壁纸可能需要移动
if ((pendingLayoutChanges & FINISH_LAYOUT_REDO_WALLPAPER) != 0) {
mWallpaperController.adjustWallpaperWindows();
}
// 配置变化,
if ((pendingLayoutChanges & FINISH_LAYOUT_REDO_CONFIG) != 0) {
if (updateOrientation()) {
setLayoutNeeded();
sendNewConfiguration();
}
}
// 布局状态变化
if ((pendingLayoutChanges & FINISH_LAYOUT_REDO_LAYOUT) != 0) {
setLayoutNeeded();
}
// 遍历执行 layout -> performLayoutNoTrace
performLayout(true /* initial */, false /* updateInputWindows */);
pendingLayoutChanges = 0;
// 应用策略
mDisplayPolicy.beginPostLayoutPolicyLw();
forAllWindows(mApplyPostLayoutPolicy, true /* traverseTopToBottom */);
mDisplayPolicy.finishPostLayoutPolicyLw();
// 处理 Surface 变化
forAllWindows(mApplySurfaceChangesTransaction, true /* traverseTopToBottom */);
...
}
private void performLayoutNoTrace(boolean initial, boolean updateInputWindows) {
if (!isLayoutNeeded()) { // mLayoutNeeded 表示需要重新布局,false 则跳过
return;
}
clearLayoutNeeded(); // mLayoutNeeded 置为 false
int seq = mLayoutSeq + 1;
if (seq < 0) seq = 0;
mLayoutSeq = seq;
mTmpInitial = initial;
// 遍历所有非 TYPE_APPLICATION_ATTACHED_DIALOG 类型窗口,进行 layout; true 表示自顶向下
forAllWindows(mPerformLayout, true /* traverseTopToBottom */);
// 遍历所有 TYPE_APPLICATION_ATTACHED_DIALOG 类型窗口,进行 layout;
forAllWindows(mPerformLayoutAttached, true /* traverseTopToBottom */);
...
}
// 函数式接口
private final Consumer<WindowState> mPerformLayout = w -> {
if (w.mLayoutAttached) { // TYPE_APPLICATION_ATTACHED_DIALOG 类型
return;
}
// 现在不可见或者将要变为不可见,如 mAnimatingExit, mDestroying 等,则跳过
final boolean gone = w.isGoneForLayout();
// 计算大小的条件:
// 1. 不是不可见或将要不可见;
// 2. 还没有进行计算过大小
// 3. mLayoutNeeded 为 false; flags = LAYOUT_CHANGED || SYSTEM_UI_VISIBILITY_CHANGED,
if (!gone || !w.mHaveFrame || w.mLayoutNeeded) {
...
w.mSurfacePlacementNeeded = true;
w.mLayoutNeeded = false;
final boolean firstLayout = !w.isLaidOut();
// 计算大小,mDisplayFrames 为屏幕大小
getDisplayPolicy().layoutWindowLw(w, null, mDisplayFrames);
w.mLayoutSeq = mLayoutSeq;
// 第一次 layout, 用当前的 frames 初始化 lastFrames
if (firstLayout) {
if (!w.getFrame().isEmpty()) {
w.updateLastFrames();
}
w.onResizeHandled();
}
}
};
DisplayPolicy
- 获取DisplayFrames,考虑屏幕转向;
- 计算窗口大小;
- 把计算后的位置大小赋值给窗口。
// attached, 如果 WindowState 是子窗口 TYPE_APPLICATION_ATTACHED_DIALOG,attached 是其附着的父窗口
public void layoutWindowLw(WindowState win, WindowState attached, DisplayFrames displayFrames) {
if (win.skipLayout()) { // 如果 token 是 ActivityRecord 而且正在进入画中画模式,则跳过
return;
}
// 如果 token 正在进行无缝旋转,则采用旋转的 displayFrames,否则采用传进来的 displayFrames
displayFrames = win.getDisplayFrames(displayFrames);
// 获取当前屏幕转向的布局参数
final WindowManager.LayoutParams attrs = win.mAttrs.forRotation(displayFrames.mRotation);
// 设置 attachedFrame
sTmpClientFrames.attachedFrame = attached != null ? attached.getFrame() : null;
// 如果 window 的 frame 与当前转向的布局参数不同,则请求大小设置为 UNSPECIFIED_LENGTH
final boolean trustedSize = attrs == win.mAttrs;
final int requestedWidth = trustedSize ? win.mRequestedWidth : UNSPECIFIED_LENGTH;
final int requestedHeight = trustedSize ? win.mRequestedHeight : UNSPECIFIED_LENGTH;
// 计算窗口大小
mWindowLayout.computeFrames(attrs, win.getInsetsState(), displayFrames.mDisplayCutoutSafe,
win.getBounds(), win.getWindowingMode(), requestedWidth, requestedHeight,
win.getRequestedVisibleTypes(), win.mGlobalScale, sTmpClientFrames);
// 设置窗口的位置大小
win.setFrames(sTmpClientFrames, win.mRequestedWidth, win.mRequestedHeight);
}
WindowLayout
- 根据 insets 调整大小位置;
- 设置 MATCH_PARENT 时的大小 outParentFrame,非子窗口则是屏幕大小,是子窗口则考虑父窗口的约束
- 切边模式的调整
- 根据缩放比例对宽高、大小进行调整
- 多窗口模式下的约束
- 根据 gravity 进行调整;
public void computeFrames(WindowManager.LayoutParams attrs, InsetsState state,
Rect displayCutoutSafe, Rect windowBounds, @WindowingMode int windowingMode,
int requestedWidth, int requestedHeight, @InsetsType int requestedVisibleTypes,
float compatScale, ClientWindowFrames frames) {
final int type = attrs.type;
final int fl = attrs.flags;
final int pfl = attrs.privateFlags;
// 不考虑父窗口的约束
final boolean layoutInScreen = (fl & FLAG_LAYOUT_IN_SCREEN) == FLAG_LAYOUT_IN_SCREEN;
final Rect attachedWindowFrame = frames.attachedFrame; // 附着的父窗口的大小
final Rect outDisplayFrame = frames.displayFrame; // 显示大小,减去 insets
final Rect outParentFrame = frames.parentFrame; // MATCH_PARENT 时的大小
final Rect outFrame = frames.frame; // 实际大小
// insets 对边界的约束
final Insets insets = state.calculateInsets(windowBounds, attrs.getFitInsetsTypes(),
attrs.isFitInsetsIgnoringVisibility());
final @WindowInsets.Side.InsetsSide int sides = attrs.getFitInsetsSides();
final int left = (sides & WindowInsets.Side.LEFT) != 0 ? insets.left : 0;
final int top = (sides & WindowInsets.Side.TOP) != 0 ? insets.top : 0;
final int right = (sides & WindowInsets.Side.RIGHT) != 0 ? insets.right : 0;
final int bottom = (sides & WindowInsets.Side.BOTTOM) != 0 ? insets.bottom : 0;
outDisplayFrame.set(windowBounds.left + left, windowBounds.top + top,
windowBounds.right - right, windowBounds.bottom - bottom);
// 设置 MATCH_PARENT 时的大小 outParentFrame,非子窗口则是屏幕大小,是子窗口则考虑父窗口的约束
if (attachedWindowFrame == null) { // 不是 子窗口 TYPE_APPLICATION_ATTACHED_DIALOG
outParentFrame.set(outDisplayFrame);
...
} else { // 如果不考虑父窗口的约束,则采用上面计算的,否则采用父窗口的
outParentFrame.set(!layoutInScreen ? attachedWindowFrame : outDisplayFrame);
}
// 切边模式的约束
final int cutoutMode = attrs.layoutInDisplayCutoutMode;
final DisplayCutout cutout = state.getDisplayCutout();
final Rect displayCutoutSafeExceptMaybeBars = mTempDisplayCutoutSafeExceptMaybeBarsRect;
displayCutoutSafeExceptMaybeBars.set(displayCutoutSafe);
frames.isParentFrameClippedByDisplayCutout = false;
if (cutoutMode != LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS && !cutout.isEmpty()) {
...
}
// 窗口可以扩展屏幕外
final boolean noLimits = (attrs.flags & FLAG_LAYOUT_NO_LIMITS) != 0;
// 多窗口模式
final boolean inMultiWindowMode = WindowConfiguration.inMultiWindowMode(windowingMode);
// TYPE_SYSTEM_ERROR is above the NavigationBar so it can't be allowed to extend over it.
// Also, we don't allow windows in multi-window mode to extend out of the screen.
// noLimits 并且不是 TYPE_SYSTEM_ERROR 类型的窗口、非多窗口模式,窗口可以扩展到屏幕的边界
if (noLimits && type != TYPE_SYSTEM_ERROR && !inMultiWindowMode) {
outDisplayFrame.left = MIN_X;
outDisplayFrame.top = MIN_Y;
outDisplayFrame.right = MAX_X;
outDisplayFrame.bottom = MAX_Y;
}
final boolean hasCompatScale = compatScale != 1f;
final int pw = outParentFrame.width();
final int ph = outParentFrame.height();
final boolean extendedByCutout =
(attrs.privateFlags & PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT) != 0;
// 请求的宽高、最终的位置坐标和宽高
int rw = requestedWidth;
int rh = requestedHeight;
float x, y;
int w, h;
// 如果窗口内部还没有测量,则请求宽高是 UNSPECIFIED_LENGTH;
// 采用布局参数中的宽高或MATCH_PARENT的宽高
if (rw == UNSPECIFIED_LENGTH || extendedByCutout) {
rw = attrs.width >= 0 ? attrs.width : pw;
}
if (rh == UNSPECIFIED_LENGTH || extendedByCutout) {
rh = attrs.height >= 0 ? attrs.height : ph;
}
// 使用 WindowState 中的缩放比例对宽高进行修正
if ((attrs.flags & FLAG_SCALED) != 0) {
if (attrs.width < 0) {
w = pw;
} else if (hasCompatScale) {
w = (int) (attrs.width * compatScale + .5f);
} else {
w = attrs.width;
}
if (attrs.height < 0) {
h = ph;
} else if (hasCompatScale) {
h = (int) (attrs.height * compatScale + .5f);
} else {
h = attrs.height;
}
} else {
if (attrs.width == MATCH_PARENT) {
w = pw;
} else if (hasCompatScale) {
w = (int) (rw * compatScale + .5f);
} else {
w = rw;
}
if (attrs.height == MATCH_PARENT) {
h = ph;
} else if (hasCompatScale) {
h = (int) (rh * compatScale + .5f);
} else {
h = rh;
}
}
// 使用 WindowState 中的缩放比例对坐标进行修正
if (hasCompatScale) {
x = attrs.x * compatScale;
y = attrs.y * compatScale;
} else {
x = attrs.x;
y = attrs.y;
}
// 如果是在多窗口模式下,确保不会超过父窗口的大小
if (inMultiWindowMode
&& (attrs.privateFlags & PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME) == 0) {
// Make sure window fits in parent frame since it is in a non-fullscreen task as
// required by {@link Gravity#apply} call.
w = Math.min(w, pw);
h = Math.min(h, ph);
}
// 非多窗口模式或者 应用窗口且设置了FLAG_LAYOUT_NO_LIMITS,需要适应显示
final boolean fitToDisplay = !inMultiWindowMode
|| ((attrs.type != TYPE_BASE_APPLICATION) && !noLimits);
// 根据 gravity 调整位置
Gravity.apply(attrs.gravity, w, h, outParentFrame,
(int) (x + attrs.horizontalMargin * pw),
(int) (y + attrs.verticalMargin * ph), outFrame);
// 如果需要适应显示,确保 outFrame 在 outDisplayFrame 内
if (fitToDisplay) {
Gravity.applyDisplay(attrs.gravity, outDisplayFrame, outFrame);
}
...
}