前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android Q暗色模式适配踩坑—状态栏

Android Q暗色模式适配踩坑—状态栏

作者头像
Android扫地僧
发布2020-04-13 13:41:51
2K0
发布2020-04-13 13:41:51
举报
文章被收录于专栏:Android进阶

暗色模式已经不是什么新鲜玩意了,大家最近看到关于暗色模式最多的内容可能就是iOS版本微信未适配暗色模式面临被AppStore下架的风险。然后今天早上一醒来,发现Android的微信也黑了(因为我手机一直用的暗色模式),然后最近也遇到了一个暗色模式适配的一个坑,就拿出来讲一讲。

适配暗色模式

在开始之前还是提一下,暗色模式的一个适配方式。这个谷歌官方讲的很清楚,方式有两种:

  • 定义两套主题(正常模式和黑暗模式)

这种方式较为复杂,需要在style下定义正常模式和暗色模式两套app_theme,且必须继承自Theme.AppCompat.DayNight.DarkActionBar,然后提取出需要适配暗色模式的属性,最后在BaseActivity的onCreate方法中,根据当前模式设置不同的主题即可。

判断系统当前是否暗色模式:

代码语言:javascript
复制
public boolean isDarkMode() {    int mode = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;    return mode == Configuration.UI_MODE_NIGHT_YES;}
  • 设置forceDarkAllowed属性

这种方式简单粗暴,只需要app_theme中声明

代码语言:javascript
复制

代码语言:javascript
复制
<item name="android:forceDarkAllowed">true</item>

应用会在系统切换暗色时,自动适配,这个前提就是不要使用硬编码颜色值。同样需要准备两套资源,暗色模式需要的资源文件,放在以values-night命名的资源目录下,在不同模式下,会自动读取对应目录下的资源。

forceDrakAllowed不仅可以用在App主题级别,也可以直接使用在View上。如果仅需某个View适配暗色模式,直接在view属性声明即可。同理,如果某个View在暗色模式下,不需要适配,通过设置forceDrakAllowed为false即可,或者通过view.setForceDarkAllowed(false)。

遇到的bug

暗色模式下,状态栏没有反色,导致看不清。

这个很好定位,肯定是StatusBar状态写死了,去代码里面看看

代码语言:javascript
复制
private void setStatusBarColor() {    Window window = getWindow();    window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);    window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE            | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);    window.setStatusBarColor(Color.TRANSPARENT);    window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);}

可以看到,之前应该是因为某种业务需要,所以将状态栏设置了LIGHT_STATUS_BAR这个flag。

方案一:

我们知道,如果不认为去设置SystemUI的Visibility,系统会自动根据当前主题颜色来适配状态栏是否进行反色,那么我们如果去掉这个这个人为设置的flag, 是否就可以解决这个问题。

代码语言:javascript
复制
private void setStatusBarColor() {    Window window = getWindow();    window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);    window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE            | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);    window.setStatusBarColor(Color.TRANSPARENT);    //去掉LIGHT_STATUS_BAR这个flag    //window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);}

结果如下:

暗色模式虽然状态栏反色了,但是正常模式下,又看不到了。也就是说,暗色模式下的状态栏,需要自己适配。并且,Activity的内容与状态栏出现了重叠。

方案二:

既然无法自动反色,那就适配咯,原本逻辑咱们不改动,加个判断在暗色模式时,咱们设置一个DRAK_STATUS_BAR属性是不是就可以了。开玩笑哈,View属性里面并没有这个flag,需要通过位运算来处理

代码语言:javascript
复制
private void setStatusBarColor() {    Window window = getWindow();    window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);    window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE            | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);    window.setStatusBarColor(Color.TRANSPARENT);    if (isDarkMode()) {        int uiOption = window.getDecorView().getSystemUiVisibility();        //没有DARK_STATUS_BAR属性,通过位运算将LIGHT_STATUS_BAR属性去除        window.getDecorView().setSystemUiVisibility(uiOption & ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);    } else {        window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);    }}

结果如下:

正常模式和暗色模式,状态栏都已经正常反色,但是暗色模式下,Activity内容依然与状态栏重叠。

方案三:

通过对比不难发现,只有暗色模式重叠,无非就是因为我们保留了之前所设置的FLAG,这里要注意,这里的FLAG是通过set方法来设置的,也就是说,后面的只会覆盖前面的,而不像我们平时所使用的addFlags,这个是叠加的。

再来回顾一下,没有修改前的代码:

代码语言:javascript
复制
private void setStatusBarColor() {    Window window = getWindow();    window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);    //第一次set了两个属性    window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE            | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);    window.setStatusBarColor(Color.TRANSPARENT);    //这里又一次set,也就是前面的e两个属性根本没有使用    window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);}

而我们出现重叠的原因,就是因为保留了之前的属性,其中SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN就是导致重叠的真凶,作用是在不隐藏StatusBar的情况下,将view所在window的显示范围扩展到StatusBar下面。之所以正常模式下,不会出现重叠,是因为二次设置LIGHT_STATUS_BAR会覆盖前面的属性。

很明显,我们的内容并不需要延伸至状态栏下,所以前面的代码就是无用的,删除即可。

代码语言:javascript
复制
private void setStatusBarColor() {    Window window = getWindow();    window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);        //window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE        //  | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);    window.setStatusBarColor(Color.TRANSPARENT);    int uiOption = window.getDecorView().getSystemUiVisibility();    if (isDarkMode()) {        //没有DARK_STATUS_BAR属性,通过位运算将LIGHT_STATUS_BAR属性去除        window.getDecorView().setSystemUiVisibility(uiOption & ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);    } else {        //这里是要注意的地方,如果需要补充新的FLAG,记得要带上之前的然后进行或运算        window.getDecorView().setSystemUiVisibility(uiOption | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);    }}

最后的真凶,并不是暗色模式导致了重叠,而是原代码作者留下的坑。主要还是对于SystemUI Flag的一些属性不熟导致。OK,修改完效果如下。

对于SystemUI的一些FLAG作用不清楚的同学,可以参考下面这个文章: https://www.jianshu.com/p/e6656707f56c


本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-04-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Android扫地僧 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档