Android 适配深色模式的总结

8,015 阅读2分钟

Android Q 推出了深色模式,其实 Android 9 就有了,部分厂商小米,三星就在系统 Android 9 加入了深色模式的开关。

Android 提供了一套夜间模式主题,继承 Theme.MaterialComponents.DayNight.NoActionBar 主题即可。

然后在 res 文件夹创建 values-night 将深色模式的 color.xml 拷贝过来,系统在深色模式下会自动识别该目录下的 color 信息。当然,除了 color 之外的资源也是一样的。

同理 drawable 同样可以创建一个 drawable-night 来区别不同的资源。

判断深色模式

系统有几个 mask 值:

// 深色
public static final int UI_MODE_NIGHT_YES = 0x20;
// 浅色
public static final int UI_MODE_NIGHT_NO = 0x10;


// 判断是否是深色
context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES
 // 全局设置夜间模式     
 
 AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
 
 // 在 App 内部切换主题的时候重新创建 Activity 以应用
recreate()

初次进入 App 的时候,如果需要指定 Dark Mode,在你的 Application 中初始化:

AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)

自定义处理

系统在切换深色/浅色模式时,会在 Actvity/Fragment 中回调


  override fun onConfigurationChanged(newConfig: Configuration) {
    super.onConfigurationChanged(newConfig)
    if(dark){
        //
    }else{
        //
    }
  }

我们只要在这个回调填写 ui 深色变换的逻辑即可。

__注意: Activity 必须注册 android:configChanges="uiMode" 才能收到onConfigurationChanged的回调

<activity
        android:name=".ui.main.home.HomeActivity"
        android:configChanges="uiMode">

基于这些基础,可以沉淀抽象出一套通用的 dake mode 库

一个额外发现

在应用了 Theme.MaterialComponents.DayNight 之后,同时系统切到了深色模式,很多页面的背景变成了全黑,拖动列表 item 还会有残影,在 twitter,知乎,即刻,华为商店,都看到过这种残影效果,之前在酷安看到一个图片中带黑色的局部,拖动图片,图片上的黑色局部也有那种残影效果,推测是利用屏幕的某种特性造成了这种视觉效果。之前我还以为是即刻和知乎自己自定义的这种动画效果。

不过也带来一个额外的问题-(App内部代码层面关闭了深色模式,但是系统的深色模式还开着时,这些页面依然是黑色的)。

这是因为 App 的 value 资源是按系统的深色模式来加载,只要系统的深色模式开着,不管 App 内部是否关闭了深色模式,总是走 value-night 的资源。最后的妥协是放弃在 App 内部自定义切换主题,这样最简单也省去了很多麻烦,(解决 bug 的最好办法,就是砍掉出 bug 的功能。逃😄2333333333333333333333333

参考

user-interface-samples/DarkTheme/

全网最详!暗黑模式在 Trip.com App 的实践