鸿蒙 ArkTS 实战:Family Truth Cards 从家庭真心话卡片到成绩记录完整解析
鸿蒙 ArkTS 实战:Family Truth Cards 从家庭真心话卡片到成绩记录完整解析
前言
Family Truth Cards 是一个面向 家庭真心话卡片 的鸿蒙 ArkTS 单页应用。项目代码集中在 Index.ets,通过 @State 管理记录列表、输入内容、声音开关和状态提示,并用 ForEach 把每一轮记录渲染为可勾选列表。
这篇文章直接基于项目源码展开,不写空泛介绍,重点分析它的数据模型、默认记录、统计函数、表单提交、列表刷新和后续扩展方式。对于学习 ArkTS 单页应用的人来说,这类项目很适合用来理解状态驱动 UI 的基本路径。
轻量小游戏或训练工具不一定先做复杂玩法。先把记录、统计、反馈和列表交互搭起来,后续接入真正的游戏逻辑会更稳。

配图展示的是开发者生态入口,发布时也可以替换成应用运行截图。本文关注的结构是:顶部指标、功能标签、声音开关、录入表单和记录列表。
一、项目定位与源码入口
1.1 应用定位
面向家庭互动场景,用问题分类、随机抽取、跳过规则和收藏问题管理每轮卡片体验。
当前页面不是复杂多页应用,而是一个清晰的单页面闭环:用户看到默认记录,输入新的轮次信息,点击按钮保存,再通过列表勾选需要关注的记录。
1.2 源码入口
核心文件位于 entry/src/main/ets/pages/Index.ets,入口组件使用 @Entry 与 @Component 标记。
@Entry
@Component
struct Index {
build() {
Scroll() {
Column({ space: 14 }) {
Text('Family Truth Cards')
}
}
}
}
| 区域 | 作用 | 对应能力 |
|---|---|---|
| 标题 | 呈现应用名称 | Family Truth Cards |
| 统计卡片 | 展示 Cards 与 Selected | 汇总与计数 |
| 标签区 | 展示玩法模块 | Question category, Random draw, Skip rule, Saved question |
| 表单 | 新增记录 | TextInput、Button |
| 列表 | 管理记录状态 | ForEach、Toggle |
二、数据模型设计
2.1 记录接口
项目使用 GameRound 描述每一条游戏记录。
interface GameRound {
title: string;
score: number;
note: string;
active: boolean;
}
这个接口非常适合成绩类应用。title 表示轮次或关卡,score 保存 卡片数量,note 用来保存补充描述,active 则控制是否被选中。
2.2 字段语义
| 字段 | 类型 | 在本项目中的含义 |
|---|---|---|
| title | string | 轮次、关卡或分类名称 |
| score | number | 卡片数量 |
| note | string | 结果说明、提示状态或扩展备注 |
| active | boolean | 是否作为重点记录 |
数据模型稳定 是这类项目的关键。后续即使增加真实玩法,也可以继续把最终结果写回同一个记录结构。
三、默认数据与业务语义
3.1 默认记录
页面初始化时已经准备两条业务记录。
@State rounds: GameRound[] = [
{ title: 'Warmup questions', score: 6, note: 'Skip rule 2', active: false },
{ title: 'Memory questions', score: 4, note: 'Saved favorite', active: false }
];
| 默认标题 | 数值 | 备注 | 是否选中 |
|---|---|---|---|
| Warmup questions | 6 | Skip rule 2 | false |
| Memory questions | 4 | Saved favorite | false |
3.2 默认数据的价值
默认数据让用户首次进入页面时就能理解这个应用要记录什么。比如 Warmup questions 和 Memory questions 分别对应不同训练或玩法场景,备注字段则补充了结果细节。
- 空列表会降低理解效率。
- 示例记录可以说明输入格式。
- 顶部统计从首次打开就有可读数值。
四、页面状态管理
4.1 状态字段
页面使用 @State 保存列表、表单、开关和提示文本。
@State rounds: GameRound[] = [];
@State titleText: string = '';
@State scoreText: string = '';
@State noteText: string = '';
@State soundOn: boolean = true;
@State status: string = 'Game ready';
4.2 状态流转
| 状态 | 触发动作 | 结果 |
|---|---|---|
rounds | 新增或勾选记录 | 列表和统计刷新 |
titleText | 输入轮次名称 | 提交时写入 title |
scoreText | 输入数值 | 转换为 score |
noteText | 输入备注 | 写入 note |
soundOn | 切换声音开关 | 保存声音偏好 |
status | 提交表单 | 展示错误或成功信息 |
ArkTS 的声明式体验重点在于状态变化。只要状态设计清楚,UI 更新就能自然跟随数据变化。
五、统计函数拆解
5.1 累计 Cards
顶部左侧指标来自 totalScore()。
private totalScore(): number {
let total = 0;
this.rounds.forEach((item: GameRound) => {
total += item.score;
});
return total;
}
5.2 计算选中数量
右侧 Selected 来自 selectedCount()。
private selectedCount(): number {
let count = 0;
this.rounds.forEach((item: GameRound) => {
if (item.active) {
count++;
}
});
return count;
}
这两个函数都只读列表状态,不修改任何数据,因此适合作为 UI 层的派生计算。
六、顶部指标卡片实现
6.1 卡片代码
顶部区域使用两个 Column 并排展示核心指标。
Row({ space: 10 }) {
Column({ space: 4 }) {
Text('Cards').fontSize(13).fontColor('#475569')
Text(String(this.totalScore())).fontSize(24).fontWeight(FontWeight.Bold).fontColor('#DC2626')
}.layoutWeight(1).padding(14).backgroundColor('#FEE2E2').borderRadius(8)
Column({ space: 4 }) {
Text('Selected').fontSize(13).fontColor('#475569')
Text(this.selectedCount() + '/' + this.rounds.length).fontSize(24).fontWeight(FontWeight.Bold).fontColor('#1D4ED8')
}.layoutWeight(1).padding(14).backgroundColor('#DBEAFE').borderRadius(8)
}
6.2 指标解释
| 指标 | 数据来源 | 产品含义 |
|---|---|---|
Cards | 所有记录的 score 汇总 | 衡量整体表现 |
| Selected | active 为 true 的记录数量 | 标记当前关注记录 |
| 总数 | rounds.length | 展示记录规模 |
对于 家庭真心话卡片,卡片数量 是最重要的结果数据,因此放在左侧主卡片。
七、功能标签区域
7.1 标签渲染
页面用四个短标签概括玩法能力。
Column({ space: 8 }) {
Row({ space: 8 }) {
Text('Question category').layoutWeight(1).fontSize(13).textAlign(TextAlign.Center)
Text('Random draw').layoutWeight(1).fontSize(13).textAlign(TextAlign.Center)
}
Row({ space: 8 }) {
Text('Skip rule').layoutWeight(1).fontSize(13).textAlign(TextAlign.Center)
Text('Saved question').layoutWeight(1).fontSize(13).textAlign(TextAlign.Center)
}
}
7.2 标签含义
| 序号 | 标签 | 说明 |
|---|---|---|
| 1 | Question category | 主玩法入口 |
| 2 | Random draw | 交互过程 |
| 3 | Skip rule | 结果反馈 |
| 4 | Saved question | 沉淀能力 |
功能标签的好处是信息密度高,不需要额外说明文字,也能让用户迅速理解页面能力。
八、声音开关与配置入口
8.1 开关代码
页面提供 Sound 开关,状态保存在 soundOn 中。
Row() {
Text('Sound').fontSize(16).fontColor('#374151')
Blank()
Toggle({ type: ToggleType.Switch, isOn: this.soundOn })
.onChange((checked: boolean) => this.soundOn = checked)
}
8.2 扩展方向
在当前版本中,soundOn 还没有接入音频系统,但它已经占好了配置位置。后续可以根据它控制按钮音效、答题反馈音、成功提示音或错误提醒音。
九、表单录入与校验
9.1 输入区结构
表单由名称、数值、备注和按钮组成。
Column({ space: 10 }) {
TextInput({ placeholder: 'Round name', text: this.titleText })
.onChange((value: string) => this.titleText = value)
TextInput({ placeholder: 'Score', text: this.scoreText })
.type(InputType.Number)
.onChange((value: string) => this.scoreText = value)
TextInput({ placeholder: 'Note', text: this.noteText })
.onChange((value: string) => this.noteText = value)
Button('Add score').height(42).width('100%').onClick(() => this.addRound())
}
9.2 校验规则
提交时会先检查名称和数值。名称不能为空,分数必须能转换成有效数字。
const score = Number(this.scoreText);
if (this.titleText.trim().length === 0 || !Number.isFinite(score)) {
this.status = 'Enter round and valid score';
return;
}
这个校验足够直接,能避免空标题和非法分数进入列表。
十、新增记录逻辑
10.1 保存方法
新增记录由 addRound() 完成。
private addRound(): void {
const score = Number(this.scoreText);
if (this.titleText.trim().length === 0 || !Number.isFinite(score)) {
this.status = 'Enter round and valid score';
return;
}
this.rounds = [{
title: this.titleText,
score: Math.round(score),
note: this.noteText.length > 0 ? this.noteText : 'No note yet',
active: false
}, ...this.rounds];
this.titleText = '';
this.scoreText = '';
this.noteText = '';
this.status = 'Round saved';
}
10.2 头部插入
新记录会插入到数组头部。这样用户提交后,最新数据立即出现在第一条,反馈路径很短。
十一、列表渲染与选中态
11.1 ForEach 渲染
列表渲染使用 ForEach 遍历 rounds。
ForEach(this.rounds, (item: GameRound, index: number) => {
Row() {
Toggle({ type: ToggleType.Checkbox, isOn: item.active })
.onChange(() => this.toggleActive(index))
Column({ space: 6 }) {
Text(item.title).fontSize(18).fontWeight(FontWeight.Bold)
Text(item.note).fontSize(14).fontColor('#475569')
}.layoutWeight(1).margin({ left: 8 })
Text(String(item.score)).fontSize(18).fontWeight(FontWeight.Bold).fontColor('#DC2626')
}.padding(14).backgroundColor(item.active ? '#DCFCE7' : '#FFFFFF').borderRadius(8)
})
11.2 勾选更新
切换勾选时,先修改当前项,再用展开语法生成新数组引用。
private toggleActive(index: number): void {
this.rounds[index].active = !this.rounds[index].active;
this.rounds = [...this.rounds];
}
这个写法保证列表 UI 能感知状态变化。选中的记录会使用 #DCFCE7 背景色,视觉反馈明确。
十二、布局与视觉层次
12.1 页面容器
外层使用 Scroll,内部使用 Column。
Scroll() {
Column({ space: 14 }) {
Text('Family Truth Cards')
// stats
// tags
// sound switch
// form
// list
}.padding(18).width('100%')
}.height('100%').backgroundColor('#F8FAFC')
12.2 配色表
| 颜色 | 使用位置 | 作用 |
|---|---|---|
#F8FAFC | 页面背景 | 保持浅色基底 |
#DC2626 | 主指标数值 | 强调成绩 |
#1D4ED8 | Selected 数值 | 强调选择状态 |
#DCFCE7 | 选中列表项 | 表达活跃状态 |
#F1F5F9 | 表单背景 | 区分输入区域 |
十三、运行验证流程
13.1 手工验证路径
可以按以下流程验证:
- 打开页面,确认标题是
Family Truth Cards。 - 查看默认两条记录是否出现。
- 点击
Add score且不输入内容,确认提示为Enter round and valid score。 - 输入
Daily round、99和备注,再点击保存。 - 勾选新记录,确认背景变绿且 Selected 数量增加。
this.titleText = 'Daily round';
this.scoreText = '99';
this.noteText = 'Manual record';
this.addRound();
// 预期:status 更新为 Round saved
// 预期:新记录位于列表第一项
13.2 验证要点
| 验证点 | 预期结果 |
|---|---|
| 空标题提交 | 显示错误提示 |
| 合法提交 | 新记录插入列表顶部 |
| 数值输入 | 保存为整数 score |
| 勾选记录 | active 取反 |
| 顶部统计 | 跟随列表变化 |
十四、面向真实玩法的扩展
14.1 业务扩展
后续可以加入问题库、随机抽卡动画、跳过次数限制、收藏列表和多人轮换。
14.2 工程扩展
后续扩展时,可以保持当前页面的状态结构不变,只把真实玩法结果接入 score 和 note:
interface PlayResult {
roundTitle: string;
finalScore: number;
summary: string;
}
function mapResultToRound(result: PlayResult): GameRound {
return {
title: result.roundTitle,
score: result.finalScore,
note: result.summary,
active: false
};
}
这样可以把玩法层和记录层分开,避免页面代码随着功能增加而失控。
十五、工程复盘
15.1 当前实现的优点
- 数据结构简单,字段语义清楚。
- 统计函数只依赖列表数据,逻辑可预测。
- 表单校验直接,错误提示明确。
- 列表更新使用新数组引用,刷新路径稳定。
- 功能标签贴合
家庭真心话卡片的业务方向。
15.2 可复用经验
这类结构可以复用到答题、训练、学习、聚会游戏、儿童教育和轻量工具中。只要业务结果可以表达为“标题 + 数值 + 备注 + 状态”,就能套入相同的记录管理骨架。
总结
Family Truth Cards 展示了一个完整的鸿蒙 ArkTS 小应用骨架:用 GameRound 定义记录,用 rounds 保存列表,用 totalScore() 和 selectedCount() 输出统计结果,用 addRound() 完成表单提交,再用 ForEach 和 Toggle 完成记录展示与选中状态更新。
它的代码不复杂,但工程链路完整。对于 家庭真心话卡片 这种玩法来说,当前版本已经完成了成绩记录层;后续只需要逐步补齐真实玩法,就能从原型平滑走向可用产品。
补充说明:当前页面把业务状态集中在组件内,适合小型训练工具快速迭代。
补充说明:记录列表采用头部插入,能让用户提交后立即看到最新结果。
补充说明:备注字段保留了足够弹性,可以承载提示、收藏、错误次数或扩展状态。
补充说明:统计卡片和列表项共享同一份数据,减少了重复状态带来的不一致风险。
补充说明:当前页面把业务状态集中在组件内,适合小型训练工具快速迭代。
补充说明:记录列表采用头部插入,能让用户提交后立即看到最新结果。
补充说明:备注字段保留了足够弹性,可以承载提示、收藏、错误次数或扩展状态。
补充说明:统计卡片和列表项共享同一份数据,减少了重复状态带来的不一致风险。
相关资源:
更多推荐


所有评论(0)