实现深色主题

试用 Compose 方式
Jetpack Compose 是推荐用于 Android 的界面工具包。了解如何在 Compose 中使用主题设置。

图 1. 深色主题。

深色主题适用于 Android 10(API 级别 29)及更高版本。它具有以下优势:

  • 大幅减少耗电量,具体取决于设备的屏幕技术。
  • 为弱视以及对强光敏感的用户提高可视性。
  • 让您可以在光线较暗的环境中更轻松地使用设备。

深色主题适用于 Android 系统界面和在设备上运行的应用。

在 Android 10 及更高版本中,可通过以下三种方式启用深色主题:

  • 如需启用深色主题,请依次前往设置 > 显示 > 主题,使用系统设置。
  • 使用“快捷设置”功能块从通知栏中切换主题(如果已启用)。
  • 在 Pixel 设备上,启用省电模式以同时启用深色主题。其他设备可能不支持此行为。

有关使用 WebView 组件对基于网络的内容应用深色主题的说明,请参阅使用 WebView 调暗网页内容的颜色

在应用中支持深色主题

如需支持深色主题,请将应用的主题(通常可在 res/values/styles.xml 中找到)设置为继承 DayNight 主题:

<style name="AppTheme" parent="Theme.AppCompat.DayNight">

您还可以使用 Material 组件深色主题

<style name="AppTheme" parent="Theme.MaterialComponents.DayNight">

这会将应用的主要主题与系统控制的夜间模式标志相关联,并在应用启用后为其提供默认的深色主题。

主题和样式

避免使用适合在浅色主题下使用的硬编码颜色或图标。请改用主题属性或适合夜间使用的资源。

对于深色主题,两个主题属性最为重要:

  • ?android:attr/textColorPrimary:通用文本颜色。它在浅色主题下接近于黑色,在深色主题下接近于白色。它包含一个停用状态。
  • ?attr/colorControlNormal:通用图标颜色。它包含一个停用状态。

我们建议您使用 Material Design 组件,因为通过它的颜色主题系统(例如主题属性 ?attr/colorSurface?attr/colorOnSurface)可以轻松获取合适的颜色。您可以在主题中自定义这些属性。

更改应用内主题

您可以允许用户在应用运行时更改应用的主题。以下是推荐的选项:

  • 浅色
  • 深色
  • 系统默认设置(推荐的默认选项)

这些选项直接映射到 AppCompat.DayNight 模式:

如需切换主题,请执行以下操作:

Force Dark

Android 10 提供了 Force Dark 功能,此功能可让开发者快速实现深色主题,而无需明确设置 DayNight 主题。

Force Dark 会分析浅色主题应用的每个视图,并在相应视图在屏幕上显示之前,自动应用深色主题。您可以混合使用 Force Dark 和本机实现,以缩短实现深色主题所需的时间。

应用必须通过在 activity 的主题中设置 android:forceDarkAllowed="true" 来选择启用 Force Dark。此属性会在系统和 AndroidX 提供的所有浅色主题(例如 Theme.Material.Light)上设置。使用 Force Dark 时,请全面测试应用,并根据需要排除视图。

如果您的应用使用深色主题(例如 Theme.Material),则系统不会应用 Force Dark。同样,如果应用的主题继承自 DayNight 主题,则系统不会应用 Force Dark,因为会自动切换主题。

在视图上停用 Force Dark

您可以通过 android:forceDarkAllowed 布局属性或 setForceDarkAllowed() 在特定视图上控制 Force Dark。

Web 内容

如需了解如何在基于网络的内容中使用深色主题,请参阅使用 WebView 调暗网页内容的颜色。如需查看应用于 WebView 的深色主题示例,请参阅 GitHub 上的 WebView 演示

最佳实践

下面几个部分介绍了实现深色主题的最佳做法。

通知和微件

对于在设备上显示但您不直接控制的界面,请确保您使用的所有视图都反映托管应用的主题。两个示例是通知和启动器 widget。

通知

使用系统提供的通知模板,例如 MessagingStyle。这意味着,系统负责应用正确的视图样式。

微件和自定义通知视图

对于启动器 widget,或者如果您的应用使用自定义通知内容视图,请针对浅色主题和深色主题测试内容。

需要注意的常见误区包括:

  • 假设背景颜色始终为浅色。
  • 对文本颜色进行硬编码。
  • 在使用默认文本颜色时设置硬编码背景颜色。
  • 使用静态颜色的可绘制对象图标。

在所有这些情况下,请使用适当的主题属性,而不是硬编码颜色。

启动屏幕

如果您的应用具有自定义启动屏幕,您可能需要修改该屏幕,使其反映所选的主题。

移除所有硬编码颜色,例如以编程方式设置为白色的背景颜色。请改用 ?android:attr/colorBackground 主题属性。

配置变更

当应用的主题通过系统设置或 AppCompat 发生更改时,会触发 uiMode 配置更改。这意味着,系统会自动重新创建 activity。

在某些情况下,您可能希望应用来处理配置变更。例如,您可能希望延迟配置变更时间,因为正在播放视频。

应用可以通过声明每个 Activity 都可以处理 uiMode 配置变更来处理深色主题的实现:

<activity
    android:name=".MyActivity"
    android:configChanges="uiMode" />

Activity 声明它会处理配置更改时,当出现主题更改时,系统会调用其 onConfigurationChanged() 方法。

若要检查当前采用的是哪种主题,应用可以运行如下代码:

Kotlin

val currentNightMode = configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
when (currentNightMode) {
    Configuration.UI_MODE_NIGHT_NO -> {} // Night mode is not active, we're using the light theme.
    Configuration.UI_MODE_NIGHT_YES -> {} // Night mode is active, we're using dark theme.
}

Java

int currentNightMode = configuration.uiMode & Configuration.UI_MODE_NIGHT_MASK;
switch (currentNightMode) {
    case Configuration.UI_MODE_NIGHT_NO:
        // Night mode is not active, we're using the light theme
        break;
    case Configuration.UI_MODE_NIGHT_YES:
        // Night mode is active, we're using dark theme
        break;
}