Android 适配Dark Theme(暗黑模式)

文章目录

  • 暗黑模式
    • 为什么我们需要暗黑模式
    • 如何适配暗黑模式
      • Force Dark自动适配
      • 自定义适配
    • 手动切换暗黑模式

暗黑模式

在 2019 年的 Google I/O上,谷歌新发布的android 10终于从系统层级支持暗黑模式,那么为什么我们需要暗黑模式?Android开发者应该如何让自己的app适配暗黑模式?接下来的文章将一一为你解答。

为什么我们需要暗黑模式

在Android 官方文档中,列举了暗黑模式的三个好处:

  1. 可以大大降低功耗(取决于设备的屏幕技术)。
  2. 提高了弱视用户和对强光敏感的用户的可见性。
  3. 使任何人在昏暗的环境中都更容易使用设备。

在OLED显示屏上,当一个像素是纯黑色(十六进制为#000000)的时候,该像素将会被关闭并且不消耗能量,这时如果显示屏显示的是大面积的黑色像素,将会大大降低显示屏消耗的电量。

如何适配暗黑模式

Force Dark自动适配

Android 10 提供 Force Dark 功能。此功能可让开发者快速实现深色主题背景,只需要在 style.xml 中的应用主题中添加这一行代码android:forceDarkAllowed=“true” ,就可以完成自动适配。

<resources>
    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
        <item name="android:forceDarkAllowed">true</item>
    </style>
</resources>

效果对比如下图所示。从结果来看,整体的界面风格好像确实变成了暗黑模式,但是菜单栏并未适配,所以这里我并不推荐你使用这种自动化的方式来实现深色主题,而是应该使用更加复杂一点的实现方式——自定义适配。

Android 适配Dark Theme(暗黑模式)_第1张图片图1 正常情况
Android 适配Dark Theme(暗黑模式)_第2张图片图2 Force Dark适配

自定义适配

  1. 将App 使用的主题从之前默认的 Light 主题修改为 DayNight
<resources>

    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.DayNight.DarkActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
    </style>
    
</resources>
  1. 右击res目录 -> New -> Directory,创建一个values-night目录,然后右击values-night目录 -> New -> Values resource file,创建一个colors-night.xml文件。接着在这个文件中指定深色主题下的颜色值,颜色的命名要与colors.xml相同。
--colors.xml
<resources>
    <color name="colorPrimary">#008577</color>
    <color name="colorPrimaryDark">#00574B</color>
    <color name="colorAccent">#D81B60</color>
    <color name="colorTextView">#000</color>
</resources>

--colors-night.xml
<resources>
    <color name="colorPrimary">#303030</color>
    <color name="colorPrimaryDark">#232323</color>
    <color name="colorAccent">#008577</color>
    <color name="colorTextView">#FFFFFF</color>
</resources>
  1. 为那些在切换为暗黑模式时,需要改变颜色的控件适配,这里我们以TextView为例。
   <TextView
       android:id="@+id/name"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:textColor="@color/colorTextView"
       android:padding="10dp"
       android:textSize="20sp"
       app:layout_constraintLeft_toLeftOf="parent"
       app:layout_constraintTop_toTopOf="parent" />

效果对比如下图。效果明显比自动适配好上不少,算是初步实现了对暗黑模式的适配。

Android 适配Dark Theme(暗黑模式)_第3张图片
Android 适配Dark Theme(暗黑模式)_第4张图片

手动切换暗黑模式

目前为止我们已经知道了如何适配暗黑模式,在完成适配之后,我们还需要为用户提供在运行时,切换主题的选项,切换的代码也很简单,在菜单监听中通过getDelegate().setLocalNightMode()来设置当前的模式。

    @Override
    public  boolean onOptionsItemSelected(MenuItem mi){

        if(mi.isCheckable()){
            mi.setChecked(true);
        }
        switch (mi.getItemId()){
            case R.id.mode_light:
                getDelegate().setLocalNightMode(AppCompatDelegate.MODE_NIGHT_NO);
                break;
            case R.id.mode_dark:
                getDelegate().setLocalNightMode(AppCompatDelegate.MODE_NIGHT_YES);
                break;
            case R.id.mode_system:
                getDelegate().setLocalNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM);
                break;
        }
        return true;
    }

setLocalNightMode()方法接收一个mode参数,用于控制当前应用程序的夜间模式。mode参数主要有以下值可供选择:

  1. MODE_NIGHT_FOLLOW_SYSTEM:默认模式,表示让当前应用程序跟随系统设置来决定使用浅色主题还是深色主题。
  2. MODE_NIGHT_YES:脱离系统设置,强制让当前应用程序使用深色主题。
  3. MODE_NIGHT_NO:脱离系统设置,强制让当前应用程序使用浅色主题
  4. MODE_NIGHT_AUTO_BATTERY:根据手机的电池状态来决定使用浅色主题还是深色主题,如果开启了节点模式,则使用深色主题。

需要注意的是,当调用setLocalNightMode()方法并成功切换主题时,应用程序中所有处于started状态的Activity都会被重新创建,那如果不想Activity重新创建Activity怎么切换主题呢?
这时候我们可以在AndroidManifest中将configChanges设置为uiMode,使当前的Activity避免被重新创建。

<application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:configChanges="uiMode"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

现在当应用程序的主题发生变化时,MainActivity并不会重新创建,而是会触发onConfigurationChanged()方法的回调,你可以在回调当中手动做一些逻辑处理。

override fun onConfigurationChanged(newConfig: Configuration) {
    val currentNightMode = newConfig.uiMode and Configuration.UI_MODE_NIGHT_MASK
    when (currentNightMode) {
        Configuration.UI_MODE_NIGHT_NO -> {} // 夜间模式未启用,使用浅色主题
        Configuration.UI_MODE_NIGHT_YES -> {} // 夜间模式启用,使用深色主题
    }
}

还有一点需要注意的是,切换逻辑仅在运行时生效,当我们重新启动 App 的时候,会与当前系统设置的模式保持一致。

你可能感兴趣的:(android)