Android Q 黑暗模式(Dark Mode)源码解析
1. 简介
随着 Android Q 发布,「黑暗模式」或者说是「夜间模式」终于在此版本中得到了支持,官方介绍见:https://developer.android.com/guide/topics/ui/look-and-feel/darktheme,再看看效果图:

其实这个功能魅族在两年前就已支持,不得不说 Android 有点落后了,今天我们就来看看原生是怎么实现全局夜间模的吧
2. 打开与关闭
从文档上我们可以可知,打开夜间模式有三个方法:
- 设置 -> 显示 -> 深色主题背景
- 下拉通知栏中开启
- Pixel 手机开启省点模式时会自动激活夜间模式
3. 如何适配
打开后,我们会发现,除原生几个应用生效外,其他应用依然没有变成深色主题,那么应用该如何适配呢?官方提供了下面两种方法:
3.1. 让应用主题继承 DayNight 主题
1 | <style name="AppTheme" parent="Theme.AppCompat.DayNight"> |
或者继承自
1 | <style name="AppTheme" parent="Theme.MaterialComponents.DayNight"> |
继承后,如果当前开启了夜间模式,系统会自动从 night-qualified 中加载资源,所以应用的颜色、图标等资源应尽量避免硬编码,而是推荐使用新增 attributes 指向不同的资源,如
1 | ?android:attr/textColorPrimary |
另外,如果应用希望主动切换夜间/日间模式,可以通过 AppCompatDelegate.setDefaultNightMode() 接口主动切换
3.2. 通过 forceDarkAllowed 启用
如果应用不想自己去适配各种颜色,图标等,可以通过在主题中添加 android:forceDarkAllowed="true" 标记,这样系统在夜间模式时,会强制改变应用颜色,自动进行适配(这个功能也是本文主要探讨的)。不过如果你的应用本身使用的就是 DayNight 或 Dark Theme,forceDarkAllowed 是不会生效的。
另外,如果你不希望某个 view 被强制夜间模式处理,则可以给 view 添加 android:forceDarkAllowed="false" 或者 view.setForceDarkAllowed(false),设置之后,即使打开了夜间模式且主题添加了 forceDarkAllowed,该 view 也不会变深色。比较重要的一点是,这个接口只能关闭夜间模式,不能开启夜间模式,也就是说,如果主题中没有显示声明 forceDarkAllowed,view.setForceDarkAllowed(true) 是没办法让 view 单独变深色的。如果 view 关闭了夜间模式,那么它的子 view 也会强制关闭夜间模式
总结如下:
- 主题若添加 forceDarkAllowed=false,无论 view 是否开启 forceDarkAllowed 都不会打开夜间模式
- 主题若添加 forceDarkAllowed=true,view 可以通过 forceDarkAllowed 关闭夜间模式,一旦关闭,子 view 的夜间模式也会被关闭
- 如果父 view 或主题设置了 forceDarkAllowed=false,子 view 无法通过 forceDarkAllowed=true 单独打开夜间模式为
- 若使用的是
DayNight或Dark Theme主题,则所有 forceDarkAllowed 都不生效
4. 实现原理
通过继承主题适配夜间模式的原理本质是根据 ui mode 加载 night-qualified 下是资源,这个并非 Android Q 新增的东西,我们这里不再描述。现在主要来看看 forceDarkAllowed 是如何让系统变深色的。
既然一切的源头都是 android:forceDarkAllowed 这个属性,那我们就从它入手吧,首先我们要知道,上面我们说的 android:forceDarkAllowed 其实是分为两个用处,它们分别的定义如下:
frameworks/base/core/res/res/values/attrs.xml
1 | <declare-styleable name="View"> |