在 2019 年的 Google I/O 和 Apple WWDC 上,新露面的 Android 10 和 iOS 13 都宣布将支持 Dark Theme 也就是我们常说的暗黑模式,并提供相关 API 供开发者适配。
那么,为什么我们需要暗黑模式?暗黑模式到底有什么好处?Android 开发者该如何适配暗黑模式呢?今天这篇文章就告诉你。
Why we need Dark Theme?
暗色主题的 App 比比皆是,但是让 Android 和 iOS 从系统层级支持暗黑模式还是头一次。也许是用户的呼声也许是工业的推动,发展得益于进步和反馈,暗黑模式就这样来到了你我的手机上。
以上这张图截选自 Android 官方文档,上面提出了 Dark Theme 的三点好处:
- 可以帮助我们节省更多的电量
- 为弱视以及对强光敏感的用户提高可视性
- 让所有人都可以在光线较暗的环境中更轻松地使用设备
这得益于 OLED 屏幕的发展,与 LCD 不同 OLED 能够自发光,每个像素点都能发出红绿蓝三色光,而 LCD 则通过其背光层穿透有颜色的薄膜来发出不同的光。这样在暗黑模式下,OLED 就具有天生的优势,只需要关闭黑色区域的显示,就可以达到纯黑效果,而 LCD 的背光层只能发射白光,所以在显示黑色的时候,仍然会有部分光透过颜色薄膜,无法达到纯黑效果,只能达到相对黑的效果。
所以,在如今使用 OLED 屏幕的手机上面,开启暗黑模式会大幅减少手机电量的消耗。关于其中更详细的作用原理以及好处,推荐阅读来自中科院物理所公众号的文章,点击下方查看。
为你的 Android App 适配暗黑模式
适配大概可以分为三部分工作:
- 为应用内的背景、文字、图标做适配
- 对在设备上显示但并不直接控制的界面进行适配,例如通知、桌面组件
- 在应用内为用户提供切换主题的开关选项
适配的方式也分为三种:自动适配、自定义适配、使用 Material Design Components 进行适配,为了方便说明,我这里写了一个实例代码,在没有开启适配前是这个样子的。
自动适配 Force Dark
Android 10 提供 Force Dark 功能。此功能可让开发者快速实现深色主题背景,只需要在 style.xml 中的应用主题中添加这一行代码 android:forceDarkAllowed="true" ,就可以完成自动适配。
如果您的应用采用浅色主题背景,则 Force Dark 会分析应用的每个视图,并在相应视图在屏幕上显示之前,自动应用深色主题背景。当然这里的适配工作全是系统自己做的,所以作为开发者你需要进行详细的测试,确保没有错误的地方。
从上面的截图来看,自动适配的效果很不错,关于自动适配的原理,这里有一篇文章对此进行了详细的说明,文章地址在最后。
自定义适配
自定义适配的关键在于,避免一切硬编码的颜色值,建立 light 、night 两份 colors,通过定义相同的名字,在不同模式下显示不同的颜色。
1. 确保当前 App 使用的主题继承自 AppCompat 或 MaterialComponents,并将之前默认的 Light 主题修改为 DayNight
2.为 Dark Mode 定义 colors
新建完成之后我们的 values 下会有两份 colors 文件,工程结构如下图所示
3. 根据需求对不同模式下相同名字的颜色值做适配
这里推荐使用颜色本身的场景意义来命名,例如上图中的 colorBackground 表示背景颜色,colorOnError 表示在错误状态上,文字的显示颜色,在控件需要颜色的地方,直接通过 @color/colorXXX 来使用
自定义适配完全可以达到任何我们想要的效果,相比自动适配,这里我对状态栏、错误颜色以及强调色进行了微调。
使用 Material Design Components 进行适配
Material Design Components 不仅仅包含了各式各样的组件,还完善了 Material Design 的规范,相比组件来说更重要的是 MD 设计规范。
上图为 Material Design 的官网,在设计一览中,详细制定了各种规范,例如颜色系统该如何设计、阴影的原理和规范、字体的规范、图标该如何选择、交互效果该怎样设计等等。
我个人来说是非常喜欢 Material Design ,之后会计划在这个公众号上面为大家详细的介绍一下Material Design 到底是怎样的,相信很多人都知道 Material Design 但真正读过它的文档,以及认真了解过其设计原理的人,应该寥寥无几。
所以说不是用了 Material Design 的组件就代表自己的 App 遵循了 Material Design 的设计规范,也更别说连这个规范都没看过的人,就一杆子打死说 Material Design 反人类。
在 Material Design 官网颜色系统的设计中,专门讲解了关于 Dark theme 该如何设计,之所以叫做 Dark theme,所有的适配都是围绕 theme 来进行的。而上一节的自定义适配中,我们只是简单粗暴的通过 colors 来适配。
Material Design Components 中内置了 12 种不同场景的颜色属性,分别为主强调色、次强调色,背景颜色、表面颜色、错误颜色、以及字体和 icon 的颜色(以 on 开头的)。
所以在使用 Material Design Components 进行适配的时候,我们需要定义两种 theme,分别代表 light 和 night,通过分别定义两个主题中相同场景意义的颜色属性来实现暗黑模式的切换。而上一部分自定义适配则是,通过定义一份主题,在同一主题下通过控制 light 、night 两种颜色来实现切换。
相比自定义适配、使用 Material Design Components 适配从设计的角度上更加规范,同时对 theme、attr、style 的不同使用场景也做了更加明确的区分,通过 theme 来规范属性,在控件中不直接操作颜色,而是通过属性来操作颜色,因为属性具有明确的场景意义。
而 colors 就只需要定义一份,这里推荐使用于颜色本身来命名。
上面就是我针对 light、night 分别定义的两份 theme。
你有没有发现这里我们不需要手动定义 textColor 了,因为前面说了,Material Design Components 内置了 12 种颜色,而我们的这个 TextView 是位于 CardView 上面的,CardView 的默认颜色就是我们 theme 中定义的 colorSurface,TextView 的默认颜色就是 colorOnSurface,所以我们只需要在 theme 中定义属性来指明颜色就可以了,这就是通过属性来操作的好处,当然前提是开发人员必须熟悉 Material Design Components 的颜色规范。
切换主题
目前为止我们已经知道了如何适配暗黑模式,在完成适配之后,我们还需要为用户提供在运行时,切换主题的选项。
通常你可以在设置中为用户提供切换的选项,切换的代码也很简单,通过 delegate.localNightMode 来设置当前的模式。
有一点需要注意的是,切换逻辑仅在运行时生效,当我们重新启动 App 的时候,会与当前系统设置的模式保持一致,所以当用户执行完切换逻辑后,我们需要对用户的行为进行保存,当下一次重新启动 App 的时候,以恢复用户之前的切换逻辑。
自定义切换逻辑
当我们设置 delegate.localNightMode 的时候,系统会自动重新创建 Activity,如果你不想要重新创建,可以在 manifest 中对应的 Activity 中指定 android:configChanges="uiMode",然后重写 onConfigurationChanged() 方法,自己来处理切换逻辑。
至此所有关于 Android App 的 Dark Mode 适配的内容就介绍到这里,关于更多 Dark Mode 的资料以及本文中 demo 的仓库,关注本公众号【Android|Kotlin】回复【暗黑】可以获取全部内容。
感谢你的观看,如果对你有用,请点个好看,分享转发这篇文章,这将会对我有很大的帮助,谢谢!