鸿蒙开发之鸿蒙应用深色模式适配完整指南(上架过程之适配手机深色模式)
鸿蒙应用深色模式适配指南:针对开发中无法正确适配深色模式的问题,本文提供了完整解决方案。关键步骤包括:1)创建base/dark两套颜色资源文件;2)替换所有硬编码颜色为资源引用($r('app.color.xxx'));3)修改EntryAbility设置COLOR_MODE_NOT_SET以跟随系统;4)处理特殊语义颜色。适配后应用可自动切换深色/浅色模式,符合UX设计规范。修改涉及15个页面
📌 前言
在开发鸿蒙应用时,遇到了一个常见问题:应用无法正常适配深色模式,不符合鸿蒙应用UX设计规范。具体表现为:
- 在应用内打开深色模式开关后,界面颜色不变
- 在设备切换为深色模式后,应用未正常跟随系统
本文记录了完整的解决过程,希望能帮助遇到同样问题的开发者。
开发环境:
- DevEco Studio 5.0.4
- HarmonyOS API 16
- 15个页面需要适配
🔍 问题分析
核心问题
应用中大量使用了硬编码颜色,例如:
// ❌ 硬编码颜色
.fontColor('#000000')
.backgroundColor('#FFFFFF')
.fillColor('#1698E4')
这些硬编码颜色在深色模式下不会自动改变,导致:
- 白色背景在深色模式下依然是白色(刺眼)
- 黑色文字在深色背景上看不清
- 品牌色在深色模式下对比度不足
错误尝试
最初尝试在 module.json5 中添加 configChanges: ["colorMode"],但遇到编译错误:
Value should be one of: "priority", "name", "srcEntry", "launchType", "description", "icon", "label", "permissions", "metadata", "visible", "exported", "skills", "backgroundModes", "continueType", "startupVisible"
原因: API 16 不需要手动配置 configChanges,系统会自动适配。
✅ 解决方案
第一步:创建颜色资源文件
1.1 创建浅色模式颜色资源
文件路径: entry/src/main/resources/base/element/color.json
{
"color": [
{
"name": "page_background",
"value": "#FFFFFF"
},
{
"name": "page_background_secondary",
"value": "#F5F5F5"
},
{
"name": "text_primary",
"value": "#000000"
},
{
"name": "text_secondary",
"value": "#666666"
},
{
"name": "text_tertiary",
"value": "#999999"
},
{
"name": "brand_color",
"value": "#007DFF"
},
{
"name": "text_on_brand",
"value": "#FFFFFF"
},
{
"name": "icon_tertiary",
"value": "#666666"
},
{
"name": "divider_color",
"value": "#E0E0E0"
},
{
"name": "start_window_background",
"value": "#FFFFFF"
}
]
}
1.2 创建深色模式颜色资源
文件路径: entry/src/main/resources/dark/element/color.json
{
"color": [
{
"name": "page_background",
"value": "#000000"
},
{
"name": "page_background_secondary",
"value": "#1C1C1E"
},
{
"name": "text_primary",
"value": "#FFFFFF"
},
{
"name": "text_secondary",
"value": "#8E8E93"
},
{
"name": "text_tertiary",
"value": "#636366"
},
{
"name": "brand_color",
"value": "#0A84FF"
},
{
"name": "text_on_brand",
"value": "#FFFFFF"
},
{
"name": "icon_tertiary",
"value": "#8E8E93"
},
{
"name": "divider_color",
"value": "#38383A"
},
{
"name": "start_window_background",
"value": "#000000"
}
]
}
说明:
- 深色模式下,浅色变深,深色变浅
- 品牌色在深色模式下略微调亮,保持识别度
- 必须包含
start_window_background,否则编译报错
第二步:替换硬编码颜色
2.1 颜色替换对照表
| 硬编码颜色 | 替换为资源引用 | 说明 |
|---|---|---|
#FFFFFF, Color.White |
$r('app.color.page_background') |
白色背景 |
#F5F5F5, #EEEEEE |
$r('app.color.page_background_secondary') |
浅灰背景 |
#000000, Color.Black |
$r('app.color.text_primary') |
黑色文字 |
#666666 |
$r('app.color.text_secondary') |
灰色文字 |
#999999, Color.Gray |
$r('app.color.text_tertiary') |
浅灰文字 |
#007DFF, #0A59F7 |
$r('app.color.brand_color') |
品牌蓝色 |
Color.White(按钮文字) |
$r('app.color.text_on_brand') |
按钮白字 |
2.2 替换示例
背景色:
// ❌ 修改前
Column() {
Text("内容")
}
.backgroundColor('#FFFFFF')
// ✅ 修改后
Column() {
Text("内容")
}
.backgroundColor($r('app.color.page_background'))
文字颜色:
// ❌ 修改前
Text("标题")
.fontColor('#000000')
Text("副标题")
.fontColor('#666666')
// ✅ 修改后
Text("标题")
.fontColor($r('app.color.text_primary'))
Text("副标题")
.fontColor($r('app.color.text_secondary'))
品牌色/强调色:
// ❌ 修改前
Button('确认')
.backgroundColor('#007DFF')
.fontColor(Color.White)
// ✅ 修改后
Button('确认')
.backgroundColor($r('app.color.brand_color'))
.fontColor($r('app.color.text_on_brand'))
条件表达式:
// ❌ 修改前
Image($r('app.media.icon'))
.fillColor(this.selected ? '#1698E4' : '#666666')
Text(item.text)
.fontColor(this.selected ? '#1698E4' : '#666666')
// ✅ 修改后
Image($r('app.media.icon'))
.fillColor(this.selected ? $r('app.color.brand_color') : $r('app.color.icon_tertiary'))
Text(item.text)
.fontColor(this.selected ? $r('app.color.brand_color') : $r('app.color.text_secondary'))
第三步:修改 EntryAbility.ets
关键问题: 如果代码中强制设置了颜色模式,深色模式将无法生效。
3.1 错误代码示例
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
// ❌ 强制浅色模式,导致深色模式失效
this.context.getApplicationContext()
.setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_LIGHT);
}
3.2 正确做法
文件路径: entry/src/main/ets/entryability/EntryAbility.ets
import { AbilityConstant, ConfigurationConstant, UIAbility, Want } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { window } from '@kit.ArkUI';
const DOMAIN = 0x0000;
export default class EntryAbility extends UIAbility {
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
// ✅ 设置为跟随系统,自动适配深色模式
this.context.getApplicationContext()
.setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_NOT_SET);
hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onCreate');
}
// 可选:监听深色模式切换事件
onConfigurationUpdate(newConfig: Configuration): void {
hilog.info(DOMAIN, 'testTag', 'ColorMode changed: %{public}d', newConfig.colorMode);
// UI 会自动刷新,无需手动处理
}
// ... 其他方法
}
ColorMode 说明:
COLOR_MODE_NOT_SET- 跟随系统(推荐)COLOR_MODE_LIGHT- 强制浅色COLOR_MODE_DARK- 强制深色
第四步:处理特殊颜色
某些颜色有特殊语义(如成功绿色、错误红色),需要单独定义:
在 base/element/color.json 中添加:
{
"name": "status_success",
"value": "#4CAF50"
},
{
"name": "status_error",
"value": "#E4080A"
},
{
"name": "status_warning",
"value": "#FFA000"
}
在 dark/element/color.json 中添加:
{
"name": "status_success",
"value": "#66BB6A"
},
{
"name": "status_error",
"value": "#EF5350"
},
{
"name": "status_warning",
"value": "#FFB84D"
}
使用方式:
Text(errorMessage)
.fontColor($r('app.color.status_error'))
Button('成功')
.backgroundColor($r('app.color.status_success'))
⚠️ 常见问题
1. 编译错误:ref '$color:xxx' don't be defined
原因: 颜色资源文件中缺少某个颜色定义。
解决: 检查 base/element/color.json 和 dark/element/color.json,确保所有用到的颜色都已定义。
2. 遮罩层在深色模式下显示异常
说明: 弹窗遮罩层通常使用半透明黑色,不需要适配。
// ✅ 遮罩层保持黑色
Column()
.backgroundColor('#000000')
.opacity(0.5)
3. 切换深色模式后应用没有变化
检查清单:
- [ ] 颜色资源文件是否正确创建
- [ ] 代码中是否替换了所有硬编码颜色
- [ ] EntryAbility 中是否设置了
COLOR_MODE_NOT_SET - [ ] 是否重新编译(Clean Build)
- [ ] 是否完全关闭应用重新打开
4. module.json5 需要配置吗?
答: API 16 不需要配置 configChanges。
❌ 错误做法:
{
"abilities": [
{
"configChanges": ["colorMode"] // API 16 不支持
}
]
}
✅ 正确做法:保持默认配置,系统会自动适配。
🎯 修改统计
以本项目为例,完成深色模式适配:
- 修改页面数量: 15个
- 替换硬编码颜色: 157+ 处
- 创建颜色资源: 10+ 种基础色 + 5种语义色
- 修改时间: 约2小时
📊 修改前后对比
浅色模式
| 元素 | 修改前 | 修改后 |
|---|---|---|
| 主背景 | #FFFFFF |
$r('app.color.page_background') |
| 主文字 | #000000 |
$r('app.color.text_primary') |
| 品牌色 | #007DFF |
$r('app.color.brand_color') |
深色模式(自动切换)
| 元素 | 颜色值 | 效果 |
|---|---|---|
| 主背景 | #000000 |
黑色背景 |
| 主文字 | #FFFFFF |
白色文字 |
| 品牌色 | #0A84FF |
亮蓝色 |
🔍 验证方法
检查是否还有硬编码颜色
在项目根目录执行:
grep -r "#[0-9A-Fa-f]\{6\}" entry/src/main/ets/pages/
如果没有输出,说明所有硬编码颜色都已替换。
测试深色模式切换
- 编译运行应用
- 进入系统设置 → 显示与亮度 → 开启深色模式
- 返回应用,界面应自动切换为深色
- 关闭深色模式,界面应自动切换回浅色
✅ 最终效果
完成适配后:
- ✅ 自动跟随系统:系统切换深色/浅色模式,应用自动跟随
- ✅ 无需手动刷新:颜色实时更新,用户体验流畅
- ✅ 符合设计规范:通过鸿蒙应用UX设计规范审核
- ✅ 减少维护成本:使用系统资源,后续维护简单
💡 最佳实践建议
1. 颜色命名规范
- 使用语义化命名:
page_background、text_primary - 避免颜色值命名:~~
color_white~~、~~color_black~~
2. 颜色层级
主背景 (page_background)
└─ 次级背景 (page_background_secondary)
└─ 卡片背景
主文字 (text_primary)
└─ 次要文字 (text_secondary)
└─ 三级文字 (text_tertiary)
3. 品牌色使用
- 交互元素:按钮、链接、选中状态
- 强调信息:重要提示、数据高亮
- 保持一致性:全局统一使用
brand_color
4. 对比度要求
- 主文字对比度 > 4.5:1
- 大号文字对比度 > 3:1
- 确保深色模式下文字清晰可读
📚 参考资料
🎉 总结
深色模式适配的核心思路:
- 创建颜色资源:base 和 dark 两套配置
- 替换硬编码:所有颜色使用
$r('app.color.xxx') - 跟随系统:EntryAbility 设置
COLOR_MODE_NOT_SET - 测试验证:多场景测试,确保显示正常
虽然修改量较大(本项目157+处),但流程清晰,按照规范逐个替换即可。完成后不仅通过审核,还能提升用户体验,特别是夜间使用场景。
希望这篇文章能帮助到遇到同样问题的开发者!如有疑问,欢迎交流讨论。
作者: 全球通史
日期: 2025年11月
标签: HarmonyOS, 深色模式, DevEco Studio, API 16, 鸿蒙开发
更多推荐




所有评论(0)