# 鸿蒙 HarmonyOS 应用开发实战(第35期)|情绪追踪(Mood Tracker)— emoji 情感选择与历史记录
一、应用概述情绪追踪Mood Tracker是一款日记式的情绪记录应用。用户每天可以从五种情绪开心、不错、一般、低落、难过中选择一种并添加文字备注然后保存到历史记录中。应用支持当天情绪覆盖更新并以列表形式展示历史情绪记录。在快节奏的现代生活中情绪记录帮助人们觉察自己的情绪变化趋势是心理健康自我管理的有力工具。从技术角度来看本应用是ForEach列表渲染、interface接口定义、条件渲染和不可变数据更新的综合实战案例展示了如何在 ArkTS 中构建一个中等复杂度的数据管理系统。二、技术架构与组件树2.1 整体架构Entry Component struct index35 ├── Column (根容器) │ ├── Row (顶部导航栏) │ └── Column (主内容区, 居中) │ ├── Text (今天 M/d) │ ├── if (todayEntry) → 今日情绪展示卡片 │ │ ├── Text (emoji, 64px) │ │ ├── Text (情绪名) │ │ └── Text (备注文字, 条件) │ ├── if (!todayEntry) → 提示文字 │ ├── Row (emoji 选择器, ForEach) │ │ └── ForEach → Column × 5 │ │ ├── Text (emoji) │ │ └── Text (情绪名) │ ├── TextInput (备注输入) │ ├── Button ( 保存) │ └── if (historyEntries.length 0) │ ├── Text (历史记录) │ └── List (历史列表) │ └── ForEach → ListItem → Row │ ├── Text (日期) │ ├── Text (emoji) │ ├── Text (情绪名) │ └── Text (备注, 条件)2.2 数据接口定义interface MoodItem { emoji: string; name: string; color: string; } interface MoodEntry { date: string; emoji: string; name: string; note: string; } const MOODS: MoodItem[] [ { emoji: , name: 开心, color: #4CAF50 }, { emoji: , name: 不错, color: #8BC34A }, { emoji: , name: 一般, color: #FF9800 }, { emoji: , name: 低落, color: #FF6B6B }, { emoji: , name: 难过, color: #F44336 }, ];interface是 ArkTS 定义数据结构的核心方式。MoodItem定义了情绪选项的结构emoji、名称、颜色MoodEntry定义了每日情绪记录的结构日期、emoji、名称、备注。使用接口类型可以确保数据的结构一致性并在编译时捕获类型错误。MOODS是一个const数组常量定义了五种预设情绪。五种情绪按积极→中性→消极排列颜色从绿色渐变到红色语义明确。三、核心代码深度解析3.1 今日情绪检测get today(): string { const now new Date(); return (now.getMonth() 1) / now.getDate(); } get todayEntry(): MoodEntry | undefined { for (let i 0; i this.entries.length; i) { if (this.entries[i].date this.today) { return this.entries[i]; } } return undefined; }today计算属性返回格式化的今日日期如7/24。注意JavaScript 的getMonth()返回 0-11 的月份值需要 1 转为 1-12。todayEntry遍历所有记录查找日期等于今日的条目。如果找到则返回该条目否则返回undefined。这里使用MoodEntry | undefined联合类型——这是 ArkTS 中处理可能不存在的标准方式。3.2 历史记录过滤get historyEntries(): MoodEntry[] { const result: MoodEntry[] []; for (let i 0; i this.entries.length; i) { if (this.entries[i].date ! this.today) { result.push(this.entries[i]); } } return result; }historyEntries过滤出所有非今日的记录。与todayEntry互补两者合起来覆盖了全部记录。这种今日/历史的二分法让 UI 可以分别展示今日情绪大卡片和历史记录列表。3.3 保存情绪——不可变数据更新saveMood(): void { const mood MOODS[this.selectedMood]; const newEntries: MoodEntry[] this.entries.concat(); let found false; for (let i 0; i newEntries.length; i) { if (newEntries[i].date this.today) { newEntries[i] { date: this.today, emoji: mood.emoji, name: mood.name, note: this.note }; found true; break; } } if (!found) { newEntries.unshift({ date: this.today, emoji: mood.emoji, name: mood.name, note: this.note }); } this.entries newEntries; this.note ; }不可变数据更新是 ArkTS 响应式编程中最重要的概念之一。为什么不能直接修改this.entries[i]因为 ArkTS 的State依赖的是引用变化——只有当数组引用发生变化时框架才会重新渲染列表。正确流程this.entries.concat()创建数组的浅拷贝在拷贝上修改数据更新已有条目或unshift新条目将新数组赋值给this.entries触发 UI 更新更新 vs 新增的逻辑如果今日已有记录 → 替换为新的情绪和备注如果今日尚无记录 → 使用unshift插入到数组最前面最新的在最上面unshift比push更适合这个场景因为用户最关心最近的情绪记录。3.4 情绪选择器的交互设计Row({ space: 8 }) { ForEach(MOODS, (mood: MoodItem, idx: number) { Column() { Text(mood.emoji).fontSize(36) Text(mood.name).fontSize(12).fontColor(#888) } .padding(10) .backgroundColor(this.selectedMood idx ? #E8F5E9 : #fff) .borderRadius(12) .border({ width: this.selectedMood idx ? 2 : 0, color: mood.color }) .onClick(() { this.selectedMood idx }) }) }ForEach是 ArkTS 中遍历数组渲染列表的核心 API。语法为ForEach( arr: any[], (item: any, index?: number) void, // 内容生成函数 (item: any) string // 可选key 生成函数 )本例中ForEach遍历MOODS数组为每种情绪渲染一个 Column 卡片。选中状态的高亮逻辑背景色选中时是浅绿色#E8F5E9未选中是白色边框选中时有 2px 宽的对应情绪颜色边框未选中无边框这种高亮 色彩编码的组合让用户一目了然地知道当前选中的情绪。3.5 历史列表渲染if (this.historyEntries.length 0) { Text(历史记录).fontSize(14).fontColor(#888).margin({ top: 12 }) List() { ForEach(this.historyEntries, (e: MoodEntry) { ListItem() { Row() { Text(e.date).fontSize(14).fontColor(#888).width(40) Text(e.emoji).fontSize(24) Text(e.name).fontSize(14) if (e.note.length 0) { Text(e.note).fontSize(13).fontColor(#aaa).padding({ left: 8 }) } }.padding(8).backgroundColor(#fff).borderRadius(8).margin({ bottom: 4 }) } }) }.width(92%).height(100) }ListListItem是 HarmonyOS 中构建可滚动列表的推荐方式。相较于在Scroll中使用Column加ForEachList具有懒加载只渲染可见项和回收复用移出视口的列表项被回收的优势在处理大量数据时性能更优。注意这里设置了List.height(100)——限制了列表高度为 100px当历史记录超出时自动启用滚动。四、HarmonyOS 特性分析4.1 ForEach 的高阶用法ForEach的第三个参数是key 生成函数用于标识列表项的唯⼀性帮助框架在列表更新时精准定位变更项。如果未提供框架使用默认的索引 key。虽然本例没有传第三个参数性能上无大问题因为记录通常不多但在动态增删列表中建议提供 keyForEach(this.entries, (entry) { ... }, (entry) entry.date entry.emoji) // 用日期emoji 作为唯一 key4.2 List 组件的懒加载机制List组件在 HarmonyOS 中经过底层优化只渲染当前视口内的ListItem。对于本例中可能只有几十条记录的场景性能差异不明显但对于聊天消息成千上万条或无限滚动列表来说必须使用 List 而非 Column Scroll。4.3 条件渲染的链式判断if (this.todayEntry) { // 显示今日情绪卡片 } else { // 显示提示文字 }在 ArkTS 的build()中if/else条件渲染可以直接嵌入到 UI 树中。这种写法比三元表达式condition ? A : B在可读性上更优尤其当分支包含多个组件时。五、UI/UX 设计与最佳实践5.1 Emoji 作为交互元素本应用使用 emoji 作为情绪选择的核心交互元素这是情感化设计的典型应用。emoji 的优势跨越语言障碍全世界用户都能理解笑脸和哭脸降低认知负担选择 emoji 比选择文字描述更直观、更快速情感共鸣emoji 天然具有情感色彩比抽象图标更温暖5.2 视觉层级与色彩语义五种情绪的颜色从绿色到红色的渐变遵循了通用的色彩语义绿色系 → 积极情绪黄色/橙色 → 中性情绪红色系 → 消极情绪用户在潜意识中就能通过颜色判断情绪类别无需阅读文字。5.3 避免数据丢失的设计saveMood()中有一个关键细节保存后清空notethis.note 。这防止了用户在连续记录时旧备注意外保留到下一次记录中。5.4 今日/历史分离将今日情绪以大卡片形式突出显示而历史记录以紧凑列表形式呈现这是一种信息层级分离的设计策略。用户关注的核心是「今天怎么样」历史记录是辅助参考。六、可扩展思路图表趋势分析使用ohos.chart组件绘制情绪趋势折线图让用户直观看到情绪变化。情绪标签允许用户自定义情绪标签如「焦虑」「兴奋」扩展五种固定情绪。数据持久化使用ohos.data.preferences或关系型数据库保存情绪记录避免页面刷新丢失。导出分享支持将情绪记录导出为图片或文本与心理咨询师分享。推送提醒每天晚上定时推送通知提醒用户记录今日情绪。七、总结情绪追踪应用以 142 行代码实现了一个完整的情绪日记系统。它的技术亮点包括interface 类型定义MoodItem和MoodEntry接口确保数据结构的一致性ForEach 列表渲染遍历情绪选项和历史记录动态生成 UI不可变数据更新concat()创建数组副本后再修改触发响应式渲染List ForEach 组合构建可滚动的历史记录列表条件渲染根据今日是否已记录展示不同 UI计算属性分离逻辑today、todayEntry、historyEntries三个 getter 各司其职这个应用的代码架构——接口定义 数组状态 不可变更新 列表渲染——是 ArkTS 中 CRUD增删改查类应用的标准范式可以复用到日记、笔记、待办事项等各类应用中。下一期我们将转向阅读领域探讨一个文化气息浓厚的应用——阅读清单Reading List看如何用三态流转管理你的书单。本文是 HarmonyOS 应用开发实战系列第 35 期更多内容请关注专栏。