为了跟上友商的步伐,我们最近也上了暗黑模式。
结果:
1.网页兼容安卓10及其以上,由网页前端处理;
2.原生页面设置浅色和深色都有效。但是跟随系统的话,由于安卓系统10以下没有设置暗黑模式的入口,所以设置跟随系统后一直是浅色。
3.由于App由原生 View 和 Compose View 实现的,所以需要两套 UI 都要实现暗黑模式;
4.在处理 UI 基础 library 和业务 library 实现了暗黑模式的同时,需要处理没有暗黑模式要求的 App ,直接设置为浅色模式即可
遇到的问题:
- 无法兼容 android 10 以下的系统
- 修改暗黑模式后,部分颜色没有更新
- 修改暗黑模式后,跟随系统部分手机不生效
- 系统暗黑模式修改后,无论当前处理什么模式,所有 Activity 都会 reCreate
问题1:无法兼容 android 10 以下的系统
1.1 第三方库 Android-skin-support
开始之前也在网上查过相关的实现,类似功能就是换肤。
但是,我们做的是暗黑模式,包括:浅色,深色,跟随系统。
其中浅色和深色比较好实现,但是跟随系统的话,换肤功能就不方便实现了。用的最多的就是 Android-skin-support 这个库,实现起来也不容易,当然自己实现也挺麻烦。
未处理的 issue 接近三位数了,最后一次维护是3年前,慎用。
插件换肤,我们除了需要在国内各大应用市场上架外,还需要在 google play 上架,皮肤颜色相关的资源打包成一个 .apk 文件放在住工程内就不符合 google play 的上架规范。
1.2 我们的用户手机系统低于 android 10 的用户占比不多,小公司也那么多人手去做这种投入产出比较低的事情
1.3 我们的 App 有传统 View 和 Compose View 的实现,接入可能产生其他问题
问题2:修改暗黑模式后,部分颜色没有更新
2.1 所有 View 相关的页面,包括 View 和 Compose View 混合的页面的 Activity 必须继承自 AppCompatActivity。单纯的 Compose 页面没有这个要求。
2.2 除非特殊页面,比如分享页外,所有地方的颜色都不能直接硬编码。所有颜色均使用 xml 中定义的两套颜色,或使用 Compose 调色板中指定的主题颜色。
2.3 View 设置颜色不能直接使用 application 的 Context 作为上下文去获取颜色,需使用 Activity 的 Context 去获取上下问。
问题3: 修改暗黑模式后,跟随系统部分手机不生效
这个手查了很久才解决的问题,起初发现是在 debug 版本和 beta 版本之间的差异问题,在 debug 版本中不存在这个问题。最后发现是由于 debug 版本中关闭了多语言的功能,为了提升编译速度,禁止了多语言相关的实现。
最后发现是在对多语言处理的时候,导致了 Configuration 中的 uiMode 出现了问题。然后解决办法也就很清晰了:
override fun attachBaseContext(context: Context?) {super.attachBaseContext(InjectUtils.updateLocalConfig(context))}override fun onConfigurationChanged(newConfig: Configuration) {super.onConfigurationChanged(newConfig)InjectUtils.onConfigChange(newConfig) // 再次矫正 UI_MODE}
// InjectUtils.kt
fun updateLocalConfig(context: Context?): Context? {return context?.let {val languageContext = setLocalLanguage(it)setLocalDarkMode(languageContext)}}
private fun setLocalDarkMode(context: Context?): Context? {val res = context?.resourcesval config = Configuration(res?.configuration)config.uiMode = DarkModeUtils.getCurrentUIMode(config.uiMode)return context?.createConfigurationContext(config)}
// DarkModeUtils.kt 根据当前模式还原正确的 UI_MODE 参数
fun getCurrentUIMode(uiMode: Int) = when (getCurrentMode()) {AppearanceMode.DAY -> Configuration.UI_MODE_NIGHT_NOAppearanceMode.NIGHT -> Configuration.UI_MODE_NIGHT_YESelse -> Configuration.UI_MODE_NIGHT_UNDEFINED}.let {uiMode and Configuration.UI_MODE_NIGHT_MASK.inv() or it}
问题四:
暂时无法解决,看起来是系统处理的。
4.1 对于重要的页面,进行数据保存: onSaveInstanceState
4.2 说服老板