鸿蒙原生项目实战(五):深色主题适配与设置页功能完善
鸿蒙原生项目实战(五):深色主题适配与设置页功能完善
收官之作!本文覆盖鸿蒙深色模式适配机制、设置页完整实现、数据备份扩展、编码规范与性能优化,助你交付一个生产级品质的鸿蒙应用。
一、前言
一个完整的鸿蒙应用,不仅要功能可用,还要有良好的用户体验和系统集成度。本文涉及三个核心主题:
- 深色主题适配 — 利用鸿蒙资源限定词机制,零代码实现暗黑模式
- 设置页面实现 — 信息展示 + 数据管理 + 使用说明
- 备份扩展能力 — 系统级备份恢复集成
- 项目回顾 — 编码规范、性能优化、常见坑点
二、鸿蒙深色主题适配机制
2.1 资源限定词(Resource Qualifiers)
HarmonyOS 的资源限定词机制参照了 Android 的配置限定符概念。通过在 resources/ 下建立特定目录,同名资源会在对应条件下自动覆盖。
entry/src/main/resources/
├── base/ # 默认资源(所有条件下生效)
│ ├── element/
│ │ ├── color.json
│ │ ├── float.json
│ │ └── string.json
│ ├── media/
│ └── profile/
├── dark/ # 深色模式限定词
│ └── element/
│ └── color.json
└── rawfile/
2.2 深色颜色定义
dark/element/color.json:
{
"color": [
{
"name": "start_window_background",
"value": "#000000"
}
]
}
为什么只有 start_window_background?
这是初始版本,只覆盖了启动窗口背景色。实际项目应该覆盖完整的颜色系统:
{
"color": [
{ "name": "start_window_background", "value": "#000000" },
{ "name": "bg_page", "value": "#1A1A2E" },
{ "name": "card_bg", "value": "#16213E" },
{ "name": "text_primary", "value": "#EEEEEE" },
{ "name": "text_secondary", "value": "#AAAAAA" },
{ "name": "divider", "value": "#333333" }
]
}
2.3 引用方式
// 正确方式:通过 $r() 引用资源
.backgroundColor($r('app.color.bg_page'))
.fontColor($r('app.color.text_primary'))
// 错误方式:硬编码颜色(不会跟随深色模式切换)
.backgroundColor('#F0F4F8')
.fontColor('#2D3436')
2.4 深色模式生命周期
在 EntryAbility.ets 中:
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
this.context.getApplicationContext()
.setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_NOT_SET);
}
COLOR_MODE_NOT_SET 表示跟随系统设置。其他选项:
| 模式 | 行为 |
|---|---|
COLOR_MODE_NOT_SET |
跟随系统(推荐) |
COLOR_MODE_LIGHT |
强制浅色 |
COLOR_MODE_DARK |
强制深色 |
COLOR_MODE_AUTO |
自动(根据时间) |
2.5 验证方法
在 DevEco Studio 中,点击工具栏的 深色模式预览 图标,或在模拟器的系统设置中切换深色模式,即可实时查看效果。
如果有颜色没有正确切换,检查点:
- ✅ 是否全部使用了
$r('app.color.xxx')引用而非硬编码? - ✅
dark/element/color.json中是否定义了同名的颜色? - ✅ 自定义组件中的颜色是否也通过
@State或$r()引用?
三、设置页面实现
Settings.ets 是一个信息型页面,展示应用信息并提供数据管理功能。
3.1 页面状态
@Entry
@Component
struct Settings {
@State totalHabits: number = 0;
@State totalRecords: number = 0;
private habitManager: HabitManager = HabitManager.getInstance();
aboutToAppear(): void {
this.loadStats();
}
async loadStats(): Promise<void> {
const habits = await this.habitManager.getAllHabits();
const records = await this.habitManager.getAllRecords();
this.totalHabits = habits.length;
this.totalRecords = records.length;
}
}
3.2 UI 布局
Column
├── 顶部导航(← 返回 | 设置)
├── Scroll
│ ├── 应用信息卡片
│ │ ├── 🎯 习惯大师
│ │ ├── 每日习惯打卡与追踪助手
│ │ ├── 📋 习惯总数: N 个
│ │ ├── ✅ 总打卡次数: N 次
│ │ ├── 📱 应用版本: 1.0.0
│ │ └── 🎯 API 版本: API 23
│ ├── 数据管理卡片
│ │ ├── 说明文字
│ │ └── 🗑️ 重置所有数据(红色按钮)
│ └── 使用说明卡片
│ ├── 1. 点击「+新习惯」创建习惯
│ ├── 2. 在首页点击圆形区域打卡
│ ├── 3. 点击习惯卡片查看详情和日历
│ ├── 4. 在统计页面查看完成率趋势
│ └── 5. 在习惯详情页可删除习惯
3.3 应用信息卡片
@Builder
infoRow(icon: string, label: string, value: string) {
Row() {
Text(icon).fontSize(18).margin({ right: 12 });
Text(label).fontSize(15).fontColor($r('app.color.text_primary')).layoutWeight(1);
Text(value).fontSize(14).fontColor($r('app.color.text_secondary'));
}
.width('100%')
.padding({ top: 6, bottom: 6 });
}
3.4 数据重置功能
resetAllData(): void {
AlertDialog.show({
title: '确认重置',
message: '确定要清除所有习惯和打卡数据吗?\n此操作不可恢复!',
primaryButton: {
value: '取消',
action: () => {}
},
secondaryButton: {
value: '确认清除',
fontColor: '#FF6B6B', // 红色文字强调危险操作
action: () => {
this.doReset();
}
}
});
}
async doReset(): Promise<void> {
await this.habitManager.clearAll();
await this.loadStats();
AlertDialog.show({
title: '已重置',
message: '所有数据已清除',
confirm: { value: '确定', action: () => {} }
});
}
安全设计要点:
| 设计 | 说明 |
|---|---|
| 二次确认弹窗 | 防止误触 |
| 红色强调按钮 | 视觉提示危险性 |
| 操作后刷新 | UI 立即反映数据变化 |
| 操作后反馈 | 弹窗告知结果 |
3.5 使用说明卡片
Text('1. 点击首页底部「+新习惯」创建习惯')
.fontSize(14).fontColor($r('app.color.text_primary'))
.alignSelf(ItemAlign.Start).margin({ bottom: 6 });
Text('2. 在首页点击圆形区域打卡')
.fontSize(14).fontColor($r('app.color.text_primary'))
.alignSelf(ItemAlign.Start).margin({ bottom: 6 });
// ...
四、备份扩展能力
4.1 什么是 ExtensionAbility?
ExtensionAbility 是鸿蒙系统的扩展机制,允许应用在特定场景下运行后台任务。EntryBackupAbility 就是其中之一,用于系统备份和恢复。
4.2 注册扩展
module.json5 中注册:
"extensionAbilities": [
{
"name": "EntryBackupAbility",
"srcEntry": "./ets/entrybackupability/EntryBackupAbility.ets",
"type": "backup",
"exported": false,
"metadata": [
{
"name": "ohos.extension.backup",
"resource": "$profile:backup_config"
}
]
}
]
4.3 实现备份/恢复
import { BackupExtensionAbility, BundleVersion } from '@kit.CoreFileKit';
export default class EntryBackupAbility extends BackupExtensionAbility {
async onBackup() {
hilog.info(0x0000, 'testTag', 'onBackup ok');
await Promise.resolve();
}
async onRestore(bundleVersion: BundleVersion) {
hilog.info(0x0000, 'testTag', 'onRestore ok %{public}s', JSON.stringify(bundleVersion));
await Promise.resolve();
}
}
当用户在「设置 → 系统和更新 → 备份与恢复」中触发备份操作时,系统会自动调用 onBackup() 方法。
4.4 备份配置
resources/base/profile/backup_config.json 定义了哪些文件需要备份:
{
"allowToBackup": true,
"includes": [
"data/storage/el2/base/haps/entry/preferences/habit_store"
]
}
⚠️ Preferences 默认存储路径在
el2分区,需要明确配置才能被系统备份工具识别。
五、项目编码规范回顾
5.1 目录规范
ets/
├── entryability/ # Ability 层(生命周期)
├── entrybackupability/ # 扩展能力(备份)
├── model/ # 数据模型 + 业务逻辑
└── pages/ # 页面组件
5.2 命名规范
| 类型 | 规范 | 示例 |
|---|---|---|
| 文件 | PascalCase | DataManager.ets |
| 类 | PascalCase | HabitManager |
| 接口 | PascalCase | HabitRecord |
| 枚举 | PascalCase | HabitCategory |
| 函数 | camelCase | getToday() |
| 变量 | camelCase | totalHabits |
| 常量 | UPPER_SNAKE | KEY_HABITS |
| 组件 | PascalCase | Index |
5.3 导入规范
// 系统 Kit 导入
import { router } from '@kit.ArkUI';
import { preferences } from '@kit.DataKit';
// 项目模块导入
import { HabitManager } from '../model/DataManager';
// 数据模型导入
import { Habit, HabitCategory, ALL_CATEGORIES } from '../model/FinanceData';
六、常见坑点与解决方案
6.1 Preferences 内存泄漏
问题:每次操作都调用 getPreferences 打开新实例
解决:缓存 Promise,复用同一个 Preferences 实例:
private preferencesPromise: Promise<preferences.Preferences> | null = null;
private async getStore(): Promise<preferences.Preferences> {
if (!this.preferencesPromise) {
this.preferencesPromise = preferences.getPreferences(this.context, STORE_NAME);
}
return this.preferencesPromise;
}
6.2 事件冒泡导致导航冲突
问题:打卡区域的 onClick 冒泡到卡片容器的 onClick,导致点击打卡同时跳转详情
解决:使用 event.stopPropagation():
Column()
.onClick((event: ClickEvent) => {
event.stopPropagation();
this.toggleCheck(card);
})
6.3 ForEach 缺少 key
问题:列表增删时 ArkUI 难以做 diff 更新
解决:给 ForEach 提供 key 生成函数:
ForEach(this.habitCards,
(card: HabitCardInfo) => { this.habitCard(card); },
(card: HabitCardInfo) => card.habit.id // ⬅ key
)
6.4 页面路由未注册
问题:新增页面后未在 main_pages.json 注册,运行时报错找不到页面
解决:新增页面后立即在配置中添加:
{
"src": [
"pages/Index",
"pages/AddHabit",
"pages/HabitDetail",
"pages/Statistics",
"pages/Settings"
]
}
七、性能优化清单
| 优化项 | 当前状态 | 建议 |
|---|---|---|
| ForEach key | ⚠️ 未提供 | 添加 key 生成器 |
| 事件冒泡处理 | ⚠️ 未处理 | 添加 stopPropagation |
| 深色模式完整覆盖 | ⚠️ 不完整 | 补充全部颜色资源 |
| 列表增量刷新 | ✅ N/A(数据量小) | 暂可不优化 |
| 图片资源压缩 | ✅ 无大图 | 保持 |
| @State 粒度 | ✅ 合理 | 保持 |
| async 错误处理 | ⚠️ 部分缺失 | 建议统一 catch |
| 首屏加载优化 | ✅ 轻量数据 | 保持 |
八、项目全貌
8.1 应用架构图
┌──────────────────────────────────────┐
│ EntryAbility │
│ (生命周期管理 / Context 初始化) │
├──────────────────────────────────────┤
│ Pages │
│ ┌─────┬───────┬──────┬──────┬────┐ │
│ │Index│AddHabit│Detail│Statis│Set.│ │
│ └──┬──┴───┬───┴──┬───┴──┬───┴──┬─┘ │
│ │ │ │ │ │ │
├─────┴──────┴──────┴──────┴──────┴────┤
│ HabitManager (单例) │
│ CRUD / 统计 / 连续天数 / 分类分布 │
├──────────────────────────────────────┤
│ Preferences (轻量持久化) │
│ habits[] + records[] → JSON │
└──────────────────────────────────────┘
8.2 数据流
用户操作 → 页面事件 → HabitManager.xxx()
↓
更新 Preferences(flush)
↓
返回数据到页面
↓
@State 变量更新
↓
UI 自动渲染
九、发布与部署
9.1 构建 Release 包
DevEco Studio → Build → Build Hap(s)/App(s) → Release
9.2 签名配置
在 build-profile.json5 中配置签名信息(需先在 DevEco Studio 中申请证书和密钥):
"signingConfigs": [
{
"name": "default",
"material": {
"certificatePath": ".../debug.cer",
"keyStorePath": ".../debug.p12",
"keyStorePassword": "******",
"keyAlias": "key0",
"keyAliasPassword": "******"
}
}
]
9.3 上架华为应用市场
- 在 AppGallery Connect 创建应用
- 上传 HAP 包
- 填写应用描述、分类、隐私说明
- 提交审核
十、总结
通过五篇文章,我们完整走完了鸿蒙原生应用「习惯大师」的全开发流程:
| 篇章 | 核心内容 |
|---|---|
| 第一篇 | 项目初始化、工程架构、核心配置 |
| 第二篇 | 数据模型、Preferences 持久化、单例 Manager |
| 第三篇 | 首页 UI、声明式布局、打卡交互 |
| 第四篇 | 统计图表、日历热力图、连续天数算法 |
| 第五篇 | 深色主题、设置页、备份扩展、编码规范 |


推荐学习路径
- 📘 HarmonyOS 官方开发文档
- 🎓 ArkTS 语言基础(TypeScript 语法 + 声明式 UI)
- 🔧 实践本文项目 → 尝试添加新功能
- 🚀 接入更多系统能力(通知、传感器、地图等)
本系列完结。感谢阅读!如果对本文有任何疑问,欢迎在评论区留言交流。
关于作者:一名专注于鸿蒙原生开发的技术博主,持续分享 HarmonyOS NEXT 实战经验。
更多推荐


所有评论(0)