引言

现代应用都有"白天用浅色、晚上用深色"的需求。本篇实现:颜色资源化、深色资源自动适配、应用内三档主题切换(跟随系统/浅色/深色)并持久化。


一、颜色资源化

目前页面里全是硬编码颜色('#F5F5F5' 背景、'#333' 文字、Color.White 卡片),这些颜色不会跟随系统深色模式变化。正确的做法是把颜色定义到资源文件里,页面通过 $r() 引用。

1.1 浅色资源(base)

// resources/base/element/color.json
{
  "color": [
    { "name": "start_window_background", "value": "#FFFFFF" },
    { "name": "page_bg", "value": "#F5F5F5" },
    { "name": "card_bg", "value": "#FFFFFF" },
    { "name": "text_primary", "value": "#333333" },
    { "name": "text_secondary", "value": "#999999" },
    { "name": "expense", "value": "#FF4444" },
    { "name": "income", "value": "#44BB44" },
    { "name": "brand", "value": "#6C63FF" },
    { "name": "chip_bg", "value": "#F0F0F0" },
    { "name": "divider", "value": "#DDDDDD" }
  ]
}

1.2 深色资源(dark)

工程自动生成了 resources/dark 目录——只要在 dark/element/color.json 里定义同名颜色,系统切到深色模式时会自动加载这一份,页面代码一行都不用改。

// resources/dark/element/color.json
{
  "color": [
    { "name": "start_window_background", "value": "#000000" },
    { "name": "page_bg", "value": "#121212" },
    { "name": "card_bg", "value": "#1E1E1E" },
    { "name": "text_primary", "value": "#E6E6E6" },
    { "name": "text_secondary", "value": "#8A8A8A" },
    { "name": "expense", "value": "#FF6B6B" },
    { "name": "income", "value": "#5FCF5F" },
    { "name": "brand", "value": "#8B84FF" },
    { "name": "chip_bg", "value": "#333333" },
    { "name": "divider", "value": "#3A3A3A" }
  ]
}

二、页面使用资源色

把硬编码颜色替换为 $r('app.color.xxx'),以账单卡片为例:

// components/BillCard.ets
Row() {
  // ...分类 emoji、名称、备注
  Text(`${this.bill.type === BillType.EXPENSE ? '-' : '+'}¥${this.bill.amount.toFixed(2)}`)
    .fontSize(18)
    .fontWeight(FontWeight.Bold)
    .fontColor(this.bill.type === BillType.EXPENSE
      ? $r('app.color.expense')
      : $r('app.color.income'))
}
.width('100%')
.padding(16)
.backgroundColor($r('app.color.card_bg'))
.borderRadius(12)

需要注意:

  • 背景card_bg / page_bg文字text_primary / text_secondary
  • 金额用语义色 expense(支出)/ income(收入)
  • 按钮上的白字(如紫色按钮的 fontColor(Color.White))保持不变——深色模式下按钮字色仍然是白色
  • 三元表达式里混合使用也没问题,例如筛选 chip:选中态 $r('app.color.brand') + 白字,未选中态 $r('app.color.chip_bg') + $r('app.color.text_primary')

替换完所有页面(首页、新增、编辑、统计、导出、卡片组件)后,切换深浅色,整个应用自动换肤。深色模式下首页的效果——背景、卡片、文字、金额全部切换:

在这里插入图片描述


三、主题切换原理

鸿蒙通过 setColorMode 控制应用配色模式:

setColorMode

COLOR_MODE_NOT_SET 跟随系统

COLOR_MODE_LIGHT 强制浅色

COLOR_MODE_DARK 强制深色

系统深色时加载 dark 资源

始终加载 base 资源

始终加载 dark 资源

// EntryAbility.onCreate 中
this.context.getApplicationContext().setColorMode(
  ConfigurationConstant.ColorMode.COLOR_MODE_DARK
);

getApplicationContext().setColorMode()应用级设置,设置后立即生效——正在显示的所有页面都会按新模式重新加载资源颜色,无需手动刷新页面状态,这就是资源化的好处。深色模式下的统计页同样自动适配:

在这里插入图片描述


四、主题持久化

用户选过的主题要记住,下次启动直接生效。复用 Preferences(和第 16 篇同一个 easy_account 文件,只是换一个 key):

// utils/ThemeManager.ts
import { preferences } from '@kit.ArkData';
import { common } from '@kit.AbilityKit';

const THEME_KEY = 'theme_mode';

// 读取持久化的主题模式,默认跟随系统
export function loadThemeMode(context: common.Context): string {
  try {
    const prefs = preferences.getPreferencesSync(context, 'easy_account');
    return prefs.getSync(THEME_KEY, 'auto') as string;
  } catch (e) {
    return 'auto';
  }
}

// 保存主题模式
export function saveThemeMode(context: common.Context, mode: string): void {
  try {
    const prefs = preferences.getPreferencesSync(context, 'easy_account');
    prefs.putSync(THEME_KEY, mode);
    prefs.flushSync();
  } catch (e) {
  }
}

启动时在 EntryAbility.onCreate 读取并应用:

const mode = loadThemeMode(this.context);
const colorMode = mode === 'light' ? ConfigurationConstant.ColorMode.COLOR_MODE_LIGHT :
  mode === 'dark' ? ConfigurationConstant.ColorMode.COLOR_MODE_DARK :
    ConfigurationConstant.ColorMode.COLOR_MODE_NOT_SET;
this.context.getApplicationContext().setColorMode(colorMode);

五、切换 UI

首页顶栏加一个"主题"入口,点击弹出操作面板(ActionSheet),提供三档选择:

// pages/HomePage.ets
private showThemeSheet() {
  this.getUIContext().showActionSheet({
    title: '主题模式',
    autoCancel: true,
    confirm: { value: '取消', action: () => {
    } },
    sheets: [
      { title: '跟随系统', action: () => this.applyTheme('auto') },
      { title: '浅色模式', action: () => this.applyTheme('light') },
      { title: '深色模式', action: () => this.applyTheme('dark') }
    ]
  });
}

private applyTheme(mode: string) {
  const appCtx = (getContext(this) as common.UIAbilityContext)
    .getApplicationContext();
  if (mode === 'light') {
    appCtx.setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_LIGHT);
  } else if (mode === 'dark') {
    appCtx.setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_DARK);
  } else {
    appCtx.setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_NOT_SET);
  }
  saveThemeMode(getContext(this), mode);
}

点击首页顶栏"主题",弹出三档选择面板:

在这里插入图片描述

顶栏入口(与"统计""导出"并列):

@Builder
HeaderBar() {
  Row() {
    // ...月份切换
    Row() {
      Text('统计').fontSize(14).fontColor($r('app.color.brand'))
      Text('导出').fontSize(14).fontColor($r('app.color.brand'))
      Text('主题').fontSize(14).fontColor($r('app.color.brand'))
    }
  }
}

深色模式下导出页(月度报告数值配色同样自动适配):

在这里插入图片描述


总结

本篇实现了:

  1. 颜色资源化:硬编码颜色全部迁入 base/dark 双份资源
  2. 自动换肤:页面用 $r() 引用资源色,深浅色自动适配
  3. 三档主题:跟随系统 / 浅色 / 深色,setColorMode 一键切换
  4. 持久化:主题选择存 Preferences,重启后仍然生效

下篇实现列表骨架屏加载效果。

Logo

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

更多推荐