news 2026/7/30 1:40:49

# 鸿蒙 HarmonyOS 应用开发实战(第35期)|情绪追踪(Mood Tracker)— emoji 情感选择与历史记录

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
# 鸿蒙 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依赖的是引用变化——只有当数组引用发生变化时,框架才会重新渲染列表。

正确流程:

  1. this.entries.concat()创建数组的浅拷贝
  2. 在拷贝上修改数据(更新已有条目或unshift新条目)
  3. 将新数组赋值给this.entries,触发 UI 更新

更新 vs 新增的逻辑:

  • 如果今日已有记录 → 替换为新的情绪和备注
  • 如果今日尚无记录 → 使用unshift插入到数组最前面(最新的在最上面)

unshiftpush更适合这个场景,因为用户最关心最近的情绪记录。

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) }

List+ListItem是 HarmonyOS 中构建可滚动列表的推荐方式。相较于在Scroll中使用ColumnForEachList具有懒加载(只渲染可见项)和回收复用(移出视口的列表项被回收)的优势,在处理大量数据时性能更优。

注意这里设置了List.height(100)——限制了列表高度为 100px,当历史记录超出时自动启用滚动。

四、HarmonyOS 特性分析

4.1 ForEach 的高阶用法

ForEach的第三个参数是key 生成函数,用于标识列表项的唯⼀性,帮助框架在列表更新时精准定位变更项。如果未提供,框架使用默认的索引 key。虽然本例没有传第三个参数(性能上无大问题,因为记录通常不多),但在动态增删列表中建议提供 key:

ForEach(this.entries, (entry) => { ... }, (entry) => entry.date + entry.emoji) // 用日期+emoji 作为唯一 key

4.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 今日/历史分离

将"今日情绪"以大卡片形式突出显示,而"历史记录"以紧凑列表形式呈现,这是一种信息层级分离的设计策略。用户关注的核心是「今天怎么样」,历史记录是辅助参考。

六、可扩展思路

  1. 图表趋势分析:使用@ohos.chart组件绘制情绪趋势折线图,让用户直观看到情绪变化。
  2. 情绪标签:允许用户自定义情绪标签(如「焦虑」「兴奋」),扩展五种固定情绪。
  3. 数据持久化:使用@ohos.data.preferences或关系型数据库保存情绪记录,避免页面刷新丢失。
  4. 导出分享:支持将情绪记录导出为图片或文本,与心理咨询师分享。
  5. 推送提醒:每天晚上定时推送通知,提醒用户记录今日情绪。

七、总结

情绪追踪应用以 142 行代码,实现了一个完整的情绪日记系统。它的技术亮点包括:

  • interface 类型定义MoodItemMoodEntry接口确保数据结构的一致性
  • ForEach 列表渲染:遍历情绪选项和历史记录,动态生成 UI
  • 不可变数据更新concat()创建数组副本后再修改,触发响应式渲染
  • List + ForEach 组合:构建可滚动的历史记录列表
  • 条件渲染:根据今日是否已记录展示不同 UI
  • 计算属性分离逻辑todaytodayEntryhistoryEntries三个 getter 各司其职

这个应用的代码架构——接口定义 + 数组状态 + 不可变更新 + 列表渲染——是 ArkTS 中 CRUD(增删改查)类应用的标准范式,可以复用到日记、笔记、待办事项等各类应用中。

下一期我们将转向阅读领域,探讨一个文化气息浓厚的应用——阅读清单(Reading List),看如何用三态流转管理你的书单。


本文是 HarmonyOS 应用开发实战系列第 35 期,更多内容请关注专栏。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/30 1:39:58

Python循环与异常处理的最佳实践与性能优化

1. 循环结构与异常处理的核心逻辑在编程实践中&#xff0c;for循环和异常捕获是两个看似独立却经常需要配合使用的关键机制。for循环作为最基础的流程控制结构&#xff0c;负责对可迭代对象进行遍历操作&#xff1b;而异常捕获则是保证程序健壮性的安全网&#xff0c;两者结合使…

作者头像 李华
网站建设 2026/7/30 1:39:15

空调PTC电辅热原理与使用指南:制热效果与耗电实测

1. 先搞清楚 PTC 电辅热到底解决空调制热的什么问题空调制热时&#xff0c;室外温度越低&#xff0c;传统热泵从室外吸热就越困难。很多人在冬天开空调会发现制热效果差、升温慢&#xff0c;甚至室外机结霜导致频繁停机除霜。PTC 电辅热就是在这种场景下介入的辅助加热方案。它…

作者头像 李华
网站建设 2026/7/30 1:36:21

构建AI就绪的数据策略:企业在规模化人工智能前必须把握的关键要素

在企业规模化部署人工智能的进程中&#xff0c;数据策略的“就绪度”已成为决定成败的关键分水岭。尽管97%的企业已启动AI项目&#xff0c;但一项调查显示&#xff0c;仅有5%的企业认为其数据已为规模化AI做好准备。构建一个AI就绪的数据策略&#xff0c;核心在于完成从“有数据…

作者头像 李华
网站建设 2026/7/30 1:35:54

基于ESP32与OLED的自制蓝牙HUD:从硬件选型到软件实现的完整指南

1. 项目缘起&#xff1a;从“鸡肋”到“真香”的HUD自制之路 几年前&#xff0c;我开车时总忍不住低头瞥一眼手机导航&#xff0c;这个动作有多危险&#xff0c;每个司机都懂。后来市面上出现了所谓的“HUD抬头显示器”&#xff0c;买回来一个&#xff0c;发现要么是简单的OBD…

作者头像 李华
网站建设 2026/7/30 1:33:53

STM32 CAN回环测试实战:CubeMX配置与HAL库驱动详解

1. 从零开始&#xff1a;为什么CAN回环测试是嵌入式开发的“第一课”如果你刚开始接触STM32的CAN总线&#xff0c;或者用CubeMX和HAL库做项目时&#xff0c;面对CAN通信心里没底&#xff0c;那这篇文章就是为你准备的。我见过不少新手&#xff0c;包括当年的我自己&#xff0c;…

作者头像 李华
网站建设 2026/7/30 1:31:25

AI 应用落地的下一个爆发点:从聊天到执行,从内容到决策

AI 应用落地的下一个爆发点&#xff1a;从聊天到执行&#xff0c;从内容到决策 一、个性化深度引言 ChatGPT发布三年半&#xff0c;对话式AI已经无处不在。但一个趋势被很多人忽略了&#xff1a;对话只是AI的"交互界面"&#xff0c;不是AI的"价值核心"。…

作者头像 李华