鸿蒙 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标记。EntryComponentstruct 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描述每一条游戏记录。interfaceGameRound{title:string;score:number;note:string;active:boolean;}这个接口非常适合成绩类应用。title表示轮次或关卡score保存卡片数量note用来保存补充描述active则控制是否被选中。2.2 字段语义字段类型在本项目中的含义titlestring轮次、关卡或分类名称scorenumber卡片数量notestring结果说明、提示状态或扩展备注activeboolean是否作为重点记录数据模型稳定是这类项目的关键。后续即使增加真实玩法也可以继续把最终结果写回同一个记录结构。三、默认数据与业务语义3.1 默认记录页面初始化时已经准备两条业务记录。Staterounds:GameRound[][{title:Warmup questions,score:6,note:Skip rule 2,active:false},{title:Memory questions,score:4,note:Saved favorite,active:false}];默认标题数值备注是否选中Warmup questions6Skip rule 2falseMemory questions4Saved favoritefalse3.2 默认数据的价值默认数据让用户首次进入页面时就能理解这个应用要记录什么。比如Warmup questions和Memory questions分别对应不同训练或玩法场景备注字段则补充了结果细节。空列表会降低理解效率。示例记录可以说明输入格式。顶部统计从首次打开就有可读数值。四、页面状态管理4.1 状态字段页面使用State保存列表、表单、开关和提示文本。Staterounds:GameRound[][];StatetitleText:string;StatescoreText:string;StatenoteText:string;StatesoundOn:booleantrue;Statestatus:stringGame ready;4.2 状态流转状态触发动作结果rounds新增或勾选记录列表和统计刷新titleText输入轮次名称提交时写入 titlescoreText输入数值转换为 scorenoteText输入备注写入 notesoundOn切换声音开关保存声音偏好status提交表单展示错误或成功信息ArkTS 的声明式体验重点在于状态变化。只要状态设计清楚UI 更新就能自然跟随数据变化。五、统计函数拆解5.1 累计Cards顶部左侧指标来自totalScore()。privatetotalScore():number{lettotal0;this.rounds.forEach((item:GameRound){totalitem.score;});returntotal;}5.2 计算选中数量右侧 Selected 来自selectedCount()。privateselectedCount():number{letcount0;this.rounds.forEach((item:GameRound){if(item.active){count;}});returncount;}这两个函数都只读列表状态不修改任何数据因此适合作为 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 汇总衡量整体表现Selectedactive 为 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 标签含义序号标签说明1Question category主玩法入口2Random draw交互过程3Skip rule结果反馈4Saved question沉淀能力功能标签的好处是信息密度高不需要额外说明文字也能让用户迅速理解页面能力。八、声音开关与配置入口8.1 开关代码页面提供Sound开关状态保存在soundOn中。Row(){Text(Sound).fontSize(16).fontColor(#374151)Blank()Toggle({type:ToggleType.Switch,isOn:this.soundOn}).onChange((checked:boolean)this.soundOnchecked)}8.2 扩展方向在当前版本中soundOn还没有接入音频系统但它已经占好了配置位置。后续可以根据它控制按钮音效、答题反馈音、成功提示音或错误提醒音。九、表单录入与校验9.1 输入区结构表单由名称、数值、备注和按钮组成。Column({space:10}){TextInput({placeholder:Round name,text:this.titleText}).onChange((value:string)this.titleTextvalue)TextInput({placeholder:Score,text:this.scoreText}).type(InputType.Number).onChange((value:string)this.scoreTextvalue)TextInput({placeholder:Note,text:this.noteText}).onChange((value:string)this.noteTextvalue)Button(Add score).height(42).width(100%).onClick(()this.addRound())}9.2 校验规则提交时会先检查名称和数值。名称不能为空分数必须能转换成有效数字。constscoreNumber(this.scoreText);if(this.titleText.trim().length0||!Number.isFinite(score)){this.statusEnter round and valid score;return;}这个校验足够直接能避免空标题和非法分数进入列表。十、新增记录逻辑10.1 保存方法新增记录由addRound()完成。privateaddRound():void{constscoreNumber(this.scoreText);if(this.titleText.trim().length0||!Number.isFinite(score)){this.statusEnter round and valid score;return;}this.rounds[{title:this.titleText,score:Math.round(score),note:this.noteText.length0?this.noteText:No note yet,active:false},...this.rounds];this.titleText;this.scoreText;this.noteText;this.statusRound 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 勾选更新切换勾选时先修改当前项再用展开语法生成新数组引用。privatetoggleActive(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主指标数值强调成绩#1D4ED8Selected 数值强调选择状态#DCFCE7选中列表项表达活跃状态#F1F5F9表单背景区分输入区域十三、运行验证流程13.1 手工验证路径可以按以下流程验证打开页面确认标题是Family Truth Cards。查看默认两条记录是否出现。点击Add score且不输入内容确认提示为Enter round and valid score。输入Daily round、99和备注再点击保存。勾选新记录确认背景变绿且 Selected 数量增加。this.titleTextDaily round;this.scoreText99;this.noteTextManual record;this.addRound();// 预期status 更新为 Round saved// 预期新记录位于列表第一项13.2 验证要点验证点预期结果空标题提交显示错误提示合法提交新记录插入列表顶部数值输入保存为整数 score勾选记录active 取反顶部统计跟随列表变化十四、面向真实玩法的扩展14.1 业务扩展后续可以加入问题库、随机抽卡动画、跳过次数限制、收藏列表和多人轮换。14.2 工程扩展后续扩展时可以保持当前页面的状态结构不变只把真实玩法结果接入score和noteinterfacePlayResult{roundTitle:string;finalScore:number;summary:string;}functionmapResultToRound(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完成记录展示与选中状态更新。它的代码不复杂但工程链路完整。对于家庭真心话卡片这种玩法来说当前版本已经完成了成绩记录层后续只需要逐步补齐真实玩法就能从原型平滑走向可用产品。补充说明当前页面把业务状态集中在组件内适合小型训练工具快速迭代。补充说明记录列表采用头部插入能让用户提交后立即看到最新结果。补充说明备注字段保留了足够弹性可以承载提示、收藏、错误次数或扩展状态。补充说明统计卡片和列表项共享同一份数据减少了重复状态带来的不一致风险。补充说明当前页面把业务状态集中在组件内适合小型训练工具快速迭代。补充说明记录列表采用头部插入能让用户提交后立即看到最新结果。补充说明备注字段保留了足够弹性可以承载提示、收藏、错误次数或扩展状态。补充说明统计卡片和列表项共享同一份数据减少了重复状态带来的不一致风险。相关资源HarmonyOS 开发者官网ArkTS 语言基础ArkUI 组件开发状态管理概览TextInput 组件