前言颜色模式Color Mode管理是应用用户体验的重要组成部分。HarmonyOS 提供了应用级别的颜色模式设置能力允许开发者通过ApplicationContext.setColorMode()方法统一控制应用的颜色模式——跟随系统、强制浅色或强制深色。本文以小事记xiaoshiji_ohos_app 的EntryAbility.ets中setColorMode的调用为切入点结合SettingsPage.ets中的主题设置需求深入解析 HarmonyOS 颜色模式的实现机制、资源限定符的配置规则和深色主题适配的最佳实践。核心特点简单易用API 设计直观上手成本低性能优异底层优化充分运行效率高扩展性强支持自定义配置和扩展本文参考 HarmonyOS 官方文档application-context-stage.md 和 ConfigurationConstant 参考。一、颜色模式的三元组1.1 ConfigurationConstant.ColorMode 枚举HarmonyOS 定义了三种颜色模式枚举值常量名称说明-1COLOR_MODE_NOT_SET跟随系统设置默认值0COLOR_MODE_LIGHT强制浅色模式1COLOR_MODE_DARK强制深色模式// 三种颜色模式的使用 import { ConfigurationConstant } from kit.AbilityKit; // 跟随系统推荐 ConfigurationConstant.ColorMode.COLOR_MODE_NOT_SET; // -1 // 强制浅色 ConfigurationConstant.ColorMode.COLOR_MODE_LIGHT; // 0 // 强制深色 ConfigurationConstant.ColorMode.COLOR_MODE_DARK; // 11.2 小事记中的颜色模式设置在EntryAbility.ets的onCreate中应用在启动时设置了颜色模式// EntryAbility.ets — 设置颜色模式 import { AbilityConstant, ConfigurationConstant, UIAbility, Want } from kit.AbilityKit; import { hilog } from kit.PerformanceAnalysisKit; const DOMAIN 0x0000; export default class EntryAbility extends UIAbility { onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void { try { // 设置颜色模式为跟随系统 this.context.getApplicationContext().setColorMode( ConfigurationConstant.ColorMode.COLOR_MODE_NOT_SET ); } catch (err) { hilog.error(DOMAIN, testTag, Failed to set colorMode. Cause: %{public}s, JSON.stringify(err)); } hilog.info(DOMAIN, testTag, %{public}s, Ability onCreate); } }关键设计点必须通过ApplicationContext调用—setColorMode是应用级别的操作只有ApplicationContext提供该方法try-catch包裹— 官方文档推荐使用try-catch捕获可能的异常如权限不足或系统错误COLOR_MODE_NOT_SET是最佳默认值— 跟随系统设置尊重用户的系统偏好二、setColorMode 的调用机制2.1 为什么必须通过 ApplicationContextsetColorMode是ApplicationContext独有的方法UIAbilityContext和UIContext都不提供// ❌ 错误UIAbilityContext 没有 setColorMode // this.context.setColorMode(...); // 编译错误 // ✅ 正确通过 ApplicationContext 设置 this.context.getApplicationContext().setColorMode( ConfigurationConstant.ColorMode.COLOR_MODE_NOT_SET );Context 类型是否提供 setColorMode原因ApplicationContext✅颜色模式是应用级配置全局生效UIAbilityContext❌Ability 级别不控制全局配置AbilityStageContext❌模块级别不控制全局配置UIContext❌UI 实例级别不控制全局配置2.2 setColorMode 的效果范围setColorMode设置的颜色模式会影响整个应用包括所有UIAbility实例的窗口背景通过Styles和Extend定义的主题样式资源限定符dark目录下的资源文件系统组件如Navigation、List的默认颜色2.3 颜色模式的持久化setColorMode设置的颜色模式不会自动持久化应用重启后会恢复为默认值。如果需要记住用户的选择需要将颜色模式偏好存储到ohos.data.preferences// 保存颜色模式偏好 import { preferences } from kit.DataKit; import { common } from kit.AbilityKit; async function saveColorModePreference( context: common.Context, mode: number ): Promisevoid { const pref await preferences.getPreferences(context, user_preferences); await pref.put(color_mode, mode); await pref.flush(); } // 读取颜色模式偏好 async function loadColorModePreference( context: common.Context ): Promisenumber { const pref await preferences.getPreferences(context, user_preferences); const mode await pref.get(color_mode, ConfigurationConstant.ColorMode.COLOR_MODE_NOT_SET); return mode as number; }三、资源限定符与深色主题3.1 资源限定符目录结构HarmonyOS 通过资源限定符目录实现深色主题适配。当应用切换到深色模式时系统会自动加载dark限定符目录下的资源resources/ ├── base/ │ ├── element/ │ │ ├── color.json ← 浅色模式颜色定义 │ │ ├── string.json │ │ └── float.json │ └── media/ │ └── logo.png ├── dark/ ← 深色模式限定符 │ └── element/ │ └── color.json ← 深色模式颜色定义 └── en_US/ ← 语言限定符 └── element/ └── string.json3.2 颜色资源的定义浅色模式颜色resources/base/element/color.json{ color: [ { name: bg_primary, value: #F8F9FA }, { name: bg_secondary, value: #FFFFFF }, { name: text_primary, value: #1A1A2E }, { name: text_secondary, value: #6B7280 }, { name: text_tertiary, value: #9CA3AF }, { name: accent_color, value: #7B68EE }, { name: divider_color, value: #E5E7EB }, { name: shadow_color, value: #00000008 } ] }深色模式颜色resources/dark/element/color.json{ color: [ { name: bg_primary, value: #121212 }, { name: bg_secondary, value: #1E1E1E }, { name: text_primary, value: #E0E0E0 }, { name: text_secondary, value: #A0A0A0 }, { name: text_tertiary, value: #707070 }, { name: accent_color, value: #9B8BED }, { name: divider_color, value: #333333 }, { name: shadow_color, value: #00000020 } ] }3.3 在组件中使用资源引用在组件中通过$r引用资源系统会根据当前颜色模式自动加载对应的颜色值// 使用 $r 引用颜色资源自动适配深色模式 Entry Component struct HomePage { build() { Column() { Text(小事记) .fontSize(20) .fontWeight(FontWeight.Bold) .fontColor($r(app.color.text_primary)) // 自动适配深色/浅色 Text(记录 86 个重要时刻) .fontSize(12) .fontColor($r(app.color.text_tertiary)) // 自动适配深色/浅色 } .width(100%) .height(100%) .backgroundColor($r(app.color.bg_primary)) // 自动适配深色/浅色 } }四、深色主题的适配策略4.1 颜色适配直接使用$r引用// ✅ 推荐使用 $r 引用资源系统自动适配 Text(标题) .fontColor($r(app.color.text_primary)) .backgroundColor($r(app.color.bg_secondary))使用 Resource 类型// 定义 Resource 类型变量 let bgColor: Resource $r(app.color.bg_primary); let textColor: Resource $r(app.color.text_primary);4.2 图片适配深色模式下图片资源也需要适配避免亮色图片在深色背景上显得刺眼resources/ ├── base/media/ │ └── logo.png ← 浅色模式下的图片 └── dark/media/ └── logo.png ← 深色模式下的图片降低亮度或调整对比度在组件中引用时使用同样的$r路径系统会自动选择Image($r(app.media.logo)) // 系统根据颜色模式自动选择对应资源4.3 阴影适配深色模式下传统的灰色阴影不可见需要使用透明度更高的颜色// 浅色模式阴影 .shadow({ radius: 4, color: #00000008, offsetX: 0, offsetY: 2 }) // 通过资源引用适配阴影 .shadow({ radius: 4, color: $r(app.color.shadow_color), // 深色模式下颜色更深 offsetX: 0, offsetY: 2 })五、在 SettingsPage 中实现主题切换5.1 主题切换的 UI小事记的SettingsPage.ets中预留了“主题设置“入口可以扩展为完整的主题切换功能// SettingsPage.ets — 主题设置扩展 import router from ohos.router; import { common } from kit.AbilityKit; import { ConfigurationConstant } from kit.AbilityKit; import { BottomTabBar } from ../common/BottomTabBar; Entry Component export struct SettingsPage { private uiAbilityContext this.getUIContext().getHostContext() as common.UIAbilityContext; State currentColorMode: number ConfigurationConstant.ColorMode.COLOR_MODE_NOT_SET; build() { Column() { Scroll() { Column({ space: 16 }) { this.buildAppearanceSettings() } .padding({ bottom: 20 }) } .layoutWeight(1) BottomTabBar({ currentTab: SettingsPage }) } .width(100%) .height(100%) .backgroundColor($r(app.color.bg_primary)) } Builder buildAppearanceSettings() { Column({ space: 0 }) { // 主题选择标题 Text(主题模式) .fontSize(16) .fontWeight(FontWeight.Bold) .fontColor($r(app.color.text_primary)) .padding({ left: 20, right: 20, top: 16, bottom: 12 }) // 跟随系统 this.buildThemeOption(跟随系统, ConfigurationConstant.ColorMode.COLOR_MODE_NOT_SET) Divider().color($r(app.color.divider_color)) // 浅色模式 this.buildThemeOption(浅色模式, ConfigurationConstant.ColorMode.COLOR_MODE_LIGHT) Divider().color($r(app.color.divider_color)) // 深色模式 this.buildThemeOption(深色模式, ConfigurationConstant.ColorMode.COLOR_MODE_DARK) } .width(100%) .backgroundColor($r(app.color.bg_secondary)) .borderRadius(16) .margin({ left: 20, right: 20 }) .shadow({ radius: 4, color: $r(app.color.shadow_color), offsetX: 0, offsetY: 1 }) } Builder buildThemeOption(label: string, mode: number) { Row() { Text(label) .fontSize(15) .fontColor($r(app.color.text_primary)) Blank() if (this.currentColorMode mode) { Circle() .width(24) .height(24) .fill($r(app.color.accent_color)) } else { Circle() .width(24) .height(24) .fill(Color.Transparent) .border({ width: 1.5, color: $r(app.color.text_tertiary) }) } } .width(100%) .height(52) .padding({ left: 20, right: 20 }) .onClick(() { this.switchColorMode(mode); }) } private switchColorMode(mode: number): void { this.currentColorMode mode; // 通过 ApplicationContext 切换颜色模式 let appContext this.uiAbilityContext.getApplicationContext(); try { appContext.setColorMode(mode); // 持久化用户的选择 this.saveColorModePreference(mode); } catch (err) { console.error(切换颜色模式失败: ${JSON.stringify(err)}); } } private async saveColorModePreference(mode: number): Promisevoid { // 保存到 Preferences // 下次启动时读取并恢复 } }5.2 主题切换的三种选项选项值效果适用场景跟随系统NOT_SET自动匹配系统设置默认选项尊重用户偏好浅色模式LIGHT强制浅色在强光环境下使用深色模式DARK强制深色在暗光环境下使用节省电量六、颜色模式变化的监听6.1 监听系统颜色模式变化当系统颜色模式发生变化时应用可以通过Configuration的回调感知变化// 在 UIAbility 中监听颜色模式变化 onConfigurationUpdated(config: Configuration): void { if (config.colorMode ! undefined) { console.log(颜色模式变化为: ${config.colorMode}); // 0 浅色1 深色 if (config.colorMode 1) { console.log(系统切换到深色模式); } else { console.log(系统切换到浅色模式); } } }6.2 使用 Watch 监听状态变化在组件中可以通过Watch装饰器监听颜色模式状态的变化Entry Component struct HomePage { State Watch(onColorModeChange) colorMode: number 0; onColorModeChange(): void { // 颜色模式变化时执行的操作 console.log(颜色模式变更为: ${this.colorMode}); // 可以在此处更新某些无法通过 $r 自动适配的 UI 元素 } }七、颜色模式与性能7.1 资源加载的性能影响切换颜色模式时系统会重新加载所有资源文件这可能会带来一定的性能开销资源类型加载开销优化建议颜色值低直接使用$r引用无需额外优化图片资源中控制深色模式图片的数量和尺寸矢量图低优先使用 SVG 矢量图而非位图字体低颜色模式通常不影响字体7.2 避免频繁切换频繁切换颜色模式会导致资源重新加载影响性能。建议使用COLOR_MODE_NOT_SET跟随系统避免手动切换如果提供手动切换选项只在用户明确选择时切换切换时使用animateTo添加过渡动画提升用户体验// 使用 animateTo 添加颜色模式切换动画 import { animateTo } from kit.ArkUI; private async switchColorModeWithAnimation(mode: number): Promisevoid { try { await animateTo({ duration: 300, curve: Curve.EaseInOut }, () { let appContext this.uiAbilityContext.getApplicationContext(); appContext.setColorMode(mode); }); } catch (err) { console.error(切换颜色模式动画失败); } }八、常见问题与解决方案8.1 颜色模式设置后未生效问题调用setColorMode后UI 没有变化。可能原因使用了硬编码颜色值而非$r资源引用资源限定符目录名称错误如dark拼写为drak颜色模式设置被其他模块覆盖解决方案// ❌ 错误硬编码颜色值不会随颜色模式变化 Text(标题) .fontColor(#1A1A2E) // 始终为浅色 // ✅ 正确使用 $r 资源引用 Text(标题) .fontColor($r(app.color.text_primary)) // 自动适配深色模式8.2 资源限定符不生效问题创建了dark目录但深色模式下没有加载该目录下的资源。检查清单目录名称必须是dark全小写目录位置必须在resources/下资源文件中的name必须与base目录中的一致颜色模式必须设置为COLOR_MODE_DARK或COLOR_MODE_NOT_SET跟随系统深色8.3 深色模式下的可读性问题问题深色模式下文字与背景的对比度不足。WCAG 对比度标准文本类型最低对比度推荐对比度正文文本4.5:17:1大号文本18px 或 14px bold3:14.5:1禁用状态文本3:13:1调整深色模式颜色// 深色模式颜色调整 { color: [ { name: text_primary, value: #E0E0E0 }, // 对比度 ~15:1 ✓ { name: text_secondary, value: #A0A0A0 }, // 对比度 ~9:1 ✓ { name: text_tertiary, value: #707070 } // 对比度 ~5:1 ✓ ] }九、与 Android 和 iOS 的对比对比维度HarmonyOSAndroidiOS设置方式ApplicationContext.setColorMode()AppCompatDelegate.setDefaultNightMode()UIWindowScene.requestGeometryUpdate()模式枚举NOT_SET/LIGHT/DARKMODE_NIGHT_FOLLOW_SYSTEM/MODE_NIGHT_NO/MODE_NIGHT_YESUIUserInterfaceStyle枚举资源限定符dark/目录values-night/目录Assets.xcassets的 Any/Dark资源引用$r(app.color.xxx)color/xxxUIColor(named:)全局生效是应用级别是应用级别是窗口级别十、最佳实践总结10.1 颜色模式管理的推荐策略默认跟随系统使用COLOR_MODE_NOT_SET尊重用户偏好提供手动切换在设置页面提供浅色/深色/跟随系统三个选项持久化用户选择使用Preferences保存用户的选择应用启动时恢复使用$r引用资源所有颜色值通过$r引用避免硬编码适配图片资源深色模式下提供降亮度的图片版本10.2 颜色 Token 设计建议为应用定义一套完整的颜色 Token 系统Token 名称浅色模式值深色模式值用途bg_primary#F8F9FA#121212页面主背景色bg_secondary#FFFFFF#1E1E1E卡片背景色text_primary#1A1A2E#E0E0E0主要文字颜色text_secondary#6B7280#A0A0A0次要文字颜色accent_color#7B68EE#9B8BED主题强调色divider_color#E5E7EB#333333分割线颜色shadow_color#00000008#00000020阴影颜色总结本文从xiaoshiji_ohos_app的EntryAbility.ets源码出发深入解析了 HarmonyOS 的颜色模式管理机制。核心要点如下三种颜色模式COLOR_MODE_NOT_SET跟随系统、COLOR_MODE_LIGHT浅色、COLOR_MODE_DARK深色通过ApplicationContext.setColorMode()设置资源限定符通过dark/目录定义深色模式资源系统自动根据当前颜色模式加载对应资源$r资源引用所有颜色值通过$r引用确保颜色模式切换时 UI 自动更新主题切换持久化使用Preferences保存用户选择应用启动时恢复深色模式适配需要适配颜色、图片、阴影等多个维度确保深色模式下的可读性和视觉一致性下一篇文章将深入解析oh-package.json5 依赖管理ohos 与 kit 的模块化演进。如果这篇文章对你有帮助欢迎点赞、收藏⭐、关注你的支持是我持续创作的动力相关资源小事记项目源码xiaoshiji_ohos_app官方文档 - Contextapplication-context-stage.md官方文档 - ConfigurationConstantjs-apis-app-ability-configurationconstant官方文档 - ApplicationContextjs-apis-inner-application-applicationcontext官方文档 - 用户体验application-dev-overview.md官方文档 - 资源管理resource-manager官方文档 - 应用生命周期application-lifecycle.md开源鸿蒙跨平台社区https://openharmonycrossplatform.csdn.net