Android Q暗黑模式适配

915 阅读3分钟

暗黑模式的适配大概可以分为三部分:

  1. 背景
  2. 文字
  3. 图标 适配的方式也分为三种:自动适配、自定义适配、换肤框架适配

1. 自动适配 Force Dark

Android 10 提供 Force Dark 功能。此功能可让开发者快速实现深色主题背景,只需要在 style.xml 中的应用主题中添加这一行代码

<style name="AppTheme" parent="Theme.MaterialComponents.Light.NoActionBar">
    <item name="android:forceDarkAllowed" tools:ignore="NewApi">true</item>
</style>

2. 自定义适配

自定义适配的关键在于,避免一切硬编码的颜色值和图片资源加载,建立 light、night两份colors,通过定义相同的名字,在不同模式下系统会自动加载对应文件夹下定义的颜色

主题定义:在res/values/style.xml文件中定义明亮模式的主题和暗色模式的主题

明亮模式

<style name="Theme.MyApplication" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
    <!-- Primary brand color. -->
    <item name="colorPrimary">@color/purple_500</item>
    <item name="colorPrimaryVariant">@color/purple_700</item>
    <item name="colorOnPrimary">@color/white</item>
    <!-- Secondary brand color. -->
    <item name="colorSecondary">@color/teal_200</item>
    <item name="colorSecondaryVariant">@color/teal_700</item>
    <item name="colorOnSecondary">@color/black</item>
    <!-- Status bar color. -->
    <item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
    <!-- Customize your theme here. -->
</style>
//res/values/colors.xml定义上述颜色值

暗色模式 在 res/values-night/style.xml 文件中定义明亮模式的主题和暗色模式的主题

<style name="Theme.MyApplication" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
    <!-- Primary brand color. -->
    <item name="colorPrimary">@color/purple_200</item>
    <item name="colorPrimaryVariant">@color/purple_700</item>
    <item name="colorOnPrimary">@color/black</item>
    <!-- Secondary brand color. -->
    <item name="colorSecondary">@color/teal_200</item>
    <item name="colorSecondaryVariant">@color/teal_200</item>
    <item name="colorOnSecondary">@color/black</item>
    <!-- Status bar color. -->
    <item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
    <!-- Customize your theme here. -->
</style>
//res/values-night/colors.xml定义上述颜色值

注意:主题一定要继承自Theme.AppCompat.DayNight.DarkActionBar 或者 Theme.AppCompat.DayNight,不同模式下的属性要保持一致,属性值根据不同主题单独设置即可。

资源引用:在布局中利用 ?attr/main_text_color 来引用你所定义的颜色值

<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Hello World!"
    android:background="?attr/main_text_color" />

3. 主动切换主题

  1. 确保需要适配夜间模式的Activity继承于AppCompatActivity;
  2. values-night目录下创建color.xml等文件定义夜间模式的颜色等,同理其他资源目录也一样;
  3. 调用AppCompatDelegate.setDefaultNightMode方法设置是否使用夜间模式,参数AppCompatDelegate.MODE_NIGHT_YES打开夜间模式,AppCompatDelegate.MODE_NIGHT_NO关闭夜间模式。
  4. AndroidManifest修改:在activity的清单文件中添加以下属性android:configChanges="uiMode"
<activity android:name=".MainActivity"
          android:configChanges="uiMode" />
  1. 在activity中设置主题
fun onCreate(savedInstanceState: Bundle?) {
    // 在setContentView之前设置主题样式
    setTheme(if(isLightMode()) R.style.theme_light else R.style.theme_dark)
    setContentView(R.layout.activity_main)

注意:setTheme() 一定要在setContentView()之前调用,否则主题设置无法生效 6. 检查当前系统是否已开启暗黑模式

fun isLightMode(context: Context): Boolean {
    val mode = context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
    return mode == Configuration.UI_MODE_NIGHT_YES
}
  1. 监听系统暗黑主题开启与关闭在activity中重写onConfigurationChanged()方法
override fun onConfigurationChanged(newConfig: Configuration) {
    super.onConfigurationChanged(newConfig)
    when(newConfig.uiMode and Configuration.UI_MODE_NIGHT_MASK) {
        Configuration.UI_MODE_NIGHT_YES -> {
            // 暗黑模式已开启
            recreate()
        }
        Configuration.UI_MODE_NIGHT_NO -> {
            // 暗黑模式已关闭
            recreate()
        }
    }
}

4. 换肤方式适配

这种方式主要是获取和监听系统深色主题模式的开启状态来动态设置主题或者皮肤。

  1. 首先我们需要定义好深色主题或者皮肤,换肤功能通过Android-skin-support三方库实现。
  2. Activity onCreate方法中setContentView之前判断是否开启暗黑模式,设置暗黑主题
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    int mode = this.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
    if (mode == Configuration.UI_MODE_NIGHT_YES && !"night".equals(SkinPreference.getInstance().getSkinName())) {
        // 加载暗黑主题
        SkinCompatManager.getInstance().loadSkin("night", null, SkinCompatManager.SKIN_LOADER_STRATEGY_BUILD_IN);
    } else if (mode == Configuration.UI_MODE_NIGHT_NO && "night".equals(SkinPreference.getInstance().getSkinName())) {
        // 使用默认的theme主题,资源
        SkinCompatManager.getInstance().restoreDefaultTheme();
    }
    setContentView(R.layout.activity_main);

到这里就完成了适配Android Q的深色主题模式的换肤功能。如果Activity配置 android:configChanges="uiMode"也可通过下面方式实现换肤onConfigurationChanged中监听暗黑模式状态,此处采用recreate方法重建Activity来换肤。

override fun onConfigurationChanged(newConfig: Configuration) {
    super.onConfigurationChanged(newConfig)
    when(newConfig.uiMode and Configuration.UI_MODE_NIGHT_MASK) {
        Configuration.UI_MODE_NIGHT_YES -> {
            // 暗黑模式已开启
            SkinCompatManager.getInstance().loadSkin("night", null, SkinCompatManager.SKIN_LOADER_STRATEGY_BUILD_IN);
        }
        Configuration.UI_MODE_NIGHT_NO -> {
            // 暗黑模式已关闭
            SkinCompatManager.getInstance().restoreDefaultTheme();
        }
    }
}