鸿蒙 ArkTS 实战:Color Identification Challenge 从颜色识别挑战到记录管理完整解析

前言

Color Identification Challenge 是一个面向 颜色识别挑战 场景的鸿蒙 ArkTS 单页应用。它不是只展示静态文案,而是围绕记录数据、分数统计、开关配置、表单录入和列表交互构建了完整的轻量业务闭环。

这篇文章会直接从项目里的 Index.ets 入手,拆解它如何通过 @State 管理页面状态,如何用 ForEach 渲染记录列表,如何用 Toggle 完成开关与勾选交互,以及如何把新增记录插入到列表头部。

这类小应用的价值在于结构清晰:数据模型简单、交互路径短、状态变化可追踪,适合用来练习鸿蒙 ArkTS 的声明式 UI 与状态驱动思维。

在这里插入图片描述

上图可作为文章发布时的界面结构配图,表达“顶部指标、功能标签、配置开关、录入表单、记录列表”的信息层次。实际发布时如果已有运行截图,也可以替换为项目真机截图。

一、项目定位与功能目标

1.1 应用场景

围绕颜色题目、限时选择、分数统计和色弱模式记录成绩,适合做视觉识别训练工具。

从代码看,页面已经具备一个可运行工具的基本骨架:顶部显示累计 积分,中部提供功能标签和声音或辅助开关,下方通过输入框新增记录,并用复选框标记活跃记录。

1.2 功能边界

当前版本聚焦单页体验,不引入路由、不依赖后端、不使用复杂状态库。这样的边界很适合做 MVP:

  1. 先完成记录展示和新增。
  2. 再验证核心指标是否符合玩法。
  3. 最后再接入真正的游戏逻辑、持久化或资源系统。
模块 当前能力 对应源码
数据模型 定义标题、分值、备注、选中态 GameRecord
统计卡片 汇总 Points 与选中数量 totalScore/totalAmountselectedCount
表单录入 输入名称、数值、备注 TextInput
列表交互 勾选记录并更新背景 ForEachToggle

二、目录入口与页面职责

2.1 主页面文件

核心逻辑集中在 entry/src/main/ets/pages/Index.ets。页面使用 @Entry@Component 声明入口组件,通过 build() 返回 UI 树。

@Entry
@Component
struct Index {
  build() {
    Scroll() {
      Column({ space: 14 }) {
        Text('Color Identification Challenge')
      }
    }
  }
}

2.2 单页结构

这个页面的职责可以拆成五层:

  • 标题层:展示 Color Identification Challenge
  • 统计层:展示 Points 与 Selected。
  • 标签层:展示 Color question, Timed choice, Score stats, Colorblind mode
  • 录入层:新增一条业务记录。
  • 列表层:渲染记录并支持勾选。

这种结构对小型鸿蒙应用很实用,因为每个区域都对应一段明确的状态读写逻辑。

三、数据模型设计

3.1 记录接口

项目使用 GameRecord 描述列表项,每条记录都包含标题、数值、备注和活跃状态。

interface GameRecord {
  title: string;
  score: number;
  note: string;
  active: boolean;
}

这里的 active 字段非常关键。它不是临时 UI 状态,而是记录级状态,决定这一条数据是否被选中,也决定列表项背景是否变为绿色。

3.2 字段语义

字段 类型 业务含义
title string 轮次、关卡或队伍名称
score number 积分,用于累计统计
note string 记录补充信息
active boolean 是否被选中

颜色识别挑战 场景里,score 承载的是 积分。例如默认数据中已经包含 Warm color roundBlue green drill 两条记录,说明页面并不是空壳,而是围绕真实玩法指标组织数据。

四、默认数据与业务含义

4.1 初始记录

项目启动时会直接展示两条默认记录,方便用户进入页面后立即理解用途。

@State records: GameRecord[] = [
    { title: 'Warm color round', score: 76, note: 'Timed 30s', active: false },
    { title: 'Blue green drill', score: 62, note: 'Colorblind mode on', active: false }
];
名称 数值 备注 初始状态
Warm color round 76 Timed 30s false
Blue green drill 62 Colorblind mode on false

4.2 为什么保留默认数据

默认数据有三个作用:

  1. 避免首次打开页面时列表为空。
  2. 给用户展示记录格式。
  3. 为统计卡片提供初始值。

颜色识别挑战 这类轻量工具来说,默认样例比空页面更容易让用户理解“该填什么、填完后会发生什么”。

五、状态管理拆解

5.1 页面状态

页面使用多个 @State 字段保存输入内容、开关状态、记录集合和状态提示。

@State titleText: string = '';
@State scoreText: string = '';
@State noteText: string = '';
@State soundOn: boolean = true;
@State status: string = 'Game session ready';

这些状态都直接参与 UI 渲染。输入框变化会更新 titleTextscoreTextnoteText;按钮点击会读取这些字段;新增成功后又会清空它们。

5.2 状态更新路径

状态 触发位置 页面反馈
records 新增记录、切换勾选 列表重新渲染
titleText 名称输入框 表单内容同步
scoreText 数值输入框 用于数值校验
noteText 备注输入框 写入记录备注
soundOn Sound 开关 保存配置状态
status 表单提交 展示成功或错误提示

六、统计逻辑实现

6.1 汇总 Points

页面通过遍历记录集合计算累计 积分

private totalScore(): number {
  let total = 0;
  this.records.forEach((item: GameRecord) => {
    total += item.score;
  });
  return total;
}

这个函数没有缓存,也没有引入额外状态。只要 records 发生变化,声明式 UI 会重新读取计算结果,顶部统计卡片随之更新。

6.2 统计选中数量

选中数量通过 active 字段计算。

private selectedCount(): number {
  let count = 0;
  this.records.forEach((item: GameRecord) => {
    if (item.active) {
      count++;
    }
  });
  return count;
}

selectedCount() 的返回值会以 已选数量/总数量 形式展示。这个设计对 颜色识别挑战 很实用:用户既能看到总体记录规模,也能看到当前被重点标记的记录数量。

七、顶部指标卡片

7.1 视觉层次

顶部使用两个并排卡片展示核心指标,左侧是 Points,右侧是 Selected。

Row({ space: 10 }) {
  Column({ space: 4 }) {
    Text('Points').fontSize(13).fontColor('#475569')
    Text(String(this.totalScore())).fontSize(24).fontWeight(FontWeight.Bold)
  }.layoutWeight(1).padding(14).backgroundColor('#FEE2E2').borderRadius(8)
  Column({ space: 4 }) {
    Text('Selected').fontSize(13).fontColor('#475569')
    Text(this.selectedCount() + '/' + this.records.length).fontSize(24).fontWeight(FontWeight.Bold)
  }.layoutWeight(1).padding(14).backgroundColor('#DBEAFE').borderRadius(8)
}

左侧指标强调业务成果,右侧指标强调列表选择状态。两者放在同一行,可以让用户在进入页面后第一眼看到结果。

7.2 指标设计

指标 来源 作用
Points 所有记录的 score 汇总 衡量整体成绩
Selected active 为 true 的记录数 标记当前关注记录
总记录数 records.length 展示列表规模

对于 Color Identification Challenge 来说,Points 不是装饰信息,而是当前玩法最重要的结果指标。

八、功能标签区域

8.1 标签代码

页面通过两行标签表达应用能力。

Column({ space: 8 }) {
  Row({ space: 8 }) {
    Text('Color question').layoutWeight(1).fontSize(13).textAlign(TextAlign.Center)
    Text('Timed choice').layoutWeight(1).fontSize(13).textAlign(TextAlign.Center)
  }
  Row({ space: 8 }) {
    Text('Score stats').layoutWeight(1).fontSize(13).textAlign(TextAlign.Center)
    Text('Colorblind mode').layoutWeight(1).fontSize(13).textAlign(TextAlign.Center)
  }
}

8.2 标签对应能力

序号 标签 含义
1 Color question 主玩法入口
2 Timed choice 过程控制能力
3 Score stats 辅助信息呈现
4 Colorblind mode 结果沉淀能力

这些标签让用户在无需阅读说明的情况下理解应用方向。比如 Color question 指向主玩法,Timed choice 指向过程控制,Score statsColorblind mode 则补充辅助能力和结果沉淀。

九、开关交互设计

9.1 开关状态

页面提供 Sound 开关,状态保存在 soundOn 中。

Row() {
  Text('Sound').fontSize(16).fontColor('#374151')
  Blank()
  Toggle({ type: ToggleType.Switch, isOn: this.soundOn })
    .onChange((checked: boolean) => this.soundOn = checked)
}

9.2 交互意义

在当前版本里,开关主要承担配置入口职责。对 颜色识别挑战 来说,它可以继续映射到声音、提示、辅助模式或教学引导。

这种做法的优点是扩展成本低:后续只需要在业务逻辑里读取 soundOn,就可以决定是否播放音效、显示提示或启用辅助规则。

十、表单录入流程

10.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.addRecord())
}

10.2 表单规则

表单提交前会校验两个条件:

  1. 名称不能为空。
  2. 数值必须能转换为有效数字。

如果校验失败,状态提示会变成 Enter round and valid score;如果成功,会创建新记录并显示 Score saved

10.3 用户体验细节

数值输入框使用 InputType.Number,可以降低输入非法字符的概率。备注允许为空,但保存时会补成 No note yet,避免列表出现空白说明。

十一、新增记录逻辑

11.1 新增方法

新增方法是页面最核心的写操作。

private addRecord(): void {
  const score = Number(this.scoreText);
  if (this.titleText.trim().length === 0 || !Number.isFinite(score)) {
    this.status = 'Enter round and valid score';
    return;
  }
  this.records = [{
    title: this.titleText,
    score: Math.round(score),
    note: this.noteText.length > 0 ? this.noteText : 'No note yet',
    active: false
  }, ...this.records];
  this.titleText = '';
  this.scoreText = '';
  this.noteText = '';
  this.status = 'Score saved';
}

11.2 插入到列表头部

新记录使用 [newItem, ...oldItems] 的方式放到列表最前面。这样用户提交后可以立即在第一屏看到刚添加的记录,不需要滚动到底部。

11.3 数值处理

项目使用 Math.round() 保存整数。对 积分 这类指标来说,整数更利于展示和比较,也能避免小数影响顶部统计读数。

十二、列表渲染与勾选更新

12.1 列表代码

记录列表通过 ForEach 渲染,每条记录都带有复选框。

ForEach(this.records, (item: GameRecord, 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)
  }.padding(14).backgroundColor(item.active ? '#DCFCE7' : '#FFFFFF').borderRadius(8)
})

12.2 切换状态

勾选状态通过 toggleActive(index) 更新。

private toggleActive(index: number): void {
  this.records[index].active = !this.records[index].active;
  this.records = [...this.records];
}

这里有一个重要细节:修改数组元素后,代码又执行了 this.records = [...this.records]。这会生成新的数组引用,确保页面感知到状态变化并刷新列表。

12.3 选中态反馈

列表项背景使用条件表达式:

backgroundColor(item.active ? '#DCFCE7' : '#FFFFFF')

绿色背景让活跃记录非常明显,适合用来标记当前重点轮次、最好成绩或正在复盘的记录。

十三、界面布局与配色

13.1 布局结构

页面外层使用 Scroll,内部使用 Column 按垂直方向排列。这样即使记录增多,用户也可以继续向下浏览。

Scroll() {
  Column({ space: 14 }) {
    Text('Color Identification Challenge')
    // stats
    // tags
    // switch
    // form
    // list
  }.padding(18).width('100%')
}.height('100%').backgroundColor('#F8FAFC')

13.2 配色策略

区域 颜色 作用
页面背景 #F8FAFC 保持轻量、干净
统计红色 #DC2626 强调主要数值
统计蓝色 #1D4ED8 强调选择状态
选中绿色 #DCFCE7 表达已激活
表单灰底 #F1F5F9 与列表内容区分

这种配色不会喧宾夺主,适合成绩记录、训练工具和轻量小游戏后台页。

十四、运行验证路径

14.1 表单验证

可以按以下路径验证页面行为:

  1. 打开页面,确认默认显示两条记录。
  2. 直接点击 Add score,观察 status 是否显示 Enter round and valid score
  3. 输入 Daily round99,点击 Add score
  4. 确认新记录出现在列表顶部。
  5. 勾选第一条记录,确认背景变绿且 Selected 数量增加。
// 手工验证路径
this.titleText = 'Daily round';
this.scoreText = '99';
this.noteText = 'Manual input';
this.addRecord();

// 预期状态
// status: Score saved
// first item: Daily round / 99 / Manual input

14.2 关键断言

验证点 预期结果
空标题提交 显示 Enter round and valid score
合法数值提交 显示 Score saved
新增记录 插入列表第一位
勾选记录 active 取反,Selected 更新
顶部统计 Points 随列表变化更新

14.3 真实玩法扩展

可以扩展色卡题库、干扰文字、倒计时、色弱友好配色和错题回顾。 扩展时仍然可以复用当前记录结构,只需要把真正的游戏结果写入 score,再调用相同的新增逻辑。

十五、工程复盘与总结

15.1 本项目的实现重点

页面关注颜色识别结果的积分沉淀,也把无障碍模式作为备注信息纳入记录。

从工程角度看,它体现了鸿蒙 ArkTS 小型应用的几个关键点:

  • 用接口固定列表数据形态。
  • @State 驱动页面刷新。
  • 用函数封装统计、提交和切换逻辑。
  • ForEach 保持列表渲染清晰。
  • 用状态提示给用户即时反馈。

15.2 可复用经验

这套结构可以迁移到很多单页工具:学习打卡、训练记录、闯关成绩、家庭游戏计分、轻量任务追踪等。只要业务对象可以抽象成“名称、数值、备注、状态”,就可以复用当前实现方式。

总结

Color Identification Challenge 用非常直接的 ArkTS 代码完成了 颜色识别挑战 的记录管理闭环。它的核心不在复杂算法,而在清晰的数据流:输入内容进入 @State,提交时生成记录,记录变化触发统计更新,勾选状态再反向影响列表样式和 Selected 数量。

对于学习鸿蒙应用开发的人来说,这个项目适合用来理解声明式 UI、状态刷新、表单校验和列表渲染。对于实际产品原型来说,它也已经具备继续扩展为完整玩法页面的基础。


相关资源:

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐