Android 深色模式的项目应用

前言

早在四年前就准备做深色模式的,当时用的三方的SDK,但是SDK上还有bug,不能适配RecyclerView,用上后会很卡,然后就一直放着了,有些用户一直催着要深色模式:

然后这段期间给整上,本以为现在深色模式应用的挺广泛的,在项目中实践了一下还是躺了很多坑,梳理一下实践过程及遇到的问题。

所有代码实践在云阅里可以看到:

  • 下载App体验,酷安:云阅
  • 直接查看源码,GitHub:CloudReader

项目实践

1.选定原生Api实现

Android官方深色主题背景开发文档(需科学上网)。

原生Api简单稳定但是就是要重启App,不过看掘金以及微信都是这样实现的。

于是参考了微信和掘金的操作,总有三种状态,跟随系统,普通模式,深色模式。

2.关键的工具类

public class NightModeUtil {

    /**
     * 当前系统是否是深色模式
     */
    public static boolean isNightMode(Context context) {
        int uiMode = context.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
        return uiMode == Configuration.UI_MODE_NIGHT_YES;
    }

    /**
     * 获取是否跟随系统,默认true
     */
    public static boolean getSystemMode() {
        return SPUtils.getBoolean(Constants.KEY_MODE_SYSTEM, true);
    }

    public static void setSystemMode(boolean nightMode) {
        SPUtils.putBoolean(Constants.KEY_MODE_SYSTEM, nightMode);
    }

    /**
     * 获取是否设置深色模式,默认false
     */
    public static boolean getNightMode() {
        return SPUtils.getBoolean(Constants.KEY_MODE_NIGHT, false);
    }

    public static void setNightMode(boolean nightMode) {
        SPUtils.putBoolean(Constants.KEY_MODE_NIGHT, nightMode);
    }

    public static void initNightMode() {
        initNightMode(getSystemMode(), getNightMode());
    }

    /**
     * 初始化App深色模式
     *
     * @param systemMode 是否是跟随系统
     * @param nightMode  是否是深色模式
     */
    public static void initNightMode(boolean systemMode, boolean nightMode) {
        if (systemMode) {
            AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM);
        } else {
            if (nightMode) {
                AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
            } else {
                AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
            }
        }
    }

    /**
     * 重启App
     */
    public static void restartApp(Activity activity) {
        final Intent intent = App.getInstance().getPackageManager().getLaunchIntentForPackage(App.getInstance().getPackageName());
        if (intent != null) {
            intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
            intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
            activity.startActivity(intent);
            android.os.Process.killProcess(android.os.Process.myPid());
        }
    }
}

3.在Application里初始化

NightModeUtil.initNightMode();

4.切换状态后重启App

NightModeUtil.initNightMode(dayNightSwitch.isChecked, ctvCheckNight.isChecked)
NightModeUtil.restartApp(activity)

其中还要保存是否跟随系统或指定深色模式的状态,具体逻辑细节可见:NavNightModeActivity.kt

5.Application下的主题设置


同时还要注意如果单个Activity有自己的主题,也需要设置parent主题为Theme.AppCompat.DayNight.NoActionBar

ToolBar也有自己的主题:


其中

  • theme为: