深色模式下那一片白

应用上线第三天,有用户反馈夜间模式下顶部导航还是刺眼的纯白。原因很简单:那个 backgroundColor('#ffffff') 写死在代码里,从来没被主题管过。深浅色适配说起来只有一句话,做起来坑却不少,颜色、图片、状态栏、切换时机,每一环都能漏。这篇把实操路径捋一遍。

颜色不要硬编码

规则只有一条,但值得反复强调:界面颜色一律走资源引用,不在组件里写字面量。

先定义颜色,放在 resources/base/element/color.json:

{
  "color": [
    { "name": "page_bg", "value": "#FFFFFF" },
    { "name": "text_primary", "value": "#1A1A1A" },
    { "name": "card_bg", "value": "#F5F5F5" }
  ]
}

再建一份深色限定的同名文件,路径是 resources/dark/element/color.json:

{
  "color": [
    { "name": "page_bg", "value": "#121212" },
    { "name": "text_primary", "value": "#E8E8E8" },
    { "name": "card_bg", "value": "#1E1E1E" }
  ]
}

名字完全一致,只是值不同。系统切到深色模式时会自动加载 dark 目录下的资源,代码一个字都不用改。

@Entry
@Component
struct Index {
  build() {
    Column() {
      Text('账户余额')
        .fontColor($r('app.color.text_primary'))
        .fontSize(16)
    }
    .width('100%')
    .height('100%')
    .backgroundColor($r('app.color.page_bg'))
  }
}

$r('app.color.page_bg') 在运行时按当前主题解析,浅色拿白、深色拿黑。这里最常见的错误是只建了 base 目录,忘了 dark 目录,结果深色下依然是白底黑字,查半天以为是系统没切换。

纯黑还是深灰

有个细节容易被忽略:深色模式的底色用纯黑 #000000 还是深灰 #121212。纯黑对比度最强,但大面积纯黑配白字在 OLED 屏上容易产生拖影和视觉疲劳,而且卡片、分割线全都没法区分层级。我习惯用 #121212 做主背景,卡片用 #1E1E1E,浮层用 #2A2A2A,三级层次一眼可分。纯黑留给需要沉浸的场景,比如全屏图片浏览。

图片资源也要分深浅

颜色改完了,图片还是白的。带透明背景的 logo、插画、空状态图,在深色下往往惨不忍睹。媒体资源目录同样支持 dark 限定:把深色版图片放在 resources/dark/media/ 下,文件名和 base 保持一致,引用方式不变,系统自己挑。

Image($r('app.media.empty_state')).width(120)

如果图片是给第三方的位图,没办法做两套,那就用 fillColor 对单色图标做染色,或者给容器加一层低透明度的遮罩把亮色压下去。别硬扛,硬扛的结果是深色下每张图都像打了闪光灯。

切换时机与刷新

用户在系统设置里切主题,应用在前台时应该立刻跟随,不能要求重启。

import { ConfigurationConstant } from '@kit.AbilityKit';

// UIAbility 里监听配置变化
onConfigurationUpdate(newConfig: Configuration): void {
  const isDark =
    newConfig.colorMode === ConfigurationConstant.ColorMode.COLOR_MODE_DARK;
  AppStorage.setOrCreate('isDark', isDark);
}

资源引用型的颜色会自动刷新,但如果某些地方用 @StorageProp('isDark') 做了逻辑分支(比如深色下换成不同的插画),那么依赖这个状态变量的组件需要 @StorageProp 或 @StorageLink 才能响应变化,用普通变量接一下就永远不会更新。这是第二个高频坑。

有一点要克制:不要做"应用内主题开关"去覆盖系统设置,除非产品明确要求。鸿蒙用户对跟随系统的预期很强,应用自己另搞一套,用户切成深色发现你不跟随,体验反而更差。真要做,也是"跟随系统/浅色/深色"三选项,默认跟随。

状态栏与沉浸区

最后一个容易忘的是状态栏和导航栏。深色模式底下如果状态栏还是浅色背景配深色文字,视觉上像贴了块补丁。系统提供了窗口级的外观设置,切换主题时同步调整状态栏内容颜色,或者直接申请沉浸式布局,让内容延伸到状态栏下方,靠内容本身的对比度解决。这一步不做,前面颜色和图片都改对了,观感依然会露怯。

写在最后

把取舍说清楚:颜色全部资源化,base 与 dark 成对出现;底色选深灰而不是纯黑,靠三级层次区分卡片与浮层;图片要么出两套,要么染色处理;主题跟随系统,监听配置变化,逻辑分支用状态装饰器接住;状态栏外观收尾。这套做法不复杂,但它有个隐性收益——资源化的过程会逼你把散落各处的硬编码颜色收敛成一套设计变量,以后换品牌色、做节日皮肤,改动量从几百处降到几十处。深色模式只是顺手拿到的结果。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐