Android 窗口的几个区域的介绍:
1.overscanScreen区域,这个区域包括屏幕的overscan区域,相当于整个屏幕
2.RestrictedOverScanScreen区域,包括overscan区,不包含导航栏、因此这个区域上面到屏幕的顶部,下面就到导航栏的顶部。
3.RestrictedScreen区域,这个区域不包含overscan区域不包含导航栏
4.UnRestrictedScreen区域,不包含屏幕的overscan区域、包含状态栏和导航栏
5.stableFullScreen区域,包含状态栏、输入法、不包含导航栏
6.Decor区域,不包含状态栏、不包含导航栏、包含输入法区域7.Curren区域、不包含状态栏、不包含导航栏、不包含输入法区域
8.stable区域,不包含状态栏、不包含导航栏,与stableFullScreen的区别是后者不考虑状态栏
Android窗口大小的计算主要流程如下:
Session.relayout()--> WindowManagerService.relayoutWindow()--> WindowManagerService.performLayoutAndPlaceSurfacesLocked()--> WindowManagerService.performLayoutAndPlaceSurfacesLockedLoop()--> WindowManagerService.performLayoutAndPlaceSurfacesLockedInner()-->
WindowManagerService.assignLayersLocked()--> WindowManagerService.performLayoutAndPlaceSurfacesLockedInner()--> PhoneWindow.beginLayoutLw()--> PhoneWindow.layoutWindowLw()-->PhoneWindow.finishLayoutLw()
其中assignLayersLocked主要是计算窗口的层级,beginLayoutLw(),layoutWindowLw(),finishLayoutLw()主要是计算窗口的大小
1.beginLayoutLw()函数分析
/** {@inheritDoc} */
@Override
public void beginLayoutLw(boolean isDefaultDisplay, int displayWidth, int displayHeight,
int displayRotation) {
mDisplayRotation = displayRotation;
final int overscanLeft, overscanTop, overscanRight, overscanBottom;
//下面这段代码主要是依据旋转的角度初始化overscan边衬区域
if (isDefaultDisplay) {
switch (displayRotation) {
case Surface.ROTATION_90:
overscanLeft = mOverscanTop;
overscanTop = mOverscanRight;
overscanRight = mOverscanBottom;
overscanBottom = mOverscanLeft;
break;
case Surface.ROTATION_180:
overscanLeft = mOverscanRight;
overscanTop = mOverscanBottom;
overscanRight = mOverscanLeft;
overscanBottom = mOverscanTop;
break;
case Surface.ROTATION_270:
overscanLeft = mOverscanBottom;
overscanTop = mOverscanLeft;
overscanRight = mOverscanTop;
overscanBottom = mOverscanRight;
break;
default:
overscanLeft = mOverscanLeft;
overscanTop = mOverscanTop;
overscanRight = mOverscanRight;
overscanBottom = mOverscanBottom;
break;
}
} else {
overscanLeft = 0;
overscanTop = 0;
overscanRight = 0;
overscanBottom = 0;
}
//初始化OverscanScreen,System,UnrestrictedScreen,RestrictedScreen,Dock,Content,VoiceContent,Stable,StableFullscreen
//这些区域,目前这些区域主要是判断是否要剔除过扫描边衬(先不考虑状态栏和导航栏)
mOverscanScreenLeft = mRestrictedOverscanScreenLeft = 0;
mOverscanScreenTop = mRestrictedOverscanScreenTop = 0;
mOverscanScreenWidth = mRestrictedOverscanScreenWidth = displayWidth;
mOverscanScreenHeight = mRestrictedOverscanScreenHeight = displayHeight;
mSystemLeft = 0;
mSystemTop = 0;
mSystemRight = displayWidth;
mSystemBottom = displayHeight;
mUnrestrictedScreenLeft = overscanLeft;
mUnrestrictedScreenTop = overscanTop;
mUnrestrictedScreenWidth = displayWidth - overscanLeft - overscanRight;
mUnrestrictedScreenHeight = displayHeight - overscanTop - overscanBottom;
mRestrictedScreenLeft = mUnrestrictedScreenLeft;
mRestrictedScreenTop = mUnrestrictedScreenTop;
mRestrictedScreenWidth = mSystemGestures.screenWidth = mUnrestrictedScreenWidth;
mRestrictedScreenHeight = mSystemGestures.screenHeight = mUnrestrictedScreenHeight;
mDockLeft = mContentLeft = mVoiceContentLeft = mStableLeft = mStableFullscreenLeft
= mCurLeft = mUnrestrictedScreenLeft;
mDockTop = mContentTop = mVoiceContentTop = mStableTop = mStableFullscreenTop
= mCurTop = mUnrestrictedScreenTop;
mDockRight = mContentRight = mVoiceContentRight = mStableRight = mStableFullscreenRight
= mCurRight = displayWidth - overscanRight;
mDockBottom = mContentBottom = mVoiceContentBottom = mStableBottom = mStableFullscreenBottom
= mCurBottom = displayHeight - overscanBottom;
mDockLayer = 0x10000000;
mStatusBarLayer = -1;
// start with the current dock rect, which will be (0,0,displayWidth,displayHeight)
//用当前dock区域去初始化mTmpParentFrame等6个临时区域,这几个区域是在后面layoutWindowLw是要用到的,这里是为了通过全局变量实现函数参数传递
final Rect pf = mTmpParentFrame;
final Rect df = mTmpDisplayFrame;
final Rect of = mTmpOverscanFrame;
final Rect vf = mTmpVisibleFrame;
final Rect dcf = mTmpDecorFrame;
final Rect osf = mTmpOutsetFrame;
pf.left = df.left = of.left = vf.left = mDockLeft;
pf.top = df.top = of.top = vf.top = mDockTop;
pf.right = df.right = of.right = vf.right = mDockRight;
pf.bottom = df.bottom = of.bottom = vf.bottom = mDockBottom;
dcf.setEmpty(); // Decor frame N/A for system bars.
//一般都是默认的,所以会进入
if (isDefaultDisplay) {
// For purposes of putting out fake window up to steal focus, we will
// drive nav being hidden only by whether it is requested.
final int sysui = mLastSystemUiFlags;
boolean navVisible = (sysui & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0;
boolean navTranslucent = (sysui
& (View.NAVIGATION_BAR_TRANSLUCENT | View.SYSTEM_UI_TRANSPARENT)) != 0;
boolean immersive = (sysui & View.SYSTEM_UI_FLAG_IMMERSIVE) != 0;
boolean immersiveSticky = (sysui & View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) != 0;
boolean navAllowedHidden = immersive || immersiveSticky;
navTranslucent &= !immersiveSticky; // transient trumps translucent
boolean isKeyguardShowing = isStatusBarKeyguard() && !mHideLockScreen;
if (!isKeyguardShowing) {
navTranslucent &= areTranslucentBarsAllowed();
}
// When the navigation bar isn't visible, we put up a fake
// input window to catch all touch events. This way we can
// detect when the user presses anywhere to bring back the nav
// bar and ensure the application doesn't see the event.
//只有在导航栏不可见并且不是沉浸式模式时需要额外的InptConsumer
if (navVisible || navAllowedHidden) {
if (mInputConsumer != null) {
mInputConsumer.dismiss();
mInputConsumer = null;
}
} else if (mInputConsumer == null) {
mInputConsumer = mWindowManagerFuncs.addInputConsumer(mHandler.getLooper(),
mHideNavInputEventReceiverFactory);
}
// For purposes of positioning and showing the nav bar, if we have
// decided that it can't be hidden (because of the screen aspect ratio),
// then take that into account.
navVisible |= !canHideNavigationBar();
boolean updateSysUiVisibility = false;
//如果导航栏是存在的那现在需要调整因为导航栏区域给各个区域大小的影响了
//Dock,RestrictedScreen,RestrictedOverscan,System等区域都可能会被影响
if (mNavigationBar != null) {
boolean transientNavBarShowing = mNavigationBarController.isTransientShowing();
// 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.
mNavigationBarOnBottom = (!mNavigationBarCanMove || displayWidth < displayHeight);
//导航栏在底部的时候
if (mNavigationBarOnBottom) {
// It's a system nav bar or a portrait screen; nav bar goes on bottom.
int top = displayHeight - overscanBottom
- mNavigationBarHeightForRotation[displayRotation];
mTmpNavigationFrame.set(0, top, displayWidth, displayHeight - overscanBottom);
mStableBottom = mStableFullscreenBottom = mTmpNavigationFrame.top;
//临时显示不影响区域计算
if (transientNavBarShowing) {
mNavigationBarController.setBarShowingLw(true);
} else if (navVisible) {
mNavigationBarController.setBarShowingLw(true);
mDockBottom = mTmpNavigationFrame.top;
mRestrictedScreenHeight = mDockBottom - mRestrictedScreenTop;
mRestrictedOverscanScreenHeight = mDockBottom - mRestrictedOverscanScreenTop;
} else {
// We currently want to hide the navigation UI.
mNavigationBarController.setBarShowingLw(false);
}
if (navVisible && !navTranslucent && !navAllowedHidden
&& !mNavigationBar.isAnimatingLw()
&& !mNavigationBarController.wasRecentlyTranslucent()) {
// If the opaque nav bar is currently requested to be visible,
// and not in the process of animating on or off, then
// we can tell the app that it is covered by it.
mSystemBottom = mTmpNavigationFrame.top;
}
} else { //导航栏在右边的情况
// Landscape screen; nav bar goes to the right.
int left = displayWidth - overscanRight
- mNavigationBarWidthForRotation[displayRotation];
mTmpNavigationFrame.set(left, 0, displayWidth - overscanRight, displayHeight);
mStableRight = mStableFullscreenRight = mTmpNavigationFrame.left;
if (transientNavBarShowing) {
mNavigationBarController.setBarShowingLw(true);
} else if (navVisible) {
mNavigationBarController.setBarShowingLw(true);
mDockRight = mTmpNavigationFrame.left;
mRestrictedScreenWidth = mDockRight - mRestrictedScreenLeft;
mRestrictedOverscanScreenWidth = mDockRight - mRestrictedOverscanScreenLeft;
} else {
// We currently want to hide the navigation UI.
mNavigationBarController.setBarShowingLw(false);
}
if (navVisible && !navTranslucent && !navAllowedHidden
&& !mNavigationBar.isAnimatingLw()
&& !mNavigationBarController.wasRecentlyTranslucent()) {
// If the nav bar is currently requested to be visible,
// and not in the process of animating on or off, then
// we can tell the app that it is covered by it.
mSystemRight = mTmpNavigationFrame.left;
}
}
// Make sure the content and current rectangles are updated to
// account for the restrictions from the navigation bar.
//计算有导航栏的时候,Content,VoiceContent,Cur区域的大小
mContentTop = mVoiceContentTop = mCurTop = mDockTop;
mContentBottom = mVoiceContentBottom = mCurBottom = mDockBottom;
mContentLeft = mVoiceContentLeft = mCurLeft = mDockLeft;
mContentRight = mVoiceContentRight = mCurRight = mDockRight;
mStatusBarLayer = mNavigationBar.getSurfaceLayer();
// And compute the final frame.
//计算导航栏的大小
//计算任何窗口的大小都会调用computeFrameLw()这个函数,传入的参数不是真正的大小,而且允许的最大的尺寸
mNavigationBar.computeFrameLw(mTmpNavigationFrame, mTmpNavigationFrame,
mTmpNavigationFrame, mTmpNavigationFrame, mTmpNavigationFrame, dcf,
mTmpNavigationFrame, mTmpNavigationFrame);
if (DEBUG_LAYOUT) Slog.i(TAG, "mNavigationBar frame: " + mTmpNavigationFrame);
if (mNavigationBarController.checkHiddenLw()) {
updateSysUiVisibility = true;
}
}
if (DEBUG_LAYOUT) Slog.i(TAG, String.format("mDock rect: (%d,%d - %d,%d)",
mDockLeft, mDockTop, mDockRight, mDockBottom));
// decide where the status bar goes ahead of time
//在有状态栏的情况下,要依据状态栏的大小调整各个区域
if (mStatusBar != null) {
// apply any navigation bar insets
//给pf,df,of等赋值来计算状态栏的大小
//就目前状态栏的大小最大值应该是去除过扫描区域及导航栏
pf.left = df.left = of.left = mUnrestrictedScreenLeft;
pf.top = df.top = of.top = mUnrestrictedScreenTop;
pf.right = df.right = of.right = mUnrestrictedScreenWidth + mUnrestrictedScreenLeft;
pf.bottom = df.bottom = of.bottom = mUnrestrictedScreenHeight
+ mUnrestrictedScreenTop;
vf.left = mStableLeft;
vf.top = mStableTop;
vf.right = mStableRight;
vf.bottom = mStableBottom;
mStatusBarLayer = mStatusBar.getSurfaceLayer();
// Let the status bar determine its size.
//计算状态栏的大小
mStatusBar.computeFrameLw(pf /* parentFrame */, df /* displayFrame */,
vf /* overlayFrame */, vf /* contentFrame */, vf /* visibleFrame */,
dcf /* decorFrame */, vf /* stableFrame */, vf /* outsetFrame */);
// For layout, the status bar is always at the top with our fixed height.
mStableTop = mUnrestrictedScreenTop + mStatusBarHeight;
boolean statusBarTransient = (sysui & View.STATUS_BAR_TRANSIENT) != 0;
boolean statusBarTranslucent = (sysui
& (View.STATUS_BAR_TRANSLUCENT | View.SYSTEM_UI_TRANSPARENT)) != 0;
if (!isKeyguardShowing) {
statusBarTranslucent &= areTranslucentBarsAllowed();
}
// If the status bar is hidden, we don't want to cause
// windows behind it to scroll.
//状态栏是可见的并且不是临时的,考虑状态栏大小后调整Content,VoiceContent,Cur,Dock等区域
if (mStatusBar.isVisibleLw() && !statusBarTransient) {
// Status bar may go away, so the screen area it occupies
// is available to apps but just covering them when the
// status bar is visible.
mDockTop = mUnrestrictedScreenTop + mStatusBarHeight;
mContentTop = mVoiceContentTop = mCurTop = mDockTop;
mContentBottom = mVoiceContentBottom = mCurBottom = mDockBottom;
mContentLeft = mVoiceContentLeft = mCurLeft = mDockLeft;
mContentRight = mVoiceContentRight = mCurRight = mDockRight;
if (DEBUG_LAYOUT) Slog.v(TAG, "Status bar: " +
String.format(
"dock=[%d,%d][%d,%d] content=[%d,%d][%d,%d] cur=[%d,%d][%d,%d]",
mDockLeft, mDockTop, mDockRight, mDockBottom,
mContentLeft, mContentTop, mContentRight, mContentBottom,
mCurLeft, mCurTop, mCurRight, mCurBottom));
}
if (mStatusBar.isVisibleLw() && !mStatusBar.isAnimatingLw()
&& !statusBarTransient && !statusBarTranslucent
&& !mStatusBarController.wasRecentlyTranslucent()) {
// If the opaque status bar is currently requested to be visible,
// and not in the process of animating on or off, then
// we can tell the app that it is covered by it.
mSystemTop = mUnrestrictedScreenTop + mStatusBarHeight;
}
if (mStatusBarController.checkHiddenLw()) {
updateSysUiVisibility = true;
}
}
if (updateSysUiVisibility) {
updateSystemUiVisibilityLw();
}
}
}
2. layoutWindowLw()分析
/** {@inheritDoc} */
@Override
public void layoutWindowLw(WindowState win, WindowState attached) {
// We've already done the navigation bar and status bar. If the status bar can receive
// input, we need to layout it again to accomodate for the IME window.
if ((win == mStatusBar && !canReceiveInput(win)) || win == mNavigationBar) {
return;
}
final WindowManager.LayoutParams attrs = win.getAttrs();
final boolean isDefaultDisplay = win.isDefaultDisplay();
final boolean needsToOffsetInputMethodTarget = isDefaultDisplay &&
(win == mLastInputMethodTargetWindow && mLastInputMethodWindow != null);
if (needsToOffsetInputMethodTarget) {
if (DEBUG_LAYOUT) Slog.i(TAG, "Offset ime target window by the last ime window state");
offsetInputMethodWindowLw(mLastInputMethodWindow);
}
//获取win中与布局相关的属性
final int fl = PolicyControl.getWindowFlags(win, attrs);
final int sim = attrs.softInputMode;
final int sysUiFl = PolicyControl.getSystemUiVisibility(win, null);
//各个边衬变量拿过来,这些变量其实是不考虑状态栏但考虑过了导航栏的影响,我的理解是因为导航栏是通常不能被覆盖的而状态栏可有可无
//还有一个非常重要的:这些各个区域一直在维护的都是最大允许值而不是最终值
final Rect pf = mTmpParentFrame;
final Rect df = mTmpDisplayFrame;
final Rect of = mTmpOverscanFrame;
final Rect cf = mTmpContentFrame;
final Rect vf = mTmpVisibleFrame;
final Rect dcf = mTmpDecorFrame;
final Rect sf = mTmpStableFrame;
Rect osf = null;
dcf.setEmpty();
final boolean hasNavBar = (isDefaultDisplay && mHasNavigationBar
&& mNavigationBar != null && mNavigationBar.isVisibleLw());
final int adjust = sim & SOFT_INPUT_MASK_ADJUST;
if (isDefaultDisplay) {
sf.set(mStableLeft, mStableTop, mStableRight, mStableBottom);
} else {
sf.set(mOverscanLeft, mOverscanTop, mOverscanRight, mOverscanBottom);
}
//一般都是默认,所以不会进
if (!isDefaultDisplay) {
//有父窗口,不能超过父窗口
if (attached != null) {
// If this window is attached to another, our display
// frame is the same as the one we are attached to.
setAttachedWindowFrames(win, fl, adjust, attached, true, pf, df, of, cf, vf);
} else {
// Give the window full screen.
pf.left = df.left = of.left = cf.left = mOverscanScreenLeft;
pf.top = df.top = of.top = cf.top = mOverscanScreenTop;
pf.right = df.right = of.right = cf.right
= mOverscanScreenLeft + mOverscanScreenWidth;
pf.bottom = df.bottom = of.bottom = cf.bottom
= mOverscanScreenTop + mOverscanScreenHeight;
}
} else if (attrs.type == TYPE_INPUT_METHOD) {//输入法的时候
pf.left = df.left = of.left = cf.left = vf.left = mDockLeft;
pf.top = df.top = of.top = cf.top = vf.top = mDockTop;
pf.right = df.right = of.right = cf.right = vf.right = mDockRight;
// IM dock windows layout below the nav bar...
pf.bottom = df.bottom = of.bottom = mUnrestrictedScreenTop + mUnrestrictedScreenHeight;
// ...with content insets above the nav bar
//在导航栏上面
cf.bottom = vf.bottom = mStableBottom;
// IM dock windows always go to the bottom of the screen.
attrs.gravity = Gravity.BOTTOM;
mDockLayer = win.getSurfaceLayer();
} else if (attrs.type == TYPE_VOICE_INTERACTION) {
pf.left = df.left = of.left = mUnrestrictedScreenLeft;
pf.top = df.top = of.top = mUnrestrictedScreenTop;
pf.right = df.right = of.right = mUnrestrictedScreenLeft + mUnrestrictedScreenWidth;
pf.bottom = df.bottom = of.bottom = mUnrestrictedScreenTop + mUnrestrictedScreenHeight;
cf.bottom = vf.bottom = mStableBottom;
// Note: In Phone landscape mode, the button bar should also be excluded.
cf.right = vf.right = mStableRight;
cf.left = vf.left = mStableLeft;
cf.top = vf.top = mStableTop;
} else if (win == mStatusBar) {
pf.left = df.left = of.left = mUnrestrictedScreenLeft;
pf.top = df.top = of.top = mUnrestrictedScreenTop;
pf.right = df.right = of.right = mUnrestrictedScreenWidth + mUnrestrictedScreenLeft;
pf.bottom = df.bottom = of.bottom = mUnrestrictedScreenHeight + mUnrestrictedScreenTop;
cf.left = vf.left = mStableLeft;
cf.top = vf.top = mStableTop;
cf.right = vf.right = mStableRight;
vf.bottom = mStableBottom;
cf.bottom = mContentBottom;
} else {
// Default policy decor for the default display
dcf.left = mSystemLeft;
dcf.top = mSystemTop;
dcf.right = mSystemRight;
dcf.bottom = mSystemBottom;
final boolean inheritTranslucentDecor = (attrs.privateFlags
& WindowManager.LayoutParams.PRIVATE_FLAG_INHERIT_TRANSLUCENT_DECOR) != 0;
final boolean isAppWindow =
attrs.type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW &&
attrs.type <= WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
final boolean topAtRest =
win == mTopFullscreenOpaqueWindowState && !win.isAnimatingLw();
if (isAppWindow && !inheritTranslucentDecor && !topAtRest) {
//在如下标志位成立时dcf区域在状态栏的下面
if ((sysUiFl & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0
&& (fl & WindowManager.LayoutParams.FLAG_FULLSCREEN) == 0
&& (fl & WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS) == 0
&& (fl & WindowManager.LayoutParams.
FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0) {
// Ensure policy decor includes status bar
dcf.top = mStableTop;
}
//在如下标志位成立时dcf区域在导航栏的上面或左边
if ((fl & WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION) == 0
&& (sysUiFl & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0
&& (fl & WindowManager.LayoutParams.
FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0) {
// Ensure policy decor includes navigation bar
dcf.bottom = mStableBottom;
dcf.right = mStableRight;
}
}
if ((fl & (FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR))
== (FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR)) {
if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle()
+ "): IN_SCREEN, INSET_DECOR");
// This is the case for a normal activity window: we want it
// to cover all of the screen space, and it can take care of
// moving its contents to account for screen decorations that
// intrude into that space.
if (attached != null) {
// If this window is attached to another, our display
// frame is the same as the one we are attached to.
setAttachedWindowFrames(win, fl, adjust, attached, true, pf, df, of, cf, vf);
} else {
//下面两个模式不用考虑状态栏,但是需要考虑导航栏
if (attrs.type == TYPE_STATUS_BAR_PANEL
|| attrs.type == TYPE_STATUS_BAR_SUB_PANEL) {
// Status bar panels are the only windows who can go on top of
// the status bar. They are protected by the STATUS_BAR_SERVICE
// permission, so they have the same privileges as the status
// bar itself.
//
// However, they should still dodge the navigation bar if it exists.
pf.left = df.left = of.left = hasNavBar
? mDockLeft : mUnrestrictedScreenLeft;
pf.top = df.top = of.top = mUnrestrictedScreenTop;
pf.right = df.right = of.right = hasNavBar
? mRestrictedScreenLeft+mRestrictedScreenWidth
: mUnrestrictedScreenLeft + mUnrestrictedScreenWidth;
pf.bottom = df.bottom = of.bottom = hasNavBar
? mRestrictedScreenTop+mRestrictedScreenHeight
: mUnrestrictedScreenTop + mUnrestrictedScreenHeight;
if (DEBUG_LAYOUT) Slog.v(TAG, String.format(
"Laying out status bar window: (%d,%d - %d,%d)",
pf.left, pf.top, pf.right, pf.bottom));
} else if ((fl & FLAG_LAYOUT_IN_OVERSCAN) != 0
&& attrs.type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW
&& attrs.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {//直接设置为overscan区域
// Asking to layout into the overscan region, so give it that pure
// unrestricted area.
pf.left = df.left = of.left = mOverscanScreenLeft;
pf.top = df.top = of.top = mOverscanScreenTop;
pf.right = df.right = of.right = mOverscanScreenLeft + mOverscanScreenWidth;
pf.bottom = df.bottom = of.bottom = mOverscanScreenTop
+ mOverscanScreenHeight;
} else if (canHideNavigationBar()
&& (sysUiFl & View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0
&& attrs.type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW
&& attrs.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {//隐藏导航栏的情况
// Asking for layout as if the nav bar is hidden, lets the
// application extend into the unrestricted overscan screen area. We
// only do this for application windows to ensure no window that
// can be above the nav bar can do this.
pf.left = df.left = mOverscanScreenLeft;
pf.top = df.top = mOverscanScreenTop;
pf.right = df.right = mOverscanScreenLeft + mOverscanScreenWidth;
pf.bottom = df.bottom = mOverscanScreenTop + mOverscanScreenHeight;
// We need to tell the app about where the frame inside the overscan
// is, so it can inset its content by that amount -- it didn't ask
// to actually extend itself into the overscan region.
of.left = mUnrestrictedScreenLeft;
of.top = mUnrestrictedScreenTop;
of.right = mUnrestrictedScreenLeft + mUnrestrictedScreenWidth;
of.bottom = mUnrestrictedScreenTop + mUnrestrictedScreenHeight;
} else {
pf.left = df.left = mRestrictedOverscanScreenLeft;
pf.top = df.top = mRestrictedOverscanScreenTop;
pf.right = df.right = mRestrictedOverscanScreenLeft
+ mRestrictedOverscanScreenWidth;
pf.bottom = df.bottom = mRestrictedOverscanScreenTop
+ mRestrictedOverscanScreenHeight;
// We need to tell the app about where the frame inside the overscan
// is, so it can inset its content by that amount -- it didn't ask
// to actually extend itself into the overscan region.
of.left = mUnrestrictedScreenLeft;
of.top = mUnrestrictedScreenTop;
of.right = mUnrestrictedScreenLeft + mUnrestrictedScreenWidth;
of.bottom = mUnrestrictedScreenTop + mUnrestrictedScreenHeight;
}
//前面主要调整pf,df,of.下面是设置cf,vf
if ((fl & FLAG_FULLSCREEN) == 0) {
if (win.isVoiceInteraction()) {
cf.left = mVoiceContentLeft;
cf.top = mVoiceContentTop;
cf.right = mVoiceContentRight;
cf.bottom = mVoiceContentBottom;
} else {
if (adjust != SOFT_INPUT_ADJUST_RESIZE) {
cf.left = mDockLeft;
cf.top = mDockTop;
cf.right = mDockRight;
cf.bottom = mDockBottom;
} else {
cf.left = mContentLeft;
cf.top = mContentTop;
cf.right = mContentRight;
cf.bottom = mContentBottom;
}
}
} else {
// Full screen windows are always given a layout that is as if the
// status bar and other transient decors are gone. This is to avoid
// bad states when moving from a window that is not hding the
// status bar to one that is.
cf.left = mRestrictedScreenLeft;
cf.top = mRestrictedScreenTop;
cf.right = mRestrictedScreenLeft + mRestrictedScreenWidth;
cf.bottom = mRestrictedScreenTop + mRestrictedScreenHeight;
}
applyStableConstraints(sysUiFl, fl, cf);
if (adjust != SOFT_INPUT_ADJUST_NOTHING) {
vf.left = mCurLeft;
vf.top = mCurTop;
vf.right = mCurRight;
vf.bottom = mCurBottom;
} else {
vf.set(cf);
}
}
} else if ((fl & FLAG_LAYOUT_IN_SCREEN) != 0 || (sysUiFl
& (View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION)) != 0) {
if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle() +
"): IN_SCREEN");
//要求全屏的情况下的各种type对应的计算
// A window that has requested to fill the entire screen just
// gets everything, period.
if (attrs.type == TYPE_STATUS_BAR_PANEL
|| attrs.type == TYPE_STATUS_BAR_SUB_PANEL
|| attrs.type == TYPE_VOLUME_OVERLAY) {
pf.left = df.left = of.left = cf.left = hasNavBar
? mDockLeft : mUnrestrictedScreenLeft;
pf.top = df.top = of.top = cf.top = mUnrestrictedScreenTop;
pf.right = df.right = of.right = cf.right = hasNavBar
? mRestrictedScreenLeft+mRestrictedScreenWidth
: mUnrestrictedScreenLeft + mUnrestrictedScreenWidth;
pf.bottom = df.bottom = of.bottom = cf.bottom = hasNavBar
? mRestrictedScreenTop+mRestrictedScreenHeight
: mUnrestrictedScreenTop + mUnrestrictedScreenHeight;
if (DEBUG_LAYOUT) Slog.v(TAG, String.format(
"Laying out IN_SCREEN status bar window: (%d,%d - %d,%d)",
pf.left, pf.top, pf.right, pf.bottom));
} else if (attrs.type == TYPE_NAVIGATION_BAR
|| attrs.type == TYPE_NAVIGATION_BAR_PANEL) {
// The navigation bar has Real Ultimate Power.
pf.left = df.left = of.left = mUnrestrictedScreenLeft;
pf.top = df.top = of.top = mUnrestrictedScreenTop;
pf.right = df.right = of.right = mUnrestrictedScreenLeft
+ mUnrestrictedScreenWidth;
pf.bottom = df.bottom = of.bottom = mUnrestrictedScreenTop
+ mUnrestrictedScreenHeight;
if (DEBUG_LAYOUT) Slog.v(TAG, String.format(
"Laying out navigation bar window: (%d,%d - %d,%d)",
pf.left, pf.top, pf.right, pf.bottom));
} else if ((attrs.type == TYPE_SECURE_SYSTEM_OVERLAY
|| attrs.type == TYPE_BOOT_PROGRESS)
&& ((fl & FLAG_FULLSCREEN) != 0)) {
// Fullscreen secure system overlays get what they ask for.
pf.left = df.left = of.left = cf.left = mOverscanScreenLeft;
pf.top = df.top = of.top = cf.top = mOverscanScreenTop;
pf.right = df.right = of.right = cf.right = mOverscanScreenLeft
+ mOverscanScreenWidth;
pf.bottom = df.bottom = of.bottom = cf.bottom = mOverscanScreenTop
+ mOverscanScreenHeight;
} else if (attrs.type == TYPE_BOOT_PROGRESS) {
// Boot progress screen always covers entire display.
pf.left = df.left = of.left = cf.left = mOverscanScreenLeft;
pf.top = df.top = of.top = cf.top = mOverscanScreenTop;
pf.right = df.right = of.right = cf.right = mOverscanScreenLeft
+ mOverscanScreenWidth;
pf.bottom = df.bottom = of.bottom = cf.bottom = mOverscanScreenTop
+ mOverscanScreenHeight;
} else if (attrs.type == TYPE_WALLPAPER) {
// The wallpaper also has Real Ultimate Power, but we want to tell
// it about the overscan area.
pf.left = df.left = mOverscanScreenLeft;
pf.top = df.top = mOverscanScreenTop;
pf.right = df.right = mOverscanScreenLeft + mOverscanScreenWidth;
pf.bottom = df.bottom = mOverscanScreenTop + mOverscanScreenHeight;
of.left = cf.left = mUnrestrictedScreenLeft;
of.top = cf.top = mUnrestrictedScreenTop;
of.right = cf.right = mUnrestrictedScreenLeft + mUnrestrictedScreenWidth;
of.bottom = cf.bottom = mUnrestrictedScreenTop + mUnrestrictedScreenHeight;
} else if ((fl & FLAG_LAYOUT_IN_OVERSCAN) != 0
&& attrs.type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW
&& attrs.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
// Asking to layout into the overscan region, so give it that pure
// unrestricted area.
pf.left = df.left = of.left = cf.left = mOverscanScreenLeft;
pf.top = df.top = of.top = cf.top = mOverscanScreenTop;
pf.right = df.right = of.right = cf.right
= mOverscanScreenLeft + mOverscanScreenWidth;
pf.bottom = df.bottom = of.bottom = cf.bottom
= mOverscanScreenTop + mOverscanScreenHeight;
} else if (canHideNavigationBar()
&& (sysUiFl & View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0
&& (attrs.type == TYPE_STATUS_BAR
|| attrs.type == TYPE_TOAST
|| attrs.type == TYPE_VOICE_INTERACTION_STARTING
|| (attrs.type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW
&& attrs.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW))) {
// Asking for layout as if the nav bar is hidden, lets the
// application extend into the unrestricted screen area. We
// only do this for application windows (or toasts) to ensure no window that
// can be above the nav bar can do this.
// XXX This assumes that an app asking for this will also
// ask for layout in only content. We can't currently figure out
// what the screen would be if only laying out to hide the nav bar.
pf.left = df.left = of.left = cf.left = mUnrestrictedScreenLeft;
pf.top = df.top = of.top = cf.top = mUnrestrictedScreenTop;
pf.right = df.right = of.right = cf.right = mUnrestrictedScreenLeft
+ mUnrestrictedScreenWidth;
pf.bottom = df.bottom = of.bottom = cf.bottom = mUnrestrictedScreenTop
+ mUnrestrictedScreenHeight;
} else {
pf.left = df.left = of.left = cf.left = mRestrictedScreenLeft;
pf.top = df.top = of.top = cf.top = mRestrictedScreenTop;
pf.right = df.right = of.right = cf.right = mRestrictedScreenLeft
+ mRestrictedScreenWidth;
pf.bottom = df.bottom = of.bottom = cf.bottom = mRestrictedScreenTop
+ mRestrictedScreenHeight;
}
applyStableConstraints(sysUiFl, fl, cf);
if (adjust != SOFT_INPUT_ADJUST_NOTHING) {
vf.left = mCurLeft;
vf.top = mCurTop;
vf.right = mCurRight;
vf.bottom = mCurBottom;
} else {
vf.set(cf);
}
} else if (attached != null) {
if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle() +
"): attached to " + attached);
// A child window should be placed inside of the same visible
// frame that its parent had.
setAttachedWindowFrames(win, fl, adjust, attached, false, pf, df, of, cf, vf);
} else {
if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle() +
"): normal window");
// Otherwise, a normal window must be placed inside the content
// of all screen decorations.
//对于一个普通的窗口,需要考虑各种边衬的影响,比如状态栏导航栏都要考虑
if (attrs.type == TYPE_STATUS_BAR_PANEL || attrs.type == TYPE_VOLUME_OVERLAY) {
// Status bar panels and the volume dialog are the only windows who can go on
// top of the status bar. They are protected by the STATUS_BAR_SERVICE
// permission, so they have the same privileges as the status
// bar itself.
pf.left = df.left = of.left = cf.left = mRestrictedScreenLeft;
pf.top = df.top = of.top = cf.top = mRestrictedScreenTop;
pf.right = df.right = of.right = cf.right = mRestrictedScreenLeft
+ mRestrictedScreenWidth;
pf.bottom = df.bottom = of.bottom = cf.bottom = mRestrictedScreenTop
+ mRestrictedScreenHeight;
} else if (attrs.type == TYPE_TOAST || attrs.type == TYPE_SYSTEM_ALERT) {
// These dialogs are stable to interim decor changes.
pf.left = df.left = of.left = cf.left = mStableLeft;
pf.top = df.top = of.top = cf.top = mStableTop;
pf.right = df.right = of.right = cf.right = mStableRight;
pf.bottom = df.bottom = of.bottom = cf.bottom = mStableBottom;
} else {
pf.left = mContentLeft;
pf.top = mContentTop;
pf.right = mContentRight;
pf.bottom = mContentBottom;
if (win.isVoiceInteraction()) {
df.left = of.left = cf.left = mVoiceContentLeft;
df.top = of.top = cf.top = mVoiceContentTop;
df.right = of.right = cf.right = mVoiceContentRight;
df.bottom = of.bottom = cf.bottom = mVoiceContentBottom;
} else if (adjust != SOFT_INPUT_ADJUST_RESIZE) {
df.left = of.left = cf.left = mDockLeft;
df.top = of.top = cf.top = mDockTop;
df.right = of.right = cf.right = mDockRight;
df.bottom = of.bottom = cf.bottom = mDockBottom;
} else {
df.left = of.left = cf.left = mContentLeft;
df.top = of.top = cf.top = mContentTop;
df.right = of.right = cf.right = mContentRight;
df.bottom = of.bottom = cf.bottom = mContentBottom;
}
if (adjust != SOFT_INPUT_ADJUST_NOTHING) {
vf.left = mCurLeft;
vf.top = mCurTop;
vf.right = mCurRight;
vf.bottom = mCurBottom;
} else {
vf.set(cf);
}
}
}
}
// TYPE_SYSTEM_ERROR is above the NavigationBar so it can't be allowed to extend over it.
if ((fl & FLAG_LAYOUT_NO_LIMITS) != 0 && attrs.type != TYPE_SYSTEM_ERROR) {
df.left = df.top = -10000;
df.right = df.bottom = 10000;
if (attrs.type != TYPE_WALLPAPER) {
of.left = of.top = cf.left = cf.top = vf.left = vf.top = -10000;
of.right = of.bottom = cf.right = cf.bottom = vf.right = vf.bottom = 10000;
}
}
// If the device has a chin (e.g. some watches), a dead area at the bottom of the screen we
// need to provide information to the clients that want to pretend that you can draw there.
// We only want to apply outsets to certain types of windows. For example, we never want to
// apply the outsets to floating dialogs, because they wouldn't make sense there.
final boolean useOutsets = shouldUseOutsets(attrs, fl);
if (isDefaultDisplay && useOutsets) {
osf = mTmpOutsetFrame;
osf.set(cf.left, cf.top, cf.right, cf.bottom);
int outset = ScreenShapeHelper.getWindowOutsetBottomPx(mContext.getResources());
if (outset > 0) {
int rotation = mDisplayRotation;
if (rotation == Surface.ROTATION_0) {
osf.bottom += outset;
} else if (rotation == Surface.ROTATION_90) {
osf.right += outset;
} else if (rotation == Surface.ROTATION_180) {
osf.top -= outset;
} else if (rotation == Surface.ROTATION_270) {
osf.left -= outset;
}
if (DEBUG_LAYOUT) Slog.v(TAG, "applying bottom outset of " + outset
+ " with rotation " + rotation + ", result: " + osf);
}
}
if (DEBUG_LAYOUT) Slog.v(TAG, "Compute frame " + attrs.getTitle()
+ ": sim=#" + Integer.toHexString(sim)
+ " attach=" + attached + " type=" + attrs.type
+ String.format(" flags=0x%08x", fl)
+ " pf=" + pf.toShortString() + " df=" + df.toShortString()
+ " of=" + of.toShortString()
+ " cf=" + cf.toShortString() + " vf=" + vf.toShortString()
+ " dcf=" + dcf.toShortString()
+ " sf=" + sf.toShortString()
+ " osf=" + (osf == null ? "null" : osf.toShortString()));
//开始计算,pf, df, of, cf, vf, dcf, sf, osf计算出来的这几个是最大允许值,还要计算最终值
win.computeFrameLw(pf, df, of, cf, vf, dcf, sf, osf);
// Dock windows carve out the bottom of the screen, so normal windows
// can't appear underneath them.
if (attrs.type == TYPE_INPUT_METHOD && win.isVisibleOrBehindKeyguardLw()
&& !win.getGivenInsetsPendingLw()) {
setLastInputMethodWindowLw(null, null);
offsetInputMethodWindowLw(win);
}
if (attrs.type == TYPE_VOICE_INTERACTION && win.isVisibleOrBehindKeyguardLw()
&& !win.getGivenInsetsPendingLw()) {
offsetVoiceInputWindowLw(win);
}
}
再来看下computeFrameLw()这个函数,窗口大小的计算最后都需要他确定,这个函数主要是综合各种计算结果和条件给出最后的窗口的大小,然后通过如下函数:
Gravity.apply(mAttrs.gravity, w, h, mContainingFrame,
(int) (x + mAttrs.horizontalMargin * pw),
(int) (y + mAttrs.verticalMargin * ph), mFrame);
//System.out.println("Out: " + mFrame);
// Now make sure the window fits in the overall display.
Gravity.applyDisplay(mAttrs.gravity, df, mFrame);
将最终的值设置给mFrame
Activity创建Surface
1、当ViewRootImpl.setView调用时会requestLayout,然后就会进行第一次的traversals,经过VSYNC同步之后调用performTraversals,然后会调用relayoutWindow在relayoutWindow中,会调用mWindowSession.relayout(mWindow, ... , mSurface),注意mSurface在构造ViewRootImpl中已经初始化new Surface,但内部的native对象是无效,这里会调用到Session.relayout,注意这里的Surface对象传递过程是通过在服务进程侧new
Surface构造另外一个Surface,然后通过readFromParcel/WriteToParcel来实现对象的序列化。
2、Sessioin.relayout()只是简单的调用mService.relayoutWindow(this, window, xxx, outSurface);也就是WindowManagerService.relayoutWindow,其中,通过windowForClientLocked(session, client, flase)获取WindowState win,然后获取win.mWinAnimator对象,调用WindowStateAnimator.createSurfaceLocked
3、在WindowStateAnimator.createSurfaceLocked中,会构造mSurfaceControl = new SurfaceControl(mSession.mSurfaceSession, xxx);
4、在SurfaceControl的构造中,会接收SurfaceSession作为参数,内部调用nativeCreate(session,xxx);通过jni调用到c++层,然后会获取SurfaceSession之前获取的SurfaceComposerClient,然后调用SurfaceComposerClient::createSurface来想SurfaceFlinger申请一个Layer,同时返回SurfaceControl对象
5、继续在WindowStateAnimator.createSurfaceLocked中,运行了以下代码来设置SurfaceFlinger进程端对应的Layer的值。具体流程见bootanimation例子。
SurfaceControl.openTransaction();
mSurfaceControl.setPosition(mSurfaceX, mSurfaceY);
mSurfaceControl.setLayerStack(mLayerStack);
mSurfaceControl.setLayer(mAnimLayer);
mSurfaceControl.setAlpha(0);
SurfaceControl.closeTransaction();
6、经过以上步骤完成SurfaceControl的初始化后,返回到WindowStateAnimator.createSurfaceLocked中,会调用outSurface.copyFrom(surfaceControl),复制一个Surface,Surface.copyFrom会调用jni方法,nativeCreateFromSurfaceControl,而这个方法内部调用SurfaceControl::getSurface获取Surface,然后返回。
Android window的层级(Z 轴)的计算过程:
在添加窗口时,应用会调用Session的addToDisplay()方法,进而调用WindowManagerService的addWindow()方法。在addWindow中会创建一个WindowState对象,在这个对象的构造函数中会通过windowTypeToLayerLw()和subWindowTypeToLayerLw()两个方法依据当前窗口的type等信息计算出mBaseLayer和mSubLayer这两个值。SubLayer值并不直接参与窗口的Z轴位置的计算,但是它会影响到窗口在窗口堆栈的位置,窗口在窗口堆栈的位置是会影响到它的Z轴位置的计算的。其实他的最终效果就是解决baseLayer值相同的时候那就看subLayer了。
在WindowManagerService的relayoutWindow方法中会调用assignLayersLocked(),在这里完成了最终的Z轴位置的计算。在assignLayersLocked中先计算各个WindowStatus的mLayer信息,然后加上附加的偏置就得到了mWinAnimator(WindowStateAnimator)的mAnimLayer值,这个值就是最终的Z轴值了。
在后面的createSurfaceLocked()中,调用mSurfaceControl.setLayer(mAnimLayer)来将他最终设置给SurfaceFlinger。
Android inset的调整过程:
WindowState.reportResized() --> W.resized() --> ViewRootImpl.dispatchResized() --> ViewRootHandler.handleMessage()