小米手机深色模式适配说明
1.深色模式背景与介绍
1.1.安卓原生深色模式介绍
Android 10 (API 级别 29) 及更高版本中提供深色主题背景。深色主题背景具有诸多优势:
- 可大幅减少耗电量(具体取决于设备的屏幕技术)。
- 为弱视以及对强光敏感的用户提高可视性。
- 让所有人都可以在光线较暗的环境中更轻松地使用设备。
深色主题背景同时适用于 Android 系统界面和在设备上运行的应用。在 Android 10 (API 级别 29) 及更高版本中,您可以通过以下三种方法启用深色主题背景:
- 使用系统设置(Settings -> Display -> Theme)启用深色主题背景。
- 使用“快捷设置”图块,从通知托盘中切换主题背景(启用后)。
- 在 Pixel 设备上,选择“省电模式”将同时启用深色主题背景。其他原始设备制造商 (OEM) 不一定支持这种行为。
可参考:https://developer.android.com/guide/topics/ui/look-and-feel/darktheme#top_of_page
1.2.MIUI深色模式介绍
深色模式是一种将屏幕主色调转为深色的模式。如下图MIUI10界面为例,上图是正常浅色背景,下图为MIUI深色模式。
用户在MIUI中有两个方式可以打开深色模式:1.设置-显示-深色模式 2.下拉控制中心 中可以开启,开启后将全局变黑如上图显示:
对于适配深色模式的应用来说,我们会优先启用应用的深色模式。未接入的应用,则会通过算法进行反色。适配方式将在第三部分详细描述。如果用户不希望某个应用被反色,用户可以手动关闭该应用的反色功能,见下方:
目前已有众多知名头部三方应用适配了深色模式,例如微信、QQ、爱奇艺、优酷、知乎、小红书、钉钉等。
2.适配深色模式的好处
我们强烈建议您为您的应用适配深色模式,主要有如下原因:
- 更酷、更时髦的流行趋势
作为一种全新的潮流,黑色界面受到众多用户、尤其是年轻用户的欢迎。目前,各大安卓系统、iOS系统都已经支持深色模式,众多主流头部应用也已适配或正在适配深色模式。
- 让用户更专注于内容
深色背景下,文字、图片、视频都能更清晰地呈现,尤其是暗光环境下。对于浏览器、资讯和视频类app,深色可以让用户沉浸其中,为应用贡献更多的使用时长:对于使用时段是晚上的应用,这一特性更加明显。
- 降低应用的耗电量
省电是用户最关注的性能之一。低电情况下,用户更青睐使用深色模式的应用。根据小米实验室测试数据:
OLED屏幕100%亮度下,深色模式耗电相比浅色模式,最高降低83%;
OLED屏幕50%亮度下,深色模式耗电相比浅色模式,最高降低50%。
- 推广优待适配深色模式时可以联系我们,小米可以为应用提供应用商店专题和微博宣传等方式,为应用增加额外的宣传。
3.适配方式
适配方式有两种:
- 适配深色模式资源:开发者根据自身应用设计深色模式,按照适配规范进行适配。
- 适配全局反色:利用安卓Q上提供的forcedark能力可以直接反色。
在适配前,开发者需要考虑是否在自有应用内增加深色模式开关。我们建议的方式:
- 务必提供跟随系统深色模式的选项。可以默认跟随系统,或在监测到系统切换为深色/浅色时提示用户。
- 可以为用户提供手动切换的开关。
目前已有三方应用如下(左 小红书 右 QQ),仅供参考:
方式一 适配深色模式资源
适配深色模式资源需要设计师先对所有页面设计深色页面,再由开发完成深色模式资源开发。
优点:在所有安卓版本、所有手机厂商用户均可以使用,且体验较好。
3.1.谷歌适配要求
开发者可基于谷歌深色模式适配标准进行适配:https://developer.android.com/guide/topics/ui/look-and-feel/darktheme#top_of_page
已经适配过的页面要将ForceDarkAllowed()参数值设置为false,若整个主题均已适配深色模式,则需要将这个主题的ForceDarkAllowed()参数值设置为false。
3.2.小米深色模式配色标准设计
其余适配建议详见设计文档:附件1-小米深色模式配色标准设计文档。
3.3.接入方式
3.3.1.接入介绍
- 根据谷歌深色模式接入标准,如要支持深色主题背景,您必须将应用的主题背景(通常可在res/values/styles.xml中找到)设置为继承 DayNight 主题背景:
<style name="AppTheme" parent="Theme.AppCompat.DayNight">
您还可以使用 MaterialComponent 的深色主题背景:
<style name="AppTheme" parent="Theme.MaterialComponents.DayNight">
这会将应用的主要主题背景与系统控制的夜间模式标记相关联,并将应用的默认主题背景设置为深色主题背景(如果已启用)。详细内容见:https://developer.android.com/guide/topics/ui/look-and-feel/darktheme#force_dark
从开发的角度说,就是打开夜间模式后,系统会优先从xxx-night资源中寻找资源并替换。
Android系统从2.2开始就已经⽀支持了了DarkMode,其对应接口是名为“uimode”的系统服务,DarkMode所使⽤用到的接口方法是:setNightMode、getNightMode分别对应设置夜间模式和获取设置状态。
// 获取uimode系统服务
UiModeManager uiModeManager = (UiModeManager)
getSystemService(Context.UI_MODE_SERVICE);
// 获取设置状态
int currentMode = uiModeManager.getNightMode();
// 设置夜间状态
uiModeManager.setNightMode(UiModeManager.MODE_NIGHT_AUTO); // ⾃动
uiModeManager.setNightMode(UiModeManager.MODE_NIGHT_YES); // 启⽤
uiModeManager.setNightMode(UiModeManager.MODE_NIGHT_NO); // 停⽤
在日志中的体现:
------------------------------------------------------
DUMP OF SERVICE uimode:
Current UI Mode Service state:
mDockState=0 mLastBroadcastState=0
mNightMode=2 mNightModeLocked=false
mCarModeEnabled=false mComputedNightMode=false
mCarModeEnableFlags=0 mEnableCarDockLaunch=true
mCurUiMode=0x2d mUiModeLocked=false mSetUiMode=0x2d
mHoldingConfiguration=false mSystemReady=true
mTwilightService.getLastTwilightState()=null
--------- 0.001s was the duration of dumpsys uimode,ending at: 2019-01-18 00:00:01--------------
从uimode服务dump出来的信息可以判断:
mNightMode的取值:0 → AUTO; 1 → NO; 2 → YES另外,在Android O开始,Android⽀支持adb命令启动和停⽤用DarkMode,具体命令是:adb shell cmd uimode night <auto | yes | no>
O以前的机型,只能通过代码调用uimode接口启动和停用DarkMode。需要注意的是,miui虽然从level 16开始支持DarkMode,但是并不意味着miui之前的版本不能切换DarkMode,用户完全可以通过命令或者代码调用系统接口,启动和关闭DarkMode。而Setting中的开关是阻⽌不了的,除⾮MIUI对这个权限进行收紧。
MIUI SDK从level 16开始,支持DarkMode,所以的支持DarkMode,是指MiuiSDK增加了新的主题,DayNight,来自适应的适配Light和Dark主题。其实在16之前的SDK上,Light和Dark主题都是一直存在的,只是没有一个自动适配的机制存在,而且Dark主题存在一些Bug,并不是它应该是的样⼦。适配过程中,存在一些APP,之前就是在Dark主题下进行的开发,那么在sdk level 16以后,发现Dark模式存一些问题,比如弹窗变成了⿊⾊,均是因为Dark主题修改造成的。附录中会给出SDK针对Dark和Light进行的改动,包括所有受影响的资源和主题样式修改。
3.3.2.接入方式详细说明
重点说明⼀下接⼊和使⽤⽅式。这⾥除了Theme.Light.DarkActionBar这个主题以外,所有Light(Dark)主题均有对应的DayNight主题。
- 对于非解耦应⽤,可以直接使⽤Miui SDK的public的资源和属性。
代码接⼊方式:可以直接将使⽤MiuiSdk的Light或者Dark主题的地方,替换成DayNight主题,⽐如:将
<style name="MiuiDemo.Theme.Test" parent="miui:Theme.Light">
或者<style name="MiuiDemo.Theme.Test" parent="miui:Theme.Dark">
替换成<style name="MiuiDemo.Theme.Test" parent="miui:Theme.DayNight">
Java代码中:将使⽤Light主题的所有地⽅miui.R.style.Theme_Light替换成miui.R.style.Theme_DayNight
- (如果本身就是⾃升级应用,就不⽤看这⼀步了)对于解耦自升级应⽤,需要在gradle中进行如下操作:
1)修改build.gradle中的dependencies,使⽤用miui插件,如下:(以gradle 3.3.0为例)
dependencies {
classpath 'com.miui.tools.build:gradle:3.3.0'
}
2)gradle/wrapper/gradle-wrapper.properties⽂文件中的gradle修改到5.0,即:
distributionUrl=http://sdk.pt.miui.com/miuisdk/software/gradle/gradle-5.0-all.zip
3)此步,3.2.0可以忽略略,在3.3.0下必须将:
apply plugin: 'com.android.application'
改成
apply plugin: 'com.miui.application'
4)如果gradlew的⽇日志中有App生成器如下输出:
add property to gradle.properties of this project:
android.aapt2FromMavenOverride=/home/{username}/.miuisdk/build-tools/aapt/28/aapt2
则修改gradle.properties,最后加一行:
android.aapt2FromMavenOverride=//home/{username}/.miuisdk/build-tools/aapt/28/aapt2
5)增加依赖:
compileOnly 'com.miui:core:alpha-SNAPSHOT'
于是,miui sdk中的Theme.DayNight就接入完成,在APP中可以直接使⽤Theme.DayNight.<…>主题。针对之前的miui:Theme.Light主题或者miui:Theme.Dark主题,均有对应的Dark或者Light主题。所以,如果需要适配自动切换夜间和日间模式,只要替换成对应的DayNight主题即可,DayNight主题负责⾃动切换两种(Dark和Light)主题。
- (非单发应⽤请忽略)对于单发应用:单发应用的接⼊⽅式,可以参考:MIUI SDK support包的使用指南将依赖由:compileOnly ‘com.miui:core:alpha-SNAPSHOT’改成 implementation ‘com.miui.support:core-compat:alpha-SNAPSHOT’
- 对于单发或者⾃升级应⽤,在代码接入上完全可以参考1中的代码接入方式。即,替换Light或者Dark为DayNight。但是,对于⾃自升级来说(单发应⽤不会有这个问题,因为单发应用已经将DayNight主题打包进APK),由于不确定⾃升级的目标ROM是否⽀持DayNight主题,所以如果在没有DayNight主题的ROM上⾃自升级,就会崩溃。这⾥需要看下⾯的注意事项1,可以找到解决方案。详见开发文档:附件2-深色模式开发文档
方式二 适配全局反色
全局反色利用安卓Q上提供的forcedark能力可以直接反色,对于开发者来说较为友好。
- 优点:开发工作量小,适配较快。
- 缺点:仅安卓Q手机可以使用该功能,复杂页面用forcedark适配难度较大;非原生的Webview无法反色,图片无法反色。
可以在安卓Q手机上,打开以下开关即可体验安卓Q反色能力,查看本应用反色后的情况。开关位置如下:
3.4.谷歌Force Dark适配要求
Android 10 提供 Force Dark 功能。一如其名,此功能可让开发者快速实现深色主题背景,而无需明确设置 DayNight 主题背景。如果您的应用采用浅色主题背景,则 Force Dark 会分析应用的每个视图,并在相应视图在屏幕上显示之前,自动应用深色主题背景。有些开发者会混合使用 Force Dark 和本机实现,以缩短实现深色主题背景所需的时间。应用必须选择启用 Force Dark,方法是在其主题背景中设置 android:forceDarkAllowed=”true”。此属性会在所有系统及 AndroidX 提供的浅色主题背景(例如 Theme.Material.Light)上设置。使用 Force Dark 时,您应确保全面测试应用,并根据需要排除视图。如果您的应用使用深色主题背景(例如Theme.Material),则系统不会应用 Force Dark。同样,如果应用的主题背景继承自 DayNight主题背景,则系统不会应用 Force Dark,因为会自动切换主题背景。
3.5.MIUI深⾊模式适配的目标和方法
在MIUI12上推出全局的深色模式,所有系统应用和主流三方应用适配。在MIUI12上用户开启深色模式后将默认开启全局反色。要达到的效果:系统切换到深⾊模式以后,应⽤也随之切换到深色模式,这是我们这次MIUI适配深色模式要达到的目标,也是我们对每个系统应⽤的要求。
适配的⽅法有三种:
- Android原生的深色主题
- 全局反⾊
- 第三方框架或者应用⾃自己实现深色模式
目前只有Android Q深色模式支持全局反色:Android Q上提供了两种实现深色模式的⽅式:深色主题和全局反⾊。
深⾊主题即Theme.AppCompat.DayNight或者Theme.MaterialComponents.DayNight。深⾊主题是Android官⽅提供的实现深色模式的一种方法,应用也可以其它自己的方法实现深色模式。深⾊主题起作用后,将会加载-night⽬目录下的资源,如果没有找到,那么加载默认资源。对于标准控件,如果应用不提供深色模式的资源,系统也会提供一套默认的深色资源(不过比较难看,基本不可用)全局反色即ForceDark,系统会根据一套算法,计算出每个View属于前台还是后台。对于前台View,会使其变亮,对于后台View,会使其变暗,⽽不需要开发者提供两套资源。具体的原理可以移步Android Q Force Dark调研。如果一个activity使⽤了深色模式,那么全局反色对其将不生效。
3.6.接入方式介绍
3.6.1.建议的整体适配步骤
- 现状评估
打开系统的深⾊模式,看看⾃⼰应用中那些⻚面有没有问题,一般可以分为三类:
A.适配了深色模式没有问题
B.没有适配深色模式的
C.适配了深⾊模式但是有问题的
- 全局反色过滤
对于所有的A,请务必禁用全局反色,否则全局反色可能会和已有的深⾊模式效果冲突。对于所有的B,可以设置 <item name=”android:forceDarkAllowed”>true</item> ,然后可以打开全局反色,观察有没有问题。没有问题的,那就适配完成了,问题⽐较⼤的归为D。
- 全局反色问题分析
对于所有的D,分析问题的原因,是反色本身的问题还是⾃己应⽤导致的,⽐如布局存在有问题,有图片、webVIew等。其他的看下能不能通过调整布局等最后还是通过全局反⾊简单解决,剩下统一归为C。
- 重新适配
对于C,看看是不是原来的适配方法有问题,有没有必要采用新的适配⽅法、框架,可不可以采用系统的深色主题等重新适配。
- 验证
打开系统深色模式,重新测试所有页⾯。
比较理想的做法是:建议采用深色主题和全局反色相结合的方式。整个应用用的主题使用DayNight,设置<item name=”android:forceDarkAllowed”>false</item>。对于已经⽀持深色主题或者用其他方法实现了深⾊模式的⻚面,没必要再支持全局反⾊,务必禁⽤。对于需要使用反⾊的⻚面,使⽤Light主题,设<itemname=”android:forceDarkAllowed”>true</item>。其他⻚面单独适配。
3.6.2.深色主题的适配
深色主题在Android Q之前就是已经有了的,只不过其在Android Q上得到了了加强。一个activity如果想使用深色主题,让其主题继承于DayNight主题或者让整个应用的主题继承于DayNight主题即可。不过,上⾯的做法只适合跟随系统变化的需求,如果有高级的需求,⽐如应用想要主动切换到深⾊色主题或者不跟随系统变化,而是让⽤户选择,那么需要这样做:
1.监听UImode的变化。manifest中 activity 设置属性 android:configChanges=”uiMode”,并重写onConfigurationChanged。注意,声明了configChanges系统将不不会重建对应的activity,⽽而是回调onConfigurationChanged,需要activity⾃己处理变化的情况
2.让Activity继承于AppCompatActivity,这样子可以通过AppCompatDelegate.setDefaultNightMode(mode) 设置当前activity的mode,通过AppCompatDelegate.getDefault私人app一键生成器NightMode()获取当前设置的mode。系统可以选择的mode有四种,具体解释可以看下面第1篇文章。
3.用户选择夜间模式的时候,保存用户选择的模式到SharedPreferences,调用recreate()重启activity,注意recreate()是重新创建activity,会回调所有生命周期方法。
4.activity启动的时候,取出之前保存的模式,通过AppCompatDelegate.setDefaultNightMode(mode) 进行当前activity的mode。
5.系统UImode发⽣变化的时候,即onConfigurationChanged时,获取系统当前的UImode,根据应用⾃身的逻辑判断需不需要改变当前activity的mode,如果需要改变重复第3、4步。
切换到深⾊模式时,资源应用的顺序:
- 应用提供的-night资源
- 应⽤设置的默认资源(通常是亮色下的资源,也可以是应用主动在深⾊模式下设置的主题)
- 系统的默认深⾊资源
- 系统默认的亮色资源
所以,凡是应用⾃己提供资源了的,那就提供两套,要么就全部⽤用系统的默认资源。如果一个应⽤没有使用DayNight主题,那么只会变化应用提供的-night深⾊资源,其他不会变化,系统也不会应⽤标准控件的默认深色资源。
具体实现,可以参考:
有两种方式,⼤同小异,相信大家能看得懂。
3.6.3.全局反色的适配
⾸先来看下全局反色生效前提:
系统端:1.开启深色模式
应用端:1.activity对应的主题是Light的 2.对应的控件允许全局反色,默认是forceDarkAllowedDefault,这个值由系统属性debug.hwui.force_dark决定,debug.hwui.force_dark⼜是由全局反⾊的开关控制,默认是关闭的。
以上几点缺⼀不可,需要详细说明几点是:
1.实现了深色主题的activity是不会受全局反色的影响,前面说了,深⾊主题是DayNight,不属于Light
2.如果一个控件没有申明forceDarkAllowed,那么forceDark是否⽣效取决于全局反色是否开启,如果全局反色开启,那么forceDark生效,否则不生效;
如果一个控件声明了forceDarkAllowed = false,那么无论如何都不会生效;
如果一个控件声明了forceDarkAllowed = true,那么只要开启深色模式,全局反色就会⽣效。
3.forceDarkAllowed可以在主题xml中申明,也可以在代码中动态设置,动态设置可以具体到某⼀个view,后者会覆盖前者。遵循覆盖原则,即子View的声明会覆盖父View的声明,activity的会覆盖这个应⽤的申明。
适配全局反色的步骤:
1.compileSdkVersion 设为29, 否则会编译失败,targetSdkVersion 貌似没有要求。
2.对于需要使用forceDark的activity,务必让其实现Light主题,Dark主题、DayNight主题都不会⽣效。
3.在activity的主题中申明<item name=”android:forceDarkAllowed”>true</item> 或者 在onCreate中,在setContentView之前,调⽤getWindow().getDecorView().setForceDarkAllowed(allowed)。
最后提供demo,需要的可以参考一下,源码:https://github.com/CQULittleMing/AndroidDemo/tree/master/demo
操作路径: View – View 下面最后的两个。
4.FAQ
4.1.全局反色的局限
- 目前全局反色只是实验性功能,存在很多bug,不可过于依赖,更多可以参考Force Dark目前存在的问题。
- 不会反色图⽚。
- WebView/Flutter⽀持不完善所以如果一个⻚面存在Flutter书写、WebView或者亮色的图片,建议还是使⽤深色模式进行适配。
4.2.如何开启和禁用全局反色
设置是否⽀持全局反色有静态和动态两种方法:
静态方式就是对应用或者activity的主题中声明<item name=”android:forceDarkAllowed”>true</item> 或者 <item name=”android:forceDarkAllowed”>false</item>;
动态⽅法是调用View.setForceDarkAllowed(true)或者View.setForceDarkAllowed(false),对DecorView设置可以对整个窗口起作⽤。
5.联系我们
有任何问题可以随时联系我们:
商务合作:liushuo3@xiaomi.com
产品问题:zhangyanan10@xiaomi.com
研发问题:darkmode@xiaomi.com
编辑:yimen,如若转载,请注明出处:https://www.yimenapp.com/kb-yimen/12151/
部分内容来自网络投稿,如有侵权联系立删