暗黑模式开发一些心得
- 一、背景介绍
- 1.1 深色主题背景
- 二、动态开启暗黑模式
- 2.1 color颜色值
- 2.2 图标配置
- 2.3 设置开启开关
- 2.4 一些特殊场景的配置
- 2.4.1 json动态效果图
- 2.4.2 H5加载
- 三、状态栏和导航栏的适配
- 3.1 状态栏
- 3.2 导航栏
- 四、总结
一、背景介绍
1.1 深色主题背景
Android 10 (API 级别 29) 及更高版本中提供深色主题背景。深色主题背景具有诸多优势:
- 可大幅减少耗电量(具体取决于设备的屏幕技术)。
- 为弱视以及对强光敏感的用户提高可视性。
- 让所有人都可以在光线较暗的环境中更轻松地使用设备。
深色主题背景同时适用于 Android 系统界面和在设备上运行的应用。
在 Android 10 (API 级别 29) 及更高版本中,您可以通过以下三种方法启用深色主题背景:
- 使用系统设置(Settings -> Display -> Theme)启用深色主题背景。
- 使用“快捷设置”图块,从通知托盘中切换主题背景(启用后)。
- 在 Pixel 设备上,选择“省电模式”将同时启用深色主题背景。其他原始设备制造商 (OEM) 不一定支持这种行为。
二、动态开启暗黑模式
2.1 color颜色值
针对res/values/目录下的color.xml文件,需要重新创建一个res/values-night目录文件夹,并在该目录下创建一个名称为color.xml的文件,用于存放暗黑模式下是所有颜色值。同时res/values-night/color.xml中定义的颜色值名称必须要与res/values/color.xml中的定义的颜色值名称一致且都能找到(定义的名称可以少于,但不能多余),否则会报错。
如果浅色模式和暗色模式下颜色值一样,可以只在res/values/color.xml中定义颜色值,也可以在res/values/color.xml和res/values-night/color.xml两个文件中定义相同的名称,且颜色值相同。
比如:res/values/color.xml文件中的颜色值设置如下:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--有颜色背景的按钮 字体颜色固定为白色-->
<color name="white">#FFFFFF</color>
<!--背景色底色-->
<color name="backgroundColor">#F5F5F5</color>
<!--toast背景色-->
<color name="toast_bg">#000000</color>
<!--白色背景-->
<color name="bg_white">#FAFBFB</color>
<!--字数提示背景色-->
<color name="ll_guide_audit">#F5F5F5</color>
<!--背景设置边框颜色值-->
<color name="border_line">#D6D6D6</color>
<color name="black">#000000</color>
<!--按钮点击效果颜色值-->
<color name="item_selector">#EBEBEB</color>
<!-- 分割线 -->
<color name="line_gray">#EBEBEB</color>
… …
</resources>
对应的res/values-night/color.xml文件中的颜色值设置如下:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--有颜色背景的按钮 字体颜色固定为白色-->
<color name="white">#FFFFFF</color>
<!--背景色底色 gray_2_b-->
<color name="backgroundColor">#0F0F0F</color>
<!--toast背景色-->
<color name="toast_bg">#666666</color>
<!--白色背景-->
<color name="bg_white">#FAFBFB</color>
<!--字数提示背景色-->
<color name="ll_guide_audit">#F5F5F5</color>
<!--背景设置边框颜色值-->
<color name="border_line">#D6D6D6</color>
<color name="black">#000000</color>
<!--按钮点击效果颜色值-->
<color name="item_selector">#2E2E2E</color>
<!-- 分割线 -->
<color name="line_gray">#1FFFFFFF</color>
… …
</resources>
特别注意:
2.2 图标配置
- 对于纯色图标,可以使用矢量图,并且矢量图中的颜色采用颜色名设置,不要写死成颜色值。这样只需要在drawable目录下存放该矢量图,不需要重新定义一个drawable-night目录存放暗黑模式下对应的图标,这样操作可以减少app体积。
- 对于含有多种颜色值的图标,可以采用webp格式,这样可以减少图片的占用空间;同时为了适配功能,需要对应的创建不同尺寸大小的文件目录用于存放暗黑模式下的图标。对应关系如下:
浅色模式 | 暗黑模式 | 对应图标尺寸大小 |
drawable | drawabe-night | 1x |
drawabe-mdpi | drawabe-night-mdpi | 1x |
drawabe-hdpi | drawabe-night-hdpi | 1.5x |
drawabe-xhdpi | drawabe-night-xhdpi | 2x |
drawabe-xxhdpi | drawabe-night-xxhdpi | 3x |
2.3 设置开启开关
应用在 Android 10 (API 级别 29) 及更高版本上运行时,推荐的选项有所不同,目的是允许用户替换系统默认设置:
- 浅色
- 深色
-系统默认(推荐的默认选项)
每个选项直接映射到以下某个 AppCompat.DayNight 模式:
- 浅色 - MODE_NIGHT_NO
- 深色 - MODE_NIGHT_YES
- 由省电模式设置 - MODE_NIGHT_AUTO_BATTERY
- 系统默认 - MODE_NIGHT_FOLLOW_SYSTEM
如要切换主题背景,请调用 AppCompatDelegate.setDefaultNightMode()。
特别注意:
查看当前手机设置的显示模式:
((UiModeManager) mContext.getSystemService(Context.UI_MODE_SERVICE)).getNightMode()
获取的值有三种模式:
- MODE_NIGHT_YES – 暗色
- MODE_NIGHT_NO – 亮色
- MODE_NIGHT_AUTO – 自动调节亮度
进行不同模式的切换,需要将已经注册的所有activity执行一次recreate操作,即重新走一次生命周期,可以通过Application.registerActivityLifecycleCallbacks()接口方法获取当前所有注册activity。
2.4 一些特殊场景的配置
2.4.1 json动态效果图
对于代码中需要加载一些json动态效果图,最好是弄两套json文件(亮色和暗色),根据不同场景加载不同的json文件即可。
2.4.2 H5加载
对于代码中需要加载H5文件,可以针对不同的场景设置不同的颜色值,比如如下创建一个AlertDialog对话框:
if ((uiModeManager.getNightMode() == UiModeManager.MODE_NIGHT_YES &&
AppCompatDelegate.getDefaultNightMode() == AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM) ||
AppCompatDelegate.getDefaultNightMode() == AppCompatDelegate.MODE_NIGHT_YES) {
new AlertDialog.Builder(activity, R.style.MyDialog3)
.setTitle(Html.fromHtml("<font color='#E0E0E0'>开启存储权限</font>"))
.setCancelable(false)
.setMessage(Html.fromHtml("<font color='#A3A3A3'>需要授权存储空间权限,以将图片视频等文件保存到手机</font>"))
.setPositiveButton(Html.fromHtml("<b><font color='#4596F8'>知道了</font></b>"), (dialog1, which1) -> {
RxPermissions rxPermissions = new RxPermissions(activity);
rxPermissions.request(
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_EXTERNAL_STORAGE)
.subscribe(granted -> {
if (!granted) {
PermissionWaringUtil
.showWaringDialog(activity, "请前往设置打开存储权限,以将图片视频等文件保存到手机。");
}
});
}).show();
} else {
new AlertDialog.Builder(activity, R.style.MyDialog3)
.setTitle(Html.fromHtml("<font color='#292929'>开启存储权限</font>"))
.setCancelable(false)
.setMessage(Html.fromHtml("<font color='#7A7A7A'>需要授权存储空间权限,以将图片视频等文件保存到手机</font>"))
.setPositiveButton(Html.fromHtml("<b><font color='#0067E5'>知道了</font></b>"), (dialog1, which1) -> {
RxPermissions rxPermissions = new RxPermissions(activity);
rxPermissions.request(
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_EXTERNAL_STORAGE)
.subscribe(granted -> {
if (!granted) {
PermissionWaringUtil
.showWaringDialog(activity, "请前往设置打开存储权限,以将图片视频等文件保存到手机。");
}
});
}).show();
}
三、状态栏和导航栏的适配
3.1 状态栏
一般情况下:
- 浅色模式下状态栏设置为StatusBarUtil.setLightMode(this);
- 暗色模式下状态栏设置为StatusBarUtil.setDarkMode(this);
3.2 导航栏
可以通过getWindow().setNavigationBarColor()这个接口设置为需要的导航栏颜色值。
四、总结
在进行暗黑模式开发过程中,最主要的就是进行color颜色值的设置和图标的设置,并添加对应暗黑模式的文件夹及文件,至于style样式不一定要设置,对于Force Dark功能在开发中设置没有生效,需要进一步学习研究。