HarmonyOS 鸿蒙Next中系统一开暗色模式页面全花了?ArkUI 颜色适配从硬编码到系统联动

HarmonyOS 鸿蒙Next中系统一开暗色模式页面全花了?ArkUI 颜色适配从硬编码到系统联动 项目快上线了才发现暗色模式完全没适配,打开系统暗色模式之后:

  1. 页面背景还是白色,但系统状态栏文字变成了白色,顶部直接看不见
  2. 有些 Text 组件的文字颜色写死成 Color.Black,暗色模式下背景变深色,文字跟背景融为一体
  3. SVG 图标是深色的,暗色模式下在深色背景上几乎看不见
12 回复

【解决方案】

开发者您好,您需要对深色模式进行适配,参见深色模式适配,包括颜色资源适配媒体资源适配状态栏适配。针对您提出的三点问题,回复如下:

  1. 根据您的描述应用已启用沉浸式,状态栏背景色与应用背景色保持一致,而状态栏文字默认在浅色模式下显示黑色,在深色模式下切换成白色。状态栏适配中已提供解决方案:若背景色可以做深浅色适配,则采用颜色资源适配的方案对应用背景色进行适配,背景色适配时需考虑到状态栏文字在深浅色模式下的默认表现;若背景色无法做深浅色适配,或做了深浅色适配,但是背景的沉浸式颜色与默认的状态栏文字颜色对比度较低,这种情况下需要获取当前的深浅色状态并动态设置状态栏字体颜色。
  2. Text组件的文字颜色需要进行颜色资源适配,在深色模式下适配浅色的字体颜色。
  3. SVG图标需要进行媒体资源适配适配,在深色模式下适配浅色图标。

更多关于HarmonyOS 鸿蒙Next中系统一开暗色模式页面全花了?ArkUI 颜色适配从硬编码到系统联动的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


可以看下我的实战案例:如何给自己的应用适配深浅主题模式?【案例示例】

  • 当系统切换到深色模式后,应用内可能会出现部分内容切换到深色主题的情况,例如状态栏、弹窗背景色、系统控件等,会导致应用内页面效果错乱。

    为应对上述情况,需要对应用进行深色模式下的内容适配,目前该适配主要依靠资源目录。当系统对应的设置项发生变化后(如系统语言、深浅色模式等),应用会自动加载对应资源目录下的资源文件。

图片

相关文档:【应用深浅色适配】

开发者您好,把颜色和资源文件放到src/main/resources/dark目录里,然后在页面通过$r引用,系统就会自动适配深色模式了,具体写法看完此文就懂了:

鸿蒙开发入门:如何适配深色模式之颜色篇

https://developer.huawei.com/consumer/cn/blog/topic/03212842464249225

鸿蒙开发入门:如何适配深色模式之媒体篇

https://developer.huawei.com/consumer/cn/blog//topic/03212973185022433

学到了

背景知识:

鸿蒙系统明暗色模式切换原理

  1. 系统分为两套全局资源集:base(默认亮色)、dark(暗色专属);系统切换深色模式后,应用会优先加载dark目录下同路径资源、颜色、图片;
  2. 状态栏文字颜色自动跟随系统主题:亮色状态栏黑字、暗色状态栏白字。如果页面根背景仍是白色,白色状态栏文字直接融合看不见,就是你第一个问题;
  3. 硬编码写死颜色Color.Black、固定深色 SVG 图标,不会随系统主题自动切换,暗色模式下对比度失效,文字 / 图标消失;
  4. 核心错误根源:
  • 全部颜色、图片直接写死硬编码,未使用系统主题资源色;
  • 未创建dark暗色资源目录;
  • 页面未设置自适应根背景,状态栏未适配沉浸主题色;
  • 未开启应用跟随系统深色模式开关。

问题解决:

1、两套全局资源集:

cke_5255.png

创建不同模式下的资源

2、状态栏文字颜色自动跟随系统主题

import uiAppearance from '@ohos.uiAppearance';


// 获取当前是否暗色模式
function isDarkMode(): boolean {
    const scheme = uiAppearance.getDarkMode();
    return scheme === uiAppearance.DarkMode.ALWAYS_DARK;
}

// 动态设置状态栏
this.windowStage.getMainWindow().setWindowSystemBarProperties({
    statusBarFontColor: isDarkMode() ? "#ffffff" : "#000000"
})

3、硬编码写死颜色Color.Black、固定深色 SVG 图标

//text的颜色直接使用 上面的base 和 dark
Text("主标题")
  .fontColor($r('app.color.text_main'))

Text("辅助说明")
  .fontColor($r("sys.color.text_secondary"))

// base/element/color.json
{
  "name": "text_main",
  "value": "#000000"
}

//dark/element/color.json
{
  "name": "text_main",
  "value": "#ffffff"
}


//使用矢量图标主题色填充
Image($r("sys.media.icon_home"))
  .width(24)
  .height(24)
//亮色图标:resources/base/media/icon_home.svg(黑色)
//暗色图标:resources/dark/media/icon_home.svg(白色)

这个不是单个组件 bug,更像是颜色体系没有语义化:页面背景、文字、图标、系统栏各写各的固定色,系统切到暗色后就互相“撞色”了。建议先把硬编码颜色收口到资源,再让资源按浅色/深色切换。

可按这几步改:

  1. 不要继续写 Color.BlackColor.White'#000000' 这类固定色。页面颜色改成语义资源:
// resources/base/element/color.json
{
  "color": [
    { "name": "page_bg", "value": "#FFFFFF" },
    { "name": "text_primary", "value": "#182431" },
    { "name": "icon_primary", "value": "#182431" }
  ]
}
// resources/dark/element/color.json
{
  "color": [
    { "name": "page_bg", "value": "#101014" },
    { "name": "text_primary", "value": "#E5E5E5" },
    { "name": "icon_primary", "value": "#E5E5E5" }
  ]
}
  1. 组件里只引用资源:
Column() {
  Text('标题').fontColor($r('app.color.text_primary'))
  Image($r('app.media.ic_back')).fillColor($r('app.color.icon_primary'))
}
.backgroundColor($r('app.color.page_bg'))
  1. SVG 图标分两类处理:单色图标用 Image.fillColor($r('app.color.icon_primary')) 统一着色;多色图标不要强行 fillColor,准备深浅色两套资源或把 SVG 内部颜色整理成标准写法。

  2. 状态栏要和顶部背景一起处理。顶部是浅色就用深色状态栏文字,顶部是深色就用浅色状态栏文字;不要只改页面背景,不同步系统栏内容色。

排查时可以先全局搜 Color.BlackColor.White#000#fff,把这些点逐个替换成语义色。官方 Code Linter 的 color-value 规则也建议颜色通过 $rcolor.json 引用,以适配不同系统颜色模式。

依据:Code Linter @cross-device-app-dev/color-value 规则、ArkUI 颜色资源适配说明。

https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/ide-code-linter

这个问题在实际项目里很常见,很多项目开发阶段只测了浅色模式,最后一开暗色模式整个页面“炸了”。

根本原因一般是:页面颜色写死,没有走系统主题资源适配。

1、不要直接写死颜色

比如:

Text('标题')
  .fontColor(Color.Black)

暗色模式:

背景变深色

文字还是 Black

自然看不见。

建议改成资源颜色:

Text('标题')
  .fontColor($r('app.color.text_primary'))

然后:

{
  "text_primary": {
    "value": "#000000"
  }
}

dark目录:

resources/
 ├ base/element/color.json
 └ dark/element/color.json

dark里面:

{
  "text_primary": {
    "value": "#FFFFFF"
  }
}

系统切换时会自动替换。

2、背景不要固定白色

例如:

.background('#FFFFFF')

暗色下就会出现:

背景白 + 状态栏白字。

改:

.background($r('app.color.page_bg'))

浅色:

#FFFFFF

深色:

#000000

3、状态栏颜色也要同步

页面背景变暗,但是状态栏还是系统判断颜色,很容易:

白底 + 白字

或者:

黑底 + 黑字

需要根据主题设置:

window.setWindowSystemBarProperties({
  statusBarColor: bgColor,
  statusBarContentColor: isDark
    ? 'light'
    : 'dark'
})

4、SVG图标也是高频坑

很多 SVG:

fill="#000000"

暗色背景:

黑图标直接消失。

建议:

  • 使用多套资源
  • 或使用 tint 动态着色

比如:

light:

icon_black.svg

dark:

icon_white.svg

资源目录区分。

5、全局排查建议

项目里搜索:

#FFFFFF
#000000
Color.Black
Color.White

这些基本都是暗色风险点。

实际项目推荐:

不要组件自己决定颜色

应该:

主题资源 → 组件引用 → 系统切换自动适配

流程:

系统暗色切换

→ resources dark 生效

→ ArkUI重新渲染

这样后期不会一个页面一个页面补。

希望可以帮到你~~~~

单独建立一个theme 文件,定义一下项目各个元素的颜色吧,而且反复测试很重要,还有中英文的适配,就连很多大公司的适配也是如此,特别是英文界面上,蹦出好多中文来。

请提供需要转换的HTML内容。

暗色模式页面花屏源于硬编码颜色不随系统切换。ArkUI应使用$r('sys.color.*')系统颜色资源或定义color.json并配置dark限定词资源,使颜色自动跟随暗色模式。避免在代码中直接写固定色值。

暗色模式适配需要全面替换硬编码颜色,ArkUI 推荐使用系统资源或 resource 中的限定词目录:

  • 状态栏文字颜色:通过 windowStage.getMainWindow().setWindowSystemBarProperties 设置状态栏图标颜色,并根据深色模式切换。更推荐使用 enableStatusBarDarkFont 动态控制,或使用 followSystem 自动跟随。
  • 页面背景/文字:不要用 Color.Black / Color.White,改用 $r('sys.color.xxx') 或自定义 resource 中的 color.json,并在 basedark 限定词下分别配置。
  • SVG 图标:使用 $r('sys.media.xxx') 区分深浅色图标,或使用 fillColor 绑定 $r('sys.color.xxx')

核心原则:所有颜色走资源引用,避免硬编码,系统切换时 UI 自动刷新。状态栏单独做监听 onColorModeChange 更新即可。

回到顶部