这篇博客我们主要分析下,窗口位置排序的一些原理。
一、添加窗口的时候 调整窗口位置
上篇博客我们分析了WMS的addWindow函数,这里我们就窗口的次序问题继续分析。
boolean imMayMove = true;
if (type == TYPE_INPUT_METHOD) {//如果窗口类是输入法窗口
win.mGivenInsetsPending = true;
mInputMethodWindow = win;
addInputMethodWindowToListLocked(win);//插入输入法窗口到应用窗口上层
imMayMove = false;
} else if (type == TYPE_INPUT_METHOD_DIALOG) {//如果窗口是输入法对话框
mInputMethodDialogs.add(win);
addWindowToListInOrderLocked(win, true);//插入到正常位置
moveInputMethodDialogsLocked(findDesiredInputMethodWindowIndexLocked(true));//调整对话框位置
imMayMove = false;
} else {
addWindowToListInOrderLocked(win, true);//插入正常位置
if (type == TYPE_WALLPAPER) {
mLastWallpaperTimeoutTime = 0;
displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
} else if ((attrs.flags&FLAG_SHOW_WALLPAPER) != 0) {
displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
} else if (mWallpaperTarget != null
&& mWallpaperTarget.mLayer >= win.mBaseLayer) {
// If there is currently a wallpaper being shown, and
// the base layer of the new window is below the current
// layer of the target window, then adjust the wallpaper.
// This is to avoid a new window being placed between the
// wallpaper and its target.
displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
}
}
DisplayContent类的mWindows列表按Z序保存了每个窗口,这段代码就是在根据窗口类型把窗口加入到DisplayContent合适位置。
addInputMethodWindowToListLocked方法作用就是一个输入法窗口放子啊需要显示输入法窗口的上面。
addWindowToListInOrderLocked将一个窗口插入到窗口堆栈的当前位置。
我们继续看addWindow函数,
final WindowStateAnimator winAnimator = win.mWinAnimator;
winAnimator.mEnterAnimationPending = true;
winAnimator.mEnteringAnimation = true;
if (displayContent.isDefaultDisplay) {
mPolicy.getInsetHintLw(win.mAttrs, mRotation, outContentInsets, outStableInsets,//计算窗口大小
outOutsets);
} else {
outContentInsets.setEmpty();
outStableInsets.setEmpty();
}
if (mInTouchMode) {
res |= WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE;//加入支持触屏的标志
}
if (win.mAppToken == null || !win.mAppToken.clientHidden) {
res |= WindowManagerGlobal.ADD_FLAG_APP_VISIBLE;//加入应用可用的标志
}
mInputMonitor.setUpdateInputWindowsNeededLw();//设置更新输入法窗口的标志
boolean focusChanged = false;
if (win.canReceiveKeys()) {//如果窗口能接受输入计算是否引起焦点变化
focusChanged = updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS,
false /*updateInputWindows*/);
if (focusChanged) {
imMayMove = false;
}
}
if (imMayMove) {
moveInputMethodWindowsIfNeededLocked(false);//调整输入法的窗口位置
}
assignLayersLocked(displayContent.getWindowList());//重新计算z轴的位置
// Don't do layout here, the window must call
// relayout to be displayed, so we'll do it there.
if (focusChanged) {
mInputMonitor.setInputFocusLw(mCurrentFocus, false /*updateInputWindows*/);
}
mInputMonitor.updateInputWindowsLw(false /*force*/);//更新输入法窗口的信息
if (localLOGV || DEBUG_ADD_REMOVE) Slog.v(TAG, "addWindow: New client "
+ client.asBinder() + ": window=" + win + " Callers=" + Debug.getCallers(5));
if (win.isVisibleOrAdding() && updateOrientationFromAppTokensLocked(false)) {
reportNewConfig = true;//配置发生变化
}
}
if (reportNewConfig) {
sendNewConfiguration();//发送新的配置
}
Binder.restoreCallingIdentity(origId);
return res;
}
如果窗口显示在缺省的显示设备,调用mPolicy的getInsetHintLw函数来获得除了状态条、导航条所占区域后的窗口大小。
接下来如果窗口能接受输入,调用updateFocusedWindowLocked来重新确定系统的焦点位置。如果焦点发生变化,则将imMayMove置为false。
新加入的窗口的位置在前面调用addWindowToListInOrderLocked的时候位置已经确定了,所以这里调用assignLayersLocked只是重新计算Z轴的位置。如果调用updateOrientationFromAppTokensLocked函数计算窗口的配置发生变化,调用sendNewConfiguration函数发送配置。
二、确定窗口的mLayer值
显示设备的水平方向,垂直方向作为X轴Y轴,我们还可以想象有一个垂直于屏幕的Z轴,Z轴的值越来越靠近屏幕。系统中所有的窗口都按次序排列在Z轴上。窗口对象WindowState的成员变量mLayer表示窗口在Z轴的值,值越小越靠近底层。
WMS作用之一就是管理各个窗口Z轴位置,确保正确显示。在所有窗口中输入法和壁纸窗口比较特殊。输入法窗口出现时,需要显示在应用窗口的前面。壁纸窗口通常在底层,但是又不是位于所有窗口的底层,而是位于当前Activity窗口的下面。
因此,当系统调整某个应用窗口的位置时,如果需要也会调整输入法和壁纸窗口,使当前Activity的窗口位于输入法窗口和壁纸窗口之间。
WindowState的成员变量mLayer的值表示窗口在Z轴的位置,这个值越小,位置越靠下。mLayer是通过计算得到,会经常变化。WindowState的另一个成员变量mBaseLayer的值是固定不变的,只和窗口类型有关。mLayer的值是根据mBaseLayer的值计算而来。
下面我们先来看看mBaseLayer的值如何而来,在WindowState的构造函数中有如下代码:
if ((mAttrs.type >= FIRST_SUB_WINDOW &&
mAttrs.type <= LAST_SUB_WINDOW)) {//如果是子窗口
// The multiplier here is to reserve