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

适配暗色模式

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

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

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

public boolean isDarkMode() {    int mode = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;    return mode == Configuration.UI_MODE_NIGHT_YES;}
  • 设置forceDarkAllowed属性

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

<item name="android:forceDarkAllowed">trueitem>

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

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

遇到的bug

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

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

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, 是否就可以解决这个问题。

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,需要通过位运算来处理

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,这个是叠加的。

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

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会覆盖前面的属性。

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

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​


更多完整面试专题和进阶知识分享,尽在“Android扫地僧”

更多相关文章

  1. Android学习之练笔---计算器的实现
  2. g1安装android软件详细攻略
  3. 如何访问android的asset目录和res目录下的文件
  4. Android面试经验一:
  5. Android(安卓)使用shape来优化界面效果
  6. Android里的动画(补间动画,帧动画,属性动画)
  7. android 双向滑动 稍加改进,可做成QQHD效果
  8. Android歌词秀设计思路(6)运用Proxy设计模式简化歌词播放服务的使
  9. Android(安卓)多个 Activity 调用 跳转

随机推荐

  1. android代码混淆
  2. Android(安卓)游戏引擎libgdx 锁屏、电话
  3. android真机测试问题
  4. 「抄底 Android(安卓)内存优化 1」——
  5. Android(安卓)Studio 设置控件和颜色透明
  6. Android(安卓)ormlite 框架介绍
  7. android 4中新增的日历处理相关API
  8. Android(安卓)实现联网——在线程中联网
  9. Android网络连接处理学习笔记
  10. Android多屏幕适配