一、应用概述
备忘录应用是移动设备上最经典、最基础的生产力工具之一。本文将以 HarmonyOS 原生开发框架 ArkTS 为基础,详细解析一个轻量级备忘录应用(Quick Memo)的完整开发过程。该应用支持用户快速记录文字内容,自动添加时间戳,并具备添加与删除备忘录的核心功能。
1.1 功能特性
- 快速记录:用户可以在输入框中输入任意文本内容,点击添加按钮即可生成一条备忘录记录
- 自动时间戳:每条备忘录在创建时自动记录当前时间,并按照
YYYY-MM-DD HH:mm的格式呈现 - 列表展示:所有备忘录以列表形式清晰展示,最新添加的记录排列在最上方
- 滑动删除:支持左滑或点击删除按钮移除指定备忘录
- 数据持久化:利用 HarmonyOS 的本地存储能力,确保重启应用后数据不丢失
1.2 适用场景
- 日常灵感记录
- 待办事项快速录入
- 购物清单管理
- 会议要点摘录
1.3 技术亮点
本应用虽然功能简洁,却完整展现了 ArkTS 声明式 UI 开发的核心理念,包括组件状态管理、列表渲染、事件绑定、数据持久化等关键技术点,是 ArkTS 入门学习的绝佳案例。
二、技术架构
2.1 整体架构概览
本应用采用 HarmonyOS 推荐的单页面(单 Ability)架构,以@Entry装饰的主页面作为唯一入口,内部通过@Component拆分为多个功能模块。整体架构分为三层:
┌─────────────────────────────────────┐ │ UI 表示层 │ │ (MainPage / MemoItem / AddMemoBar) │ ├─────────────────────────────────────┤ │ 状态管理层 │ │ (@State / @Prop / @Link 装饰器) │ ├─────────────────────────────────────┤ │ 数据持久层 │ │ (Preferences / AppStorage) │ └─────────────────────────────────────┘2.2 核心数据结构
备忘录的数据模型定义如下:
interface Memo { id: string; // 唯一标识符 content: string; // 备忘录内容 timestamp: number; // 创建时间(时间戳) }每个Memo对象通过id字段实现唯一标识,确保在删除和更新操作中能精确定位目标记录。timestamp字段存储 Unix 时间戳,便于后续按时间排序和格式化显示。
2.3 组件树结构
@Entry MainPage ├── Column (主容器) │ ├── Text (标题栏) │ ├── Row (输入区域) │ │ ├── TextInput (文本输入框) │ │ └── Button (添加按钮) │ └── List (备忘录列表) │ └── ForEach → MemoItem (自定义列表项) │ ├── Text (内容) │ ├── Text (时间戳) │ └── Button (删除)三、核心代码分析
3.1 数据模型与状态管理
备忘录的数据模型使用interface定义,确保类型的强约束性。所有备忘录数据通过@State装饰器管理,当数据发生变化时,框架自动重新渲染 UI。
// 备忘录数据模型 interface Memo { id: string; content: string; timestamp: number; } @Entry @Component struct MemoApp { // 使用 @State 管理备忘录列表,驱动 UI 更新 @State private memoList: Memo[] = []; @State private inputContent: string = ''; // 格式化时间戳为可读字符串 private formatTime(timestamp: number): string { const date = new Date(timestamp); const year = date.getFullYear(); const month = String(date.getMonth() + 1).padStart(2, '0'); const day = String(date.getDate()).padStart(2, '0'); const hour = String(date.getHours()).padStart(2, '0'); const minute = String(date.getMinutes()).padStart(2, '0'); return `${year}-${month}-${day} ${hour}:${minute}`; } }@State是 ArkTS 中最基础的状态装饰器。当memoList的内容发生变化(增删改)时,所有依赖于该数据的 UI 组件会自动重新渲染。这种声明式编程方式大幅简化了传统的命令式 DOM 操作,让开发者可以专注于业务逻辑本身。
3.2 添加备忘录功能
备忘录的添加操作需要完成三步工作:生成唯一 ID、获取当前时间戳、将新记录插入列表头部。
// 添加备忘录 addMemo() { if (this.inputContent.trim() === '') { // 空内容不添加,提升用户体验 return; } // 生成唯一 ID const newMemo: Memo = { id: Date.now().toString() + Math.random().toString(36).substr(2, 9), content: this.inputContent.trim(), timestamp: Date.now() }; // 插入到列表头部(最新在上) this.memoList.unshift(newMemo); // 清空输入框 this.inputContent = ''; // 持久化存储 this.saveData(); }关键设计考量:
- ID 生成策略:使用
Date.now()结合随机数,确保在多设备、多线程环境下 ID 的唯一性 - 空值校验:在添加前校验内容是否为空,避免产生无意义的空记录
- 头部插入:使用
unshift而非push,保证最新记录显示在列表最上方 - 自动清空:添加成功后立即清空输入框,方便用户连续记录
3.3 删除备忘录功能
删除操作提供两种交互方式:点击删除按钮和左滑删除手势。
// 删除备忘录 deleteMemo(memoId: string) { // 根据 ID 过滤出要保留的记录 this.memoList = this.memoList.filter(item => item.id !== memoId); // 同步持久化数据 this.saveData(); } // UI 中的删除按钮 Button('删除') .fontColor(Color.Red) .fontSize(14) .onClick(() => { this.deleteMemo(item.id); })使用数组的filter方法删除记录是一种函数式编程的实践,它不会修改原数组,而是返回一个新数组。ArkTS 的状态管理机制能检测到这种变化并触发 UI 更新。
3.4 列表渲染与 ForEach
备忘录列表使用ForEach指令进行循环渲染,这是 ArkTS 中最常用的列表渲染方式。
List({ space: 12 }) { ForEach(this.memoList, (item: Memo, index: number) => { ListItem() { // 自定义备忘录卡片 Column() { Text(item.content) .fontSize(16) .fontWeight(FontWeight.Medium) .width('100%') .textAlign(TextAlign.Start) Text(this.formatTime(item.timestamp)) .fontSize(12) .fontColor(Color.Gray) .width('100%') .textAlign(TextAlign.End) .margin({ top: 8 }) // 删除按钮 Button('删除') .fontColor(Color.Red) .fontSize(14) .onClick(() => { this.deleteMemo(item.id); }) .alignSelf(ItemAlign.End) .margin({ top: 4 }) } .padding(16) .backgroundColor(Color.White) .borderRadius(12) .shadow({ radius: 4, color: 'rgba(0, 0, 0, 0.08)', offsetX: 0, offsetY: 2 }) .width('100%') } // 添加滑动删除支持 .swipeAction({ end: { builder: () => { Button('删除') .width(80) .height('100%') .backgroundColor(Color.Red) .fontColor(Color.White) .onClick(() => { this.deleteMemo(item.id); }) } } }) }, (item: Memo) => item.id) }ForEach 的 key 生成器:第三个参数(item: Memo) => item.id是 key 生成器,它告诉框架如何唯一标识每个列表项。使用item.id作为 key 可以确保列表在增删操作时能够高效地复用和重排组件,避免不必要的全量重建。
3.5 数据持久化
利用 HarmonyOS 的 Preferences 轻量级键值数据库实现数据持久化:
import preferences from '@ohos.data.preferences'; // 保存数据到本地 async saveData() { const context = getContext(this); const pref = await preferences.getPreferences(context, 'memo_db'); // 将对象数组转换为 JSON 字符串存储 await pref.put('memo_list', JSON.stringify(this.memoList)); await pref.flush(); } // 从本地加载数据 async loadData() { const context = getContext(this); const pref = await preferences.getPreferences(context, 'memo_db'); const jsonStr = await pref.get('memo_list', '[]'); this.memoList = JSON.parse(jsonStr as string); }持久化流程:
- 通过
getPreferences获取或创建数据库实例 - 使用
put方法存储序列化后的数据 - 调用
flush确保数据写入磁盘 - 读取时使用
get方法获取 JSON 字符串,再反序列化为对象数组
四、HarmonyOS关键技术应用
4.1 声明式 UI 与状态管理
HarmonyOS 的 ArkUI 框架采用了声明式 UI 范式,这与传统的命令式 UI 有本质区别。在声明式 UI 中,开发者只需描述 UI 的最终状态,框架负责处理状态变化后的 UI 更新。
| 特性 | 传统命令式 | 声明式(ArkTS) |
|---|---|---|
| 编程方式 | 手动操作 DOM | 描述 UI 状态 |
| 状态同步 | 手动同步 | 自动追踪 |
| 代码量 | 较多 | 精简 |
| 可维护性 | 一般 | 优秀 |
@State 装饰器是状态管理的核心,其工作原理可概括为:
- 开发者用
@State标记一个变量为响应式数据 - 当该变量的值发生变化时,框架自动标记所有依赖它的组件为"脏"状态
- 框架在下一个渲染周期中,重新执行所有脏组件的
build方法 - 通过虚拟 DOM 的 diff 算法,仅更新实际变化的 UI 部分
4.2 List 组件的性能优化
List组件是 HarmonyOS 中高效的列表容器,它内置了以下优化机制:
- 懒加载(Lazy Loading):只渲染可见区域的列表项,不可见区域的内容不会创建对应的组件实例
- 组件复用(Component Reuse):当用户滚动列表时,移出屏幕的组件实例会被回收并复用给新进入屏幕的列表项
- 缓存池(Cache Pool):在可见区域上下各预缓存一定数量的列表项,确保快速滚动时的流畅体验
List({ space: 12, scroller: this.scroller }) { // 列表内容 } .layoutWeight(1) .listDirection(Axis.Vertical) .edgeEffect(EdgeEffect.Spring) .scrollBar(BarState.Off)4.3 自定义组件封装
将备忘录卡片封装为独立的@Component,提升了代码的复用性和可维护性:
@Component struct MemoItem { private memo: Memo; private onDelete: () => void; build() { Column() { Text(this.memo.content) .fontSize(16) .fontWeight(FontWeight.Medium) Text(this.formatTime(this.memo.timestamp)) .fontSize(12) .fontColor(Color.Gray) .margin({ top: 8 }) Button('删除') .fontColor(Color.Red) .fontSize(14) .onClick(() => { this.onDelete(); }) } .padding(16) .backgroundColor(Color.White) .borderRadius(12) } private formatTime(timestamp: number): string { // 时间格式化逻辑 } }自定义组件的优势:
- 封装性:将 UI 结构和样式封装在组件内部,对外只暴露必要的属性和回调
- 复用性:同一组件可在不同页面或同一页面的不同位置重复使用
- 可测试性:独立组件便于进行单元测试和 UI 测试
五、UI设计与交互
5.1 视觉设计
本应用的视觉设计遵循 Material Design 设计规范,同时融入 HarmonyOS 的设计语言特点:
配色方案:
- 主色调:系统蓝色(
#0A59F7) - 背景色:浅灰色(
#F5F5F5) - 卡片背景:纯白色(
#FFFFFF) - 文字色:深灰色(
#333333) - 辅助色:灰色(
#999999)用于时间戳 - 警示色:红色(
#FF0000)用于删除操作
卡片设计:
每条备忘录以卡片形式呈现,圆角 12px,带有微弱的阴影效果,营造出层次感和立体感。卡片内左上方显示备忘录内容,右下方显示创建时间,布局清晰直观。
5.2 交互反馈
- 添加动画:新备忘录添加时,列表项以淡入动画呈现
- 删除反馈:删除按钮点击后,列表项以淡出动画消失
- 滑动删除:列表项支持左滑手势,露出删除按钮,提升操作效率
- 空状态提示:当列表为空时,显示"暂无备忘录"的友好提示
5.3 自适应布局
应用使用 ArkTS 的弹性布局(Flex和Column)实现自适应,在不同屏幕尺寸下均有良好表现。
Column() { // 标题区域 Text('备忘录') .fontSize(24) .fontWeight(FontWeight.Bold) .width('100%') .padding(16) // 输入区域 Row() { TextInput({ placeholder: '输入备忘录内容...' }) .layoutWeight(1) .height(48) .backgroundColor(Color.White) .borderRadius(8) Button('添加') .width(64) .height(48) .margin({ left: 12 }) .backgroundColor('#0A59F7') .fontColor(Color.White) .borderRadius(8) } .padding({ left: 16, right: 16, bottom: 12 }) // 列表区域 List({ space: 12 }) { // ... } .layoutWeight(1) .padding(16) } .width('100%') .height('100%') .backgroundColor('#F5F5F5')六、性能优化与最佳实践
6.1 性能优化策略
6.1.1 减少不必要的重新渲染
在使用@State时,需要注意避免触发不必要的 UI 更新:
// ❌ 不良实践:每次添加都触发全列表重绘 this.memoList = [...this.memoList, newMemo]; // ✅ 最佳实践:使用数组方法并配合 key 生成器 this.memoList.unshift(newMemo); // ForEach 配合唯一 key 可精确定位需要更新的项6.1.2 合理使用计算属性
对于需要复杂计算的数据,使用@Computed或 getter 方法缓存计算结果:
// 使用 getter 计算统计数据 get totalMemos(): number { return this.memoList.length; } get todayMemos(): number { const today = new Date().toDateString(); return this.memoList.filter(item => new Date(item.timestamp).toDateString() === today ).length; }6.1.3 数据持久化优化
避免频繁的磁盘写入操作,可以采用防抖(Debounce)策略:
private saveTimer: number | null = null; debounceSave() { if (this.saveTimer) { clearTimeout(this.saveTimer); } this.saveTimer = setTimeout(() => { this.saveData(); this.saveTimer = null; }, 500); // 500ms 防抖 }6.2 最佳实践总结
6.2.1 代码组织规范
src/ ├── main/ │ ├── ets/ │ │ ├── pages/ │ │ │ └── Index.ets # 主页面 │ │ ├── components/ │ │ │ ├── MemoItem.ets # 备忘录卡片组件 │ │ │ └── AddMemoBar.ets # 添加栏组件 │ │ ├── model/ │ │ │ └── MemoModel.ets # 数据模型定义 │ │ └── utils/ │ │ └── DateUtil.ets # 工具函数 │ └── resources/ │ └── ...6.2.2 状态提升原则
将共享状态提升到共同的父组件中,子组件通过@Prop或@Link接收数据和回调:
// 父组件管理状态 @State memoList: Memo[] = []; build() { Column() { AddMemoBar({ onAdd: (content) => this.addMemo(content) }) MemoList({ memos: this.memoList, onDelete: (id) => this.deleteMemo(id) }) } }6.2.3 错误处理与边界情况
// 处理空列表情况 if (this.memoList.length === 0) { // 显示空状态 Image($r('app.media.empty_illustration')) .width(200) .height(200) Text('暂无备忘录') .fontColor(Color.Gray) .fontSize(16) } // 处理输入过长的情况 if (this.inputContent.length > 500) { showToast('备忘录内容不能超过500字'); return; }七、总结与扩展思路
7.1 项目总结
本文详细解析了基于 HarmonyOS ArkTS 框架开发的备忘录应用。通过本项目的学习,开发者可以掌握以下关键技术点:
- ArkTS 声明式 UI 开发:理解
@State、@Prop、@Link等装饰器的工作原理和使用场景 - 列表渲染与交互:掌握
List+ForEach的高效列表渲染方案 - 数据持久化:熟悉 HarmonyOS Preferences 轻量级数据库的使用
- 自定义组件设计:学会将 UI 拆分为可复用的自定义组件
- 交互体验优化:掌握动画、手势、反馈等交互设计要点
7.2 扩展思路
本应用作为基础版本,可以从以下几个方面进行扩展和增强:
7.2.1 功能增强
- 分类管理:为备忘录添加分类标签,支持按分类筛选
- 搜索功能:实现关键词搜索,快速定位目标记录
- 富文本编辑:支持 Markdown 语法或富文本格式
- 图片附件:支持为备忘录添加图片或语音附件
- 云同步:利用 HarmonyOS 的分布式能力实现多设备数据同步
7.2.2 交互升级
- 长按拖拽排序:通过拖拽手势改变备忘录顺序
- 暗黑模式适配:响应系统暗黑模式,提供舒适的夜间使用体验
- 小组件(Widget):开发 HarmonyOS 卡片小组件,在桌面直接查看和添加备忘录
- 语音输入:集成语音识别能力,支持语音转文字记录
7.2.3 技术进阶
- 原子化服务:将应用改造为 HarmonyOS 原子化服务,免安装即用
- 跨端移植:利用 ArkTS 的多平台适配能力,将应用移植到平板和折叠屏设备
- 性能监控:接入性能监控 SDK,持续优化应用性能
- 单元测试:为关键业务逻辑编写单元测试,确保代码质量
7.3 学习建议
对于 HarmonyOS 初学者,建议按照以下路径学习本应用相关的技术点:
- 先理解
@State状态管理机制,这是 ArkTS 开发的核心 - 掌握
List组件的使用,包括懒加载和组件复用 - 学习
ForEach的 key 生成器原理 - 理解自定义组件的生命周期和通信方式
- 最后研究数据持久化和性能优化方案
通过本项目的学习,开发者可以建立起 ArkTS 应用开发的基本框架思维,为进一步开发更复杂的 HarmonyOS 应用打下坚实基础。
项目代码已完整开源,开发者可以基于本文的代码示例进行二次开发和创新。HarmonyOS 生态正快速发展,掌握 ArkTS 开发技能将为开发者打开通往万物互联时代的大门。