android 非全屏界面,Android进阶:非全屏的Window无法设置SYSTEM_UI_FLAG_LIGHT_STATUS_BAR问题分析...

小编在做沉浸式状态栏功能时,遇到一个这样的问题:

当我在一个Dialog的onCreate()方法中执行下面的代码:

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.dialog_fullscreen);

Window window = getWindow();

if (window != null) {

window.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams

.MATCH_PARENT);

window.setGravity(Gravity.TOP);

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {

window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);

window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);

window.setStatusBarColor(Color.GREEN);

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {

window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);

}

}

}

}

效果是这样的:

88e9e889932c

当我把第7行代码改成这样:

window.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);

效果变成了这样:

88e9e889932c

大家仔细观察会发现当弹窗设置全屏时状态栏的图标文字是黑色的,而设置成非全屏时图标文字却是白色的。而这两种状态下我都通过代码window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR)设置状态栏为亮色模式(状态栏图标和文字变成黑色),但是非全屏时却失效了,这是为什么呢?下面通过源码分析一下:

在LightBarController.java中,方法updateStatus()是用来设置状态栏图标颜色的:

private void updateStatus(Rect fullscreenStackBounds, Rect dockedStackBounds) {

boolean hasDockedStack = !dockedStackBounds.isEmpty();

// If both are light or fullscreen is light and there is no docked stack, all icons get

// dark.

if ((mFullscreenLight && mDockedLight) || (mFullscreenLight && !hasDockedStack)) {

mStatusBarIconController.setIconsDarkArea(null);

mStatusBarIconController.getTransitionsController().setIconsDark(true, animateChange());

}

// If no one is light or the fullscreen is not light and there is no docked stack,

// all icons become white.

else if ((!mFullscreenLight && !mDockedLight) || (!mFullscreenLight && !hasDockedStack)) {

mStatusBarIconController.getTransitionsController().setIconsDark(

false, animateChange());

}

// Not the same for every stack, magic!

else {

Rect bounds = mFullscreenLight ? fullscreenStackBounds : dockedStackBounds;

if (bounds.isEmpty()) {

mStatusBarIconController.setIconsDarkArea(null);

} else {

mStatusBarIconController.setIconsDarkArea(bounds);

}

mStatusBarIconController.getTransitionsController().setIconsDark(true, animateChange());

}

}

这里解释如下:

如果在全屏和Docked下开启亮色模式或者在全屏下开启亮色模式且没有Docked栈时,状态栏图标设成黑色;

如果在全屏和Docked下均未开启亮色模式或者在全屏下未开启亮色模式且没有Docked栈时,状态栏图标设成白色;

如果以上情况均不符,则状态栏图标设成黑色。

那么什么叫Docked呢?其实Android为了支持多窗口,在运行时创建了多个Stack,系统中可能会包含这么几个Stack:

Home Stack:这个是Launcher所在的Stack。 其实还有一些系统界面也运行在这个Stack上,例如近期任务;

FullScreen Stack:全屏的Activity所在的Stack;

Freeform模式的Activity所在Stack;

Docked Stack:在分屏模式下,屏幕有一半运行了一个固定的应用(一般在上方),这个就是这里的Docked Stack;

Pinned Stack:这个是画中画Activity所在的Stack。

了解了Fullscreen和Docked栈的概念,再加上上面的代码,我们就可以得出结论,其实只有全屏或分屏时应用处于屏幕的上方,才能修改状态栏的颜色。这个大家也可以自己测试一下。

那么,mFullscreenLight和mDockedLight是怎么来的呢?这个可以追溯到调用的代码:

public void onSystemUiVisibilityChanged(int fullscreenStackVis, int dockedStackVis,

int mask, Rect fullscreenStackBounds, Rect dockedStackBounds, boolean sbModeChanged,

int statusBarMode) {

int oldFullscreen = mFullscreenStackVisibility;

int newFullscreen = (oldFullscreen & ~mask) | (fullscreenStackVis & mask);

int diffFullscreen = newFullscreen ^ oldFullscreen;

int oldDocked = mDockedStackVisibility;

int newDocked = (oldDocked & ~mask) | (dockedStackVis & mask);

int diffDocked = newDocked ^ oldDocked;

if ((diffFullscreen & View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR) != 0

|| (diffDocked & View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR) != 0

|| sbModeChanged

|| !mLastFullscreenBounds.equals(fullscreenStackBounds)

|| !mLastDockedBounds.equals(dockedStackBounds)) {

mFullscreenLight = isLight(newFullscreen, statusBarMode,

View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);

mDockedLight = isLight(newDocked, statusBarMode, View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);

updateStatus(fullscreenStackBounds, dockedStackBounds);

}

mFullscreenStackVisibility = newFullscreen;

mDockedStackVisibility = newDocked;

mLastStatusBarMode = statusBarMode;

mLastFullscreenBounds.set(fullscreenStackBounds);

mLastDockedBounds.set(dockedStackBounds);

}

在这里可以看到16-18行就是赋值的地方,同时也看到了这里使用了View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR常量。

再跟进isLight()方法:

private boolean isLight(int vis, int barMode, int flag) {

boolean isTransparentBar = (barMode == MODE_TRANSPARENT

|| barMode == MODE_LIGHTS_OUT_TRANSPARENT);

boolean light = (vis & flag) != 0;

return isTransparentBar && light;

}

这里的第4行可以看到,其实就是newFullscreen或者newDocked和SYSTEM_UI_FLAG_LIGHT_STATUS_BAR按位与来判断是否为亮色模式。至于newFullscreen和newDocked,它们是传递过来的fullscreenStackVis和dockedStackVis再进行一些位操作生成的,而fullscreenStackVis和dockedStackVis是一层一层的方法调用传过来的,这里我们就不具体分析了,有兴趣的读者可以自己再去研究。

以上的分析适用于Activity和Dialog等所持有的Window,那么如果读者有做类似于我示例中的半弹窗效果,并且还有设置状态栏需求的话,我们可以改成做一个全屏的弹窗,然后把不想展示出来的界面设成透明的背景,就可以实现我们的需求啦!

个人一点拙见,有什么不对的地方欢迎大家一起交流~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值