前言

早在四年前就准备做深色模式的,当时用的三方的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下的主题设置

<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也有自己的主题:

<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里加上如下代码:

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).启动页我们经常会放品牌图,页面的深色模式可以通过 改变普通/深色模式文件夹下的图来实现。 会存在的问题:当系统是深色模式,应用里选择的是普通模式时,开屏图的背景是深色的。因为这时候还没有进入App,还没有初始化去设置模式。

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

<activityandroid:name=".NavNightModeActivity"android:configChanges="uiMode" />

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

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 themeConfiguration.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的主题:

// 设置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包的时候就提示了这个问题。

7).ViewModel里的page=1,不会被初始化,例如滑动到第二页后切换深色模式,page还是等于2,需要在onCreate里初始化page=1。

8).使用Glide时,不能直接获取颜色作为placeholder使用,需要使用ContextCompat.getDrawable(context, resId);

Glide.with(imageView.getContext()).load(url).transition(DrawableTransitionOptions.withCrossFade(500)).placeholder(ContextCompat.getDrawable(imageView.getContext(), getDefaultPic(type))).error(ContextCompat.getDrawable(imageView.getContext(), getDefaultPic(type))).into(imageView);

总结

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

Android 深色模式的项目应用相关推荐

  1. android自动切换暗色,超实用!Android 深色模式适配(可定时开启的APP内主题切换管理工具)...

    前言 前面分享了一篇"黑白化主题"的文,主要适用场景是不久就要到来的"清明"等时节或者是其他的国家公祭日什么的(一名成熟的程序员,要学会自己提产品需求). 今天 ...

  2. Android深色模式适配原理分析,android应用开发

    return when (resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) { Configuration.UI ...

  3. Android深色模式下,看不见字的解决办法

    你去看看,看不见字的那些是不是都没有设置字体颜色! 设置一下字体颜色就好啦! 对于AlertDialog,千万不要用它自己的,要我们自己写好view,然后设置view,不然的话,深色模式下,你是很难修 ...

  4. H5项目适配系统深色模式方案总结

    文章目录 一.背景 二.问题 三.H5项目适配深色模式方案 1.声明 color-scheme 1.1meta 1.2CSS 2.通过 CSS 媒体查询 3.图片适配 4. JavaScript中判断 ...

  5. H5项目适配系统深色模式方案

    一.背景 随着 iOS 13 的发布,深色模式(Dark Mode)越来越多地出现在大众的视野中,支持深色模式已经成为现代移动应用和网站的一个潮流,前段时间更是因为微信的适配再度引起热议.深色模式不仅 ...

  6. android开发适配深色模式,手机不支持深色模式,如何用软件解决深色模式的问题?(附有系统全局深色模式实现方法...

    本帖最后由 巷子口的你 于 2020-8-8 07:57 编辑 1.92允许通过设置为助手应用来饮捷切频深色模式(设置入口一般为系统默认应用-助手和语音输人, MIU需要设置为语音助手)提醒:稳定模式 ...

  7. android自动切换暗色,Android 适配深色模式的总结

    Android Q 推出了深色模式,其实 Android 9 就有了,部分厂商小米,三星就在系统 Android 9 加入了深色模式的开关. Android 提供了一套夜间模式主题,继承 Theme. ...

  8. android 9.0谷歌商店,Android 10现可选择Play商店主题模式 附Android 9强制开启深色模式教程...

    不可否认的是自打苹果于去年推出配备深色模式的 iOS 13以来,整个移动互联网都开始流行起为应用配备深色模式设计的风气,不管用户个人到底喜不喜欢用深色模式,深色模式都已经给出了足够具有说服力的理由来推 ...

  9. line android 英文版,LINE 手機版也有深色模式啦!更新完我要去哪裡設定呢?Android/iOS 都有! (1/8 更新)...

    發現大家都喜歡深色/暗黑模式,越來越多 App 的介面也開始提供黑色主題,現在 LINE 也有自己的深色模式啦-iPhone 與 Android 用戶將 LINE 手機版更新至最新版本後,就可以獲得全 ...

  10. 小米9android q测试版,小米9 Android Q Beta优先体验版已推送:新增深色模式

    原标题:小米9 Android Q Beta优先体验版已推送:新增深色模式 7月12日消息,小米MIUI官方微博称,小米9的MIUI Android Q Beta优先体验版现已推送!已获得测试资格的小 ...

最新文章

  1. 03-老马jQuery教程-DOM操作(上)
  2. 关于mybatis里面的Executor--转载
  3. 如何将.sof转换成.jic
  4. 多媒体技术 PI 第一期:OSS 圆桌预告
  5. python中print的用法_Python中print函数简单使用总结
  6. 【转】各种树:trie树、B树、B-树、B+树、B*树
  7. Windows编译环境搭建(VS2010)
  8. Python模块之uuid
  9. aes js 加盐值 解密_cryptoJS AES 加解密简单使用
  10. 一行代码迁移TensorFlow 1.x到TensorFlow 2.0
  11. Linux gcc编译时强制链接并依赖一个库(即使未使用)
  12. pc 浏览器最小字体12px
  13. 基于三轴加速度传感器的老人摔倒检测
  14. MVC下压缩输入的HTML内容
  15. Maya多版本下载与激活方法
  16. Arduino使用u8g2库函数驱动4线/6线OLED屏幕(I2C/SPI通讯)附带库函数详解
  17. APP - 查询名下微信实名账户(可注销微信封号账户支付功能)
  18. android 仿微信加载框,Android自定义控件——仿微信半透明加载框
  19. Https 网站 访问 Http资源
  20. Benchmark测试——IOzone

热门文章

  1. 脉冲触发器和边沿触发器的区别
  2. 智能优化算法学习总结
  3. 手游pvp系统网络设计
  4. Android启动模式分析
  5. 互联网这股裁员风,要吹到啥时候?
  6. 【服务器】服务器安全防护、防止服务器攻击和保护措施
  7. 美团外卖订单中心的演进
  8. 使用Scrapy框架爬取网页并保存到Mysql
  9. activiti会签以及动态设置办理人员
  10. Codeforces Round #595 (Div. 3) E. By Elevator or Stairs?