系统切到深色模式,页面背景还是白的,状态栏文字却跟着系统变白了,顶部直接看不见;Text 里写死的 Color.Black 在白底上好好的,切过去和深色背景融为一体;更离谱的是,整个应用退到后台再切回来,部分页面的颜色只反转了一半,看着像个半成品。

这真不是系统的深色模式有 bug,而是代码里写死了颜色,系统只认资源。鸿蒙的深色适配核心思路是“差异化声明出来,交给资源目录匹配”,而不是每页去 AppStorage.get('isDark') 然后手写两套色值。

资源限定符的同名映射机制

最小配置只需要两个文件,放在 src/main/resources 下。base/element/color.json 定义浅色值,dark/element/color.json 用相同的资源名定义深色值。

json5

// base/element/color.json
{ "color": [
  { "name": "page_bg", "value": "#FFFFFF" },
  { "name": "page_text", "value": "#182431" }
] }
// dark/element/color.json
{ "color": [
  { "name": "page_bg", "value": "#182431" },
  { "name": "page_text", "value": "#F1F3F5" }
] }

页面里只写 $r('app.color.page_text'),不出现任何布尔判断。系统切色模式时,ResourceManager 自动从 dark 目录取同名资源,组件跟着刷新。

typescript

Column() {
  Text('内容').fontColor($r('app.color.page_text'))
}
.width('100%')
.height('100%')
.backgroundColor($r('app.color.page_bg'))

关键语句是 $r()。它指向的是资源名,不是具体色值。资源名不变,值随限定词目录切换,这是整个适配机制的支点。

不要试图用if-else硬切颜色

很多老项目习惯在页面里维护一个 isDark 变量,然后通过判断去切换样式类或者直接改颜色。这种写法不仅维护成本高(每个页面都要加一堆判断),而且极其容易漏。一旦遇到弹窗、Toast、甚至自定义的 Canvas,if-else 的逻辑根本覆盖不全。

如果在开发过程中发现某些颜色在深色模式下依然没变,先全局搜一下代码里有没有硬编码的 Color.White 或者 #FFFFFF。把业务代码里的硬编码颜色全部抽离到 color.json 中,是深色模式适配的第一步,也是最苦但最有效的一步。

Web与Canvas的独立渲染边界

颜色资源解决的是 ArkUI 组件的声明式颜色。Web 组件里加载的网页、Canvas 里自绘的像素,不会因为 dark/element/color.json 就自动反转。这些边界要么单独注入深色样式,要么在深色模式下替换素材。

对于 Web 组件,需要在前端页面里支持 prefers-color-scheme 媒体查询。ArkUI 侧的深色模式切换时,Web 容器的背景色可能已经变了,但网页内部没变,这时候需要通过 runJavaScript 去通知网页切换主题。

typescript

Web({ src: $rawfile('index.html') })
  .onPageEnd(() => {
    if (isDarkMode()) {
      this.controller.runJavaScript('document.body.classList.add("dark")')
    }
  })

如果你的 Canvas 里有自绘的图表,也要在 Canvas 绘制之前去读取当前的系统颜色模式,判断画笔颜色是否需要反转。不能指望它自动适配。

系统状态栏的跟随策略

沉浸式场景下状态栏文字颜色在深色模式会自动变白。如果你的页面背景做了深浅适配,但某个页面的背景色在深色下仍然偏亮(比如展示了一张浅色的商品大图),状态栏白字就会看不清。

这种情况下,要么让背景色也走资源适配,要么在需要时动态设置状态栏字体颜色。不能只改页面不改状态栏,导致头部信息不可见。

typescript

window.getLastWindow(getContext(this)).then((win) => {
  win.setWindowSystemBarProperties({
    statusBarContentColor: '#FFFFFF', // 根据实际情况动态设置
  })
})

开发警示与后续排查手段

深色适配的工程化答案是把颜色从代码里赶到资源目录里。排查时,先看页面上的硬编码颜色有没有删干净,再看 dark 目录下的颜色值是否符合暗色对比度标准,最后检查 Web 和 Canvas 这些不受系统强制控制的区域。写一个简单的脚本扫描一下全局代码里的 # 开头的颜色值,往往能揪出大量的隐藏地雷。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐