前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android 深色模式的项目应用

Android 深色模式的项目应用

原创
作者头像
Jingbin
发布2021-11-29 09:46:53
1.3K5
发布2021-11-29 09:46:53
举报
文章被收录于专栏:Android 技术栈Android 技术栈

前言

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

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

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

项目实践

1.选定原生Api实现

Android官方深色主题背景开发文档(需访问国外网站)。

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

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

2.关键的工具类

代码语言:txt
复制
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里初始化

代码语言:txt
复制
NightModeUtil.initNightMode();

4.切换状态后重启App

代码语言:txt
复制
NightModeUtil.initNightMode(dayNightSwitch.isChecked, ctvCheckNight.isChecked)
NightModeUtil.restartApp(activity)

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

5.Application下的主题设置

代码语言:txt
复制
<style name="AppTheme" parent="Theme.AppCompat.DayNight.NoActionBar">

    <!--选中状态icon的颜色和字体颜色-->
    <item name="colorPrimary">@color/colorTheme</item>
    <item name="colorPrimaryDark">@color/colorTheme</item>
    <item name="colorAccent">@color/colorTheme</item>
    <item name="android:windowAnimationStyle">@style/default_animation</item>
    <item name="android:listDivider">@drawable/shape_line</item>
    <!--默认状态下页面的背景色-->
    <item name="android:windowBackground">@color/color_page_bg</item>
</style>

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

ToolBar也有自己的主题:

代码语言:txt
复制
<androidx.appcompat.widget.Toolbar xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/tool_bar"
    android:layout_width="match_parent"
    android:layout_height="?attr/actionBarSize"
    android:background="@color/colorToolBar"
    app:contentInsetStart="0.0dp"
    app:contentInsetStartWithNavigation="0dp"
    app:layout_scrollFlags="enterAlways|scroll"
    app:popupTheme="@style/ThemeOverlay.AppCompat.ActionBar"
    app:subtitleTextAppearance="@style/Toolbar.SubTitle"
    app:theme="@style/ToolbarStyle"
    app:titleMarginStart="0dp"
    app:titleTextAppearance="@style/ToolBar.Title"
    tools:layout_height="50dp"
    tools:title="云阅" />

其中

  • theme为:<style name="ToolbarStyle" parent="@style/ThemeOverlay.AppCompat.ActionBar"/>
  • popupTheme也不能设置单个的LightDark主题,不然切换深色模式的时候也不会改变效果。

6.WebView的深色模式设置

引入implementation 'androidx.webkit:webkit:1.2.0'后可轻易的实现WebView的深色模式,不过有兼容问题,这和WebView的版本有关,WebView版本独立于Android版本。(亲测在系统6.0和7.1上无效。)

在有WebView的Activity的onCarete里加上如下代码:

代码语言:txt
复制
WebSettings webSetting = webView.getSettings();
// 检查是否支持暗模式
if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) {
    boolean isAppDarkMode;
    if (NightModeUtil.getSystemMode()) {
        // 是否是跟随系统
        isAppDarkMode = NightModeUtil.isNightMode(this);
    } else {
        isAppDarkMode = NightModeUtil.getNightMode();
    }
    if (isAppDarkMode) {
        WebSettingsCompat.setForceDark(webSetting, WebSettingsCompat.FORCE_DARK_ON);
    } else {
        WebSettingsCompat.setForceDark(webSetting, WebSettingsCompat.FORCE_DARK_OFF);
    }
}

7.配置项

1).接下来就是一些配色和部分深色模式下的图片处理问题。

  • 颜色:新建values-night文件夹,里面是深色模式下的colors.xml文件
  • 图片:新建drawable-night-xxhdpi图片文件夹

2).启动页我们经常会放品牌图,页面的深色模式可以通过 改变普通/深色模式文件夹下的图来实现

3).也可以自己处理配置变更,不重建Activity:

代码语言:txt
复制
<activity
    android:name=".NavNightModeActivity"
    android:configChanges="uiMode" />

当某个 Activity 声明它会处理配置变更时,系统会在出现主题背景变更时调用该 Activity 的 onConfigurationChanged()方法。

代码语言:txt
复制
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
}

遇到的问题

1).获取颜色ContextCompat.getColor(context, resId),需要加Activity的context,如果是Application的context会变不了色,这个和切换语言是一样的,获取String也不能用全局的context。

2).之前使用了关闭应用时杀掉进程的代码,导致不能重建Activity,找了好长时间问题。杀掉进程代码:android.os.Process.killProcess(android.os.Process.myPid());

3).如果代码需要单独动态设置ToolBar的主题:

代码语言:javascript
复制
// 设置toolbar的dark模式,为了使"完成"文字颜色显示白色
supportActionBar?.themedContext?.setTheme(R.style.ToolBarDarkActionBar)

4).可以使用系统自己的颜色值:

  • ?android:attr/textColorPrimary 这是一种通用型文本颜色。它在浅色主题背景下接近于黑色,在深色主题背景下接近于白色。
  • ?android:attr/textColorSecondary可作为第二文本颜色,相对于上面的颜色较浅。

5).在dialog打开后,再切换系统的深色模式,这时使用系统的颜色会不生效,需要使用自己的color文件里的颜色。具体出现在首次打开应用时,弹出的隐私弹框。

6).需要使用png后缀的图,最好别直接将jpg改为png,可以打开图片后将图片另存为png格式。我做时候debug模式下没问题,打release包的时候就提示了这个问题。

总结

使用官方给出的深色模式Api,实现起来比较简单,但是也有一些注意项和优化点,如有需要可自取代码 GitHub:CloudReader,如有其他问题,欢迎留言骚扰~

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 项目实践
    • 1.选定原生Api实现
      • 2.关键的工具类
        • 3.在Application里初始化
          • 4.切换状态后重启App
            • 5.Application下的主题设置
              • 6.WebView的深色模式设置
                • 7.配置项
                • 遇到的问题
                • 总结
                相关产品与服务
                图片处理
                图片处理(Image Processing,IP)是由腾讯云数据万象提供的丰富的图片处理服务,广泛应用于腾讯内部各产品。支持对腾讯云对象存储 COS 或第三方源的图片进行处理,提供基础处理能力(图片裁剪、转格式、缩放、打水印等)、图片瘦身能力(Guetzli 压缩、AVIF 转码压缩)、盲水印版权保护能力,同时支持先进的图像 AI 功能(图像增强、图像标签、图像评分、图像修复、商品抠图等),满足多种业务场景下的图片处理需求。
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档