🐾 鸿蒙应用实战:从零开发「宠物成长日记」—— 记录喂食、洗澡、疫苗与体重

版本:HarmonyOS 6.1.0 (API 12) | 开发工具:DevEco Studio | 语言:ArkTS + ArkUI
项目源码:基于 Stage 模型,RDB 数据库存储,纯 ArkTS 实现


📖 一、前言

1.1 为什么做这个应用?

养宠物的人越来越多,但毛孩子的健康管理却常常靠「脑子记」—— 今天喂了吗?上次洗澡是什么时候?疫苗什么时候打的?体重有没有变化?这些琐碎信息拼在一起,才是宠物健康的全貌。

市面上的宠物 App 要么功能臃肿,要么需要注册账号。我想要的是一 个轻量、离线、打开即用的记录工具。于是就有了这个「宠物成长日记」。

1.2 技术选型

维度 选择 理由
开发语言 ArkTS HarmonyOS 原生声明式语言
UI 框架 ArkUI 组件化、响应式、一站式
数据存储 relationalStore (RDB) 结构化数据,SQL 查询灵活
项目模型 Stage Model 推荐的新一代 Ability 模型
API 版本 6.1.0 (23) 兼容当前主流鸿蒙设备
导航方式 router 轻量页面路由

🏗️ 二、项目架构设计

2.1 整体结构

entry/src/main/ets/
├── model/                  # 数据模型层
│   └── PetRecord.ets       # 记录类型枚举、接口定义
├── database/               # 数据持久层
│   └── DatabaseHelper.ets  # RDB 建表、CRUD 操作
├── pages/                  # UI 页面层
│   ├── Index.ets           # 首页仪表盘
│   ├── AddRecord.ets       # 添加记录
│   ├── RecordDetail.ets    # 记录列表
│   └── PetInfo.ets         # 宠物信息编辑
└── entryability/
    └── EntryAbility.ets    # 应用入口

2.2 三层架构说明

应用严格遵循 Model → Database → Pages 的三层分离:

  1. Model 层:定义 PetRecordPetInfo 接口,以及 RecordType 枚举。纯数据契约,不包含任何 UI 逻辑。
  2. Database 层:封装所有 RDB 操作,对外暴露 async 函数。上层只需调用 getRecords()addRecord() 等,无需关心 SQL 细节。
  3. Pages 层:纯 UI 渲染,通过 @State 驱动视图更新,数据从 Database 层获取。

这种分层的好处是:如果以后要迁移到云端存储,只需要替换 Database 层的实现,UI 层完全不动。


💾 三、数据模型与数据库设计

3.1 实体关系

应用只有两个实体,关系非常简单:

PetInfo (1) ── 拥有 ──▶ (N) PetRecord

一 只宠物对应多条成长记录。

3.2 PetRecord 表设计

CREATE TABLE IF NOT EXISTS pet_records (
    id           INTEGER PRIMARY KEY AUTOINCREMENT,
    type         TEXT NOT NULL,       -- 'feeding' | 'bathing' | 'vaccine' | 'weight'
    pet_name     TEXT NOT NULL,       -- 宠物名字(冗余存储,方便查询)
    date         TEXT NOT NULL,       -- 记录日期 YYYY-MM-DD
    time         TEXT NOT NULL,       -- 记录时间 HH:MM
    detail_value TEXT DEFAULT '',     -- 具体数值(体重/喂食量/疫苗名/洗澡方式)
    notes        TEXT DEFAULT '',     -- 备注文字
    created_at   TEXT NOT NULL        -- 系统创建时间戳
);

3.3 PetInfo 表设计

CREATE TABLE IF NOT EXISTS pet_info (
    id       INTEGER PRIMARY KEY AUTOINCREMENT,
    name     TEXT NOT NULL,           -- 宠物名字
    species  TEXT DEFAULT '',         -- 种类(猫/狗/兔子...)
    birthday TEXT DEFAULT '',         -- 生日
    avatar   TEXT DEFAULT ''          -- 头像路径(预留)
);

3.4 为什么选用 RDB 而非 Preferences?

HarmonyOS 提供两种主要本地存储方案:

方案 适用场景 本应用选择理由
Preferences 键值对,少量配置 不适合存储记录列表
relationalStore 结构化数据,SQL 查询 ✅ 需要按类型筛选、排序、统计

本应用需要频繁做「按类型查询」「按日期排序」「统计今日次数」等操作,用 RDB 一 条 SQL 就能搞定,比 Preferences 手写遍历高效得多。

3.5 DatabaseHelper 的核心实现

所有数据库操作集中在一个模块中,核心函数包括:

  • getDatabase() —— 懒汉单例,首次调用时建表,并自动插入默认宠物信息
  • getPetInfo() / updatePetInfo() —— 宠物信息的查改
  • addRecord() —— 插入新记录,返回 rowId
  • getRecords(type?) —— 按类型筛选(可选),按日期时间降序
  • getTodayFeedingCount() —— 统计当日喂食次数,首页仪表盘使用
  • getLatestRecordByType() —— 获取某类型最新一 条记录

关键代码片段(简化):

export async function getDatabase(context: common.Context): Promise<relationalStore.RdbStore> {
    if (store) return store; // 单例
    const config: relationalStore.StoreConfig = {
        name: 'pet_diary.db',
        securityLevel: relationalStore.SecurityLevel.S1
    };
    store = await relationalStore.getRdbStore(context, config);
    await store.executeSql(CREATE_TABLE_RECORDS);
    await store.executeSql(CREATE_TABLE_PET_INFO);
    // 自动初始化默认宠物
    const result = await store.querySql(`SELECT COUNT(*) as cnt FROM pet_info`);
    if (result.rowCount > 0) {
        result.goToFirstRow();
        if (result.getLong(0) === 0) {
            await store.executeSql(
                `INSERT INTO pet_info (name, species, birthday) VALUES (?, ?, ?)`,
                ['我的宠物', '未知', '']
            );
        }
    }
    result.close();
    return store;
}

💡 注意querySql() 接收 SQL 字符串 + 参数列表,返回 ResultSet;而 query() 接收的是 RdbPredicates 对象,两者不可混用。这是初学者容易踩的坑。


🎨 四、UI 设计与 ArkUI 实现

4.1 整体设计风格

应用采用 卡片式布局 + 柔和配色

  • 主色调:#5B8DEF(清新蓝)
  • 功能色:🍽️ #FF9F43 / 🛁 #54A0FF / 💉 #5F27CD / ⚖️ #00D2D3
  • 背景色:#F5F6FA(浅灰)
  • 卡片:纯白 + 轻微投影

4.2 首页仪表盘(Index.ets)

首页是用户打开应用第一眼看到的内容,承载了四个核心功能:

顶部区域
  • 应用名称 + 宠物名
  • 蓝色渐变背景
统计卡片(4 个)
卡片 数据来源 刷新时机
🍽️ 今日喂食 getTodayFeedingCount() 每次页面显示
🛁 最近洗澡 getLatestRecordByType('bathing') 每次页面显示
💉 最近疫苗 getLatestRecordByType('vaccine') 每次页面显示
⚖️ 最新体重 getLatestRecordByType('weight') 每次页面显示

这里使用了 ArkUI 的 @Builder 装饰器来复用卡片 UI:

@Builder
statCard(icon: string, value: string, color: string) {
    Column() {
        Text(icon).fontSize(24)
        Text(value).fontSize(13).fontColor('#333')
    }
    .width('45%')
    .padding(12)
    .backgroundColor(Color.White)
    .borderRadius(10)
    .shadow({ radius: 3, color: 'rgba(0,0,0,0.05)' })
}
宠物信息卡片

点击可跳转到编辑页面。使用 Blank() 组件实现左右分布 —— 左侧「宠物信息」标题,右侧「编辑 >」入口。

快捷操作按钮

4 个按钮对应 4 种记录类型,点击跳转到 AddRecord 页面并携带 recordType 参数:

router.pushUrl({
    url: 'pages/AddRecord',
    params: { recordType: RecordType.FEEDING }
});

⚠️ router.pushUrl 已在较新版本标记为 deprecated,建议关注 @ohos.router 的新 API 迁移。

最近记录列表

通过 ForEach 渲染 recentRecords 数组,每个记录项显示:类型图标 + 标题 + 简述 + 日期时间。

4.3 添加记录页面(AddRecord.ets)

表单页面的核心挑战是 根据记录类型动态调整 UI

方案:在 aboutToAppear 中读取 router.getParams() 获取类型,然后调用 updateUI() 更新标题和占位符:

updateUI(): void {
    switch (this.recordType) {
        case RecordType.FEEDING:
            this.title = '🍽️ 记录喂食';
            this.detailPlaceholder = '喂食内容(如:猫粮50g)';
            break;
        case RecordType.BATHING:
            this.title = '🛁 记录洗澡';
            this.detailPlaceholder = '洗澡方式(如:干洗/水洗)';
            break;
        // ... 省略类似代码
    }
}

表单包含 4 个字段:

  1. 日期:默认当天,可编辑
  2. 时间:默认当前时间,可编辑
  3. 详情:根据类型不同,placeholder 不同
  4. 备注:多行文本

点击「保存」调用 addRecord()router.back() 返回首页。

4.4 记录列表页面(RecordDetail.ets)

这是数据展示的核心页面,包含两个关键设计:

筛选逻辑
  • 默认显示「全部」记录
  • 点击「切换」展开筛选芯片:全部 / 喂食 / 洗澡 / 疫苗 / 体重
  • 使用 @State activeFilter 控制当前筛选状态
applyFilter(): void {
    if (this.activeFilter === 'all') {
        this.filteredRecords = this.allRecords;
    } else {
        this.filteredRecords = this.allRecords.filter(r => r.type === this.activeFilter);
    }
}
记录卡片

每个卡片以 Column 包裹,包含:

  • 类型标签(带颜色标识)+ 日期时间(右上)
  • 详情值(如「5.2kg」)
  • 备注文字(可选)

4.5 宠物信息编辑页面(PetInfo.ets)

三个输入框分别编辑名字、种类、生日。保存后返回首页,首页的 onPageShow() 会自动刷新数据。

这里有一个细节:首页使用了 onPageShow() 生命周期而不是 aboutToAppear(),因为 aboutToAppear 只在页面创建时触发一 次,而 onPageShow 每次页面可见都会触发。这样从编辑页返回后,首页数据会自动更新。

onPageShow(): void {
    this.loadData(); // 每次回到首页都刷新数据
}

🔍 五、踩坑与解决方案

5.1 ❌ query() vs querySql() 的误解

问题:直接传 SQL 字符串给 store.query(),报错 Argument of type 'string' is not assignable to parameter of type 'RdbPredicates'

原因:HarmonyOS 的 RdbStore 有两个不同的查询方法:

方法 第一参数 第二参数
query() RdbPredicates string[] (列名)
querySql() string (SQL) ValueType[] (参数)

解决:所有传入 SQL 字符串的地方改为 querySql()

5.2 ❌ justifyContent / alignItems 误用于 Text

问题:给 Text 组件设置了 .justifyContent().alignItems()

原因:这两个属性是容器组件(Row / Column)专有,Text 是基础组件,没有这些方法。

解决:在 Text 外面嵌套一 层 Row 作为容器:

Row() {
    Text('🍽️').fontSize(22)
}
.width(44).height(44)
.borderRadius(22)
.backgroundColor('#F0F4FF')
.justifyContent(FlexAlign.Center)   // ✅ Row 可以
.alignItems(VerticalAlign.Center)   // ✅ Row 的 alignItems 用 VerticalAlign

5.3 ❌ Row 的 alignItems 参数类型

问题Row.alignItems(HorizontalAlign.Center) 报类型错误。

原因Row 是水平方向容器,它的 alignItems 控制的是垂直方向的对齐,所以参数类型必须是 VerticalAlign,而不是 HorizontalAlign

类比记忆:

容器 alignItems 控制方向 参数类型
Row 垂直方向(交叉轴) VerticalAlign
Column 水平方向(交叉轴) HorizontalAlign

5.4 ❌ 系统颜色资源不存在

问题$r('sys.color.ohos_id_color_complementary') 报资源找不到。

原因ohos_id_color_complementaryohos_id_color_success 在目标 API 版本中不存在。

解决:统一使用十六进制色值字符串代替系统资源引用:

backgroundColor('#5F27CD')  // ✅ 用字符串
backgroundColor($r('sys.color.ohos_id_color_complementary'))  // ❌ 不存在

5.5 ⚠️ app_name 资源冲突

问题:编译警告 'app_name' conflict, first declared in AppScope

原因app_name 应该在 AppScope/resources/base/element/string.json 中定义,但在 entry 模块中又定义了一 次。

解决:删除 entry 模块中的 app_name 定义,只保留 AppScope 中的。


📊 六、数据流与生命周期

6.1 页面数据流

┌─────────┐   router.pushUrl(params)   ┌────────────┐
│  Index   │ ───────────────────────▶  │ AddRecord   │
│ (首页)   │                           │ (添加页)    │
└────┬────┘                           └──────┬─────┘
     │ onPageShow()                          │ save + back
     ▼                                       ▼
┌─────────┐                           ┌────────────┐
│ Database │ ◀──────────────────────  │ Database    │
│ 层 RDB   │                          │ 层 RDB      │
└─────────┘                           └────────────┘

所有页面通过 Database 层与 RDB 交互,页面之间不直接传递数据对象,只传递参数(如 recordType)。

6.2 页面生命周期

页面 数据加载时机 刷新机制
Index onPageShow() 每次返回首页自动刷新
AddRecord aboutToAppear() 仅创建时加载
RecordDetail onPageShow() 每次返回列表自动刷新
PetInfo aboutToAppear() 仅创建时加载

🧪 七、构建与部署

7.1 构建命令

# 调试构建
hvigorw --mode module -p module=entry assembleHap --no-daemon

# 发布构建(需配置签名)
hvigorw --mode module -p module=entry assembleHap --no-daemon -p buildMode=release

7.2 签名配置

build-profile.json5 中配置 signingConfigs

{
  "app": {
    "signingConfigs": [{
      "name": "default",
      "material": {
        "certpath": "路径/xxx.cer",
        "keypair": "路径/xxx.p7b",
        "profile": "路径/xxx.p7b"
      }
    }]
  }
}

未配置签名时构建会跳过签名步骤,生成的 HAP 只能在调试模式下运行。

7.3 DevEco Studio 真机运行

  1. 用 USB 连接鸿蒙设备,开启开发者模式
  2. DevEco Studio 选择设备后点击 ▶️ 运行
  3. 首次安装会自动创建数据库和默认宠物信息

🌟 八、优化方向与未来规划

当前版本已实现核心功能,后续可以从以下几个方向优化:

8.1 多宠物支持

  • pet_info 表增加多条记录
  • 首页增加宠物切换器
  • 所有查询增加 pet_name 筛选

8.2 图片记录

  • 使用 @ohos.multimedia.camera 拍照
  • 使用 @ohos.file.picker 从相册选择
  • 图片存为本地文件路径,RDB 只存路径

8.3 数据导出

  • 导出 JSON / CSV 到本地
  • 使用 @ohos.file.fs 写入文件
  • 通过 @ohos.share 分享

8.4 图表统计

  • 使用 Canvas 或第三方图表库
  • 体重变化曲线
  • 喂食频率热力图

8.5 提醒通知

  • 定时提醒喂食/洗澡/疫苗
  • 使用 @ohos.backgroundTaskManager 后台任务
  • 使用 @ohos.notification 推送通知

📝 九、总结

9.1 项目收获

通过这个项目,完整实践了 HarmonyOS 应用开发的完整流程:

  1. Stage 模型:理解了 UIAbility + 页面路由的生命周期管理
  2. ArkUI 声明式 UI:掌握了 @State@BuilderForEach 等核心装饰器
  3. relationalStore:熟练使用 RDB 进行增删改查,理解了 queryquerySql 的区别
  4. 页面导航:掌握了 router.pushUrl / router.back / router.getParams 的传参方式
  5. 调试技巧:通过 hvigor 编译错误定位问题,快速修复类型和 API 使用错误

9.2 数据

  • 代码量:约 650 行 ArkTS
  • 页面数:4 个主页面
  • 数据表:2 张
  • API 调用:8 个 Database 操作函数
  • 编译时间:约 8 秒(含预热)

9.3 感受

HarmonyOS 的 ArkTS + ArkUI 组合在开发体验上非常接近 SwiftUI 和 Jetpack Compose,声明式语法上手快,组件丰富。但 API 文档的完整性和社区资源相比 Android 仍有差距,部分系统资源在不同 API 版本中存在差异,需要实际编译验证。

不过,鸿蒙生态正在快速完善。从 API 12 开始,@kit.* 模块化导入规范了 API 组织方式,开发体验有了明显提升。对于个人开发者和小团队来说,开发一个纯粹、好用的鸿蒙原生应用,现在正当时。


项目名称:宠物成长日记(Pet Growth Diary)
开发平台:HarmonyOS 6.1.0 (API 12)
源码结构:Stage Model + ArkTS + RDB
功能特性:喂食记录、洗澡记录、疫苗记录、体重记录、宠物信息管理
GitHub(可根据需要托管)


在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐