鸿蒙 ArkTS 实战:Secondhand Book Exchange 从二手书交换站到社区换书应用完整解析

前言

二手书交换站 是一个典型的鸿蒙 ArkTS 生活服务类单页应用。它围绕“社区书架 A2 支持算法图解、小王子、旅行手账等书籍交换,页面展示匹配需求、当前书籍和信用分增长。”这个具体业务展开,用较少的状态字段完成了选择、确认、推进、计费或展示隐藏等常见交互。

这篇文章会从项目真实源码出发,拆解 Secondhand Book Exchange 的状态设计、数组渲染、条件样式、按钮事件和业务计算方式。重点不是泛泛介绍语法,而是看一个可运行页面如何把业务动作落到 ArkTS 代码中。

生活服务类页面最需要的是清楚、稳定、可感知:用户点一下,界面就应该立刻告诉他结果。

在这里插入图片描述

图示说明:本文围绕页面中的标题区、选择区、状态区和操作区展开,所有示例都对应项目中的真实交互模式。

一、项目背景与使用场景

1.1 业务背景

社区书架 A2 支持算法图解、小王子、旅行手账等书籍交换,页面展示匹配需求、当前书籍和信用分增长。

这类场景很适合用鸿蒙声明式 UI 实现,因为它们通常有明确的当前状态、固定的业务选项,以及一个或两个能够推进流程的按钮。

1.2 用户路径

用户进入页面后的典型路径是:

  1. 先查看标题、对象、时间或当前金额。
  2. 在列表、网格、标签或流程中选择目标项。
  3. 点击按钮完成确认、推进、增加数量或更新状态。
  4. 在当前页面看到结果变化。

1.3 页面目标

目标 页面表现 技术实现
识别业务 标题和摘要信息 Text
选择对象 卡片、标签、列表 ForEach
展示结果 状态、金额、进度 @State 绑定
推动动作 确认、增加、更新 Button / Toggle

二、页面入口与基础结构

2.1 Entry 组件

项目页面以 @Entry@Component 标识入口组件,build 方法负责组织完整 UI。

@Entry
@Component
struct Index {
  build() {
    Column() {
      Text('二手书交换站')
    }
  }
}

2.2 为什么适合单页实现

二手书交换站 的业务闭环很短,核心状态只有 2 个:book、credit。单页实现能让开发者直接看到状态字段如何影响界面。

2.3 页面区域划分

区域 内容 设计目的
顶部区域 标题、业务对象、当前数值 建立上下文
主体区域 选项、流程、图形区域 承载主要交互
说明区域 地址、备注、时间、提醒 补充业务信息
底部动作 确认、推进、增加 完成状态变化

三、状态字段设计

3.1 State 字段

字段 类型 含义
book number 当前匹配书籍索引
credit number 信用分,默认 32

这些字段都是界面变化的来源。只要字段被更新,ArkTS 声明式 UI 会自动刷新依赖它的组件。

3.2 状态声明代码

@Entry
@Component
struct Index {
  @State book: number = 0; // 当前匹配书籍索引
  @State credit: number = 32; // 信用分,默认 32
}

3.3 字段粒度

book、credit 的粒度比较克制,没有把静态文案也做成状态。这种写法适合小型业务页面,既直观又不会制造额外复杂度。

能用普通字段表达的静态配置,就不必放进响应式状态里。

四、业务数组与数据映射

4.1 静态业务数据

books:算法图解、小王子、旅行手账;matches:想换 Python 入门、可换绘本、可换明信片

这些数据与页面的选择项、说明项或流程节点直接对应,是文章分析这个项目时最重要的项目差异来源。

4.2 ForEach 循环渲染

ForEach(this.items, (item: string, index: number) => {
  Text(item)
    .fontSize(16)
    .fontWeight(FontWeight.Bold)
    .onClick(() => {
      this.current = index
    })
})

4.3 索引驱动详情

索引状态可以同时驱动标题、备注、价格、状态和高亮样式:

Text(this.items[this.current])
Text(this.notes[this.current])
.backgroundColor(this.current === index ? '#4F46E5' : '#FFFFFF')

这也是 二手书交换站 页面保持简洁的关键。

五、布局设计与视觉层级

5.1 主容器选择

页面通常以 Column 或 Row 作为主容器。Column 适合从上到下展示流程,Row 适合左侧导航、右侧详情的结构。

Column() {
  Text('二手书交换站')
  Blank()
  Button('主要操作')
}
.width('100%')
.height('100%')

5.2 视觉层级表

信息类型 推荐样式 在项目中的作用
标题 大字号加粗 快速识别业务
状态 主题色加粗 强调当前结果
备注 灰色中小字号 承载辅助说明
按钮 主题色背景 引导下一步操作

5.3 主题色

该项目使用 #4F46E5 作为主色。主色集中出现在选中态、按钮、数字和关键状态上,让页面的操作重点更明确。

Text('关键状态')
  .fontSize(20)
  .fontWeight(FontWeight.Bold)
  .fontColor('#4F46E5')

六、交互行为拆解

6.1 主要动作

  1. 点击书籍行切换 book
  2. 匹配说明读取 matches[index]
  3. 完成交换按钮让 credit 增加 2

6.2 项目中的关键代码

Row().onClick(() => { this.book = index })
Button('完成交换').onClick(() => { this.credit += 2 })

6.3 反馈闭环

  • 点击书籍行切换 book
  • 匹配说明读取 matches[index]
  • 完成交换按钮让 credit 增加 2

这些动作都在当前页面完成,不需要跳转或弹窗。对于生活服务类小工具来说,这种直接反馈是体验稳定的关键。

七、业务计算与状态推进

7.1 计算表达式

credit = credit + 2

7.2 展示计算结果

计算结果可以直接展示在 Text 中:

Text('当前结果:' + String(result))
  .fontSize(24)
  .fontWeight(FontWeight.Bold)
  .fontColor('#4F46E5')

7.3 边界条件

如果是流程推进类操作,需要控制最大值:

if (this.current < max) {
  this.current += 1
}

如果是布尔确认类操作,则要避免重复逻辑产生歧义:

this.confirmed = true

八、ArkTS 组件细节

8.1 Text 组件

Text 负责承载标题、说明、状态、金额和提醒。它是这类业务页面中出现最多的组件。

Text('二手书交换站')
  .fontSize(26)
  .fontWeight(FontWeight.Bold)

8.2 Button 组件

Button 与业务动作直接绑定。按钮文字应描述结果,而不是描述内部实现。

Button('确认')
  .backgroundColor('#4F46E5')
  .onClick(() => {
    this.done = true
  })

8.3 条件样式

条件样式让用户看出当前项、完成项和待处理项:

.fontColor(this.current === index ? '#FFFFFF' : '#4F46E5')
.backgroundColor(this.current === index ? '#4F46E5' : '#FFFFFF')

8.4 图形与示意区域

Stack、Circle、Rect 可以构造图形区域、照片区域、取件码区域或视觉焦点。

Stack() {
  Rect().width('100%').height(220).fill('#F8FAFC')
  Text('业务卡片')
}

九、代码可维护性

9.1 状态与静态数据分离

二手书交换站 将变化字段放在 @State 中,将静态选项放在 private 数组中。这是非常清楚的职责分离。

9.2 业务命名清晰

字段名 book、credit 都能直接对应业务含义。后续排查点击问题时,可以很快定位到更新逻辑。

9.3 拆分 Builder 的时机

当列表项、卡片或状态节点变复杂时,可以抽出 builder 方法:

@Builder
private StatusCard(title: string, active: boolean) {
  Text(title)
    .fontColor(active ? '#FFFFFF' : '#4F46E5')
}

十、调试方法

10.1 点击路径验证

调试时按以下路径走一遍:

  1. 初始页面是否显示默认状态。
  2. 点击选项后高亮是否变化。
  3. 按钮点击后数值或文案是否更新。
  4. 到达边界后是否保持稳定。

10.2 日志输出

Button('调试动作')
  .onClick(() => {
    console.info('state changed')
  })

10.3 常见问题

问题 可能原因 解决方式
列表点击无效 onClick 没有更新状态 检查赋值字段
高亮不跟随 条件判断字段错误 对齐 index 与状态
数字不变化 Text 未读取 @State 改为读取状态字段
状态越界 缺少最大值判断 增加 if 限制

十一、业务扩展设计

11.1 本地缓存

二手书交换站 可以将当前选择和操作结果保存到本地,方便下次继续处理。

interface Snapshot {
  selected: number
  updatedAt: string
}

11.2 服务端同步

接入服务端后,可以把状态字段作为订单、工单或预约记录的一部分。

{
  "business": "二手书交换站",
  "stateFields": "book、credit",
  "theme": "社区换书"
}

11.3 历史记录

对于服务类应用,历史记录可以帮助用户查看上一次预约、上一次费用和当前处理进度。

十二、体验优化

12.1 减少误触

移动端操作时,列表项和按钮应保持足够高度,尤其是外勤、门店、社区服务场景。

12.2 提升可读性

关键数字、状态和当前选择要用加粗或主题色强调,辅助说明使用较浅颜色。

12.3 保持反馈一致

同一页面中,选中态、确认态和按钮主色最好保持一致,降低用户理解成本。

十三、完整实现片段

13.1 状态骨架

@Entry
@Component
struct Index {
  @State book: number = 0; // 当前匹配书籍索引
  @State credit: number = 32; // 信用分,默认 32
}

13.2 数据配置

// books:算法图解、小王子、旅行手账;matches:想换 Python 入门、可换绘本、可换明信片
private primaryColor: string = '#4F46E5'

13.3 交互代码

Row().onClick(() => { this.book = index })
Button('完成交换').onClick(() => { this.credit += 2 })

13.4 样式示例

Text('业务状态')
  .fontSize(18)
  .fontWeight(FontWeight.Bold)
  .fontColor('#4F46E5')

十四、同类页面复用价值

14.1 可以复用的模式

  • number 状态表示当前选项或流程节点。
  • boolean 状态表示确认、在线、上传、显示隐藏等二值状态。
  • 数组字段承载固定业务清单。
  • 按钮事件只做一个明确状态变化。

14.2 可以迁移的场景

二手书交换站 的写法可以迁移到门店预约、社区登记、上门服务、租借寄存、维修售后等多种页面。

14.3 工程化升级方向

当页面数量增多时,可以抽出主题色、卡片样式、状态节点和列表项组件,让不同业务保持统一体验。

十五、学习价值

15.1 对 ArkTS 初学者

这个项目适合练习 @State、ForEach、条件表达式和 onClick 事件。

15.2 对业务开发者

它展示了如何把一个真实服务流程拆成状态、数组和动作,而不是只停留在静态页面。

15.3 对后续项目

理解 二手书交换站 后,再做预约、工单、订单、清单、计费页面时,会更容易设计清楚的数据流。

总结

Secondhand Book Exchange 用简洁的 ArkTS 页面完成了 社区换书 场景的核心功能。它把 book、credit 作为响应式状态,把 books:算法图解、小王子、旅行手账;matches:想换 Python 入门、可换绘本、可换明信片 作为业务数据来源,再通过按钮、列表和条件样式组成完整交互。

从这个项目可以学到,鸿蒙应用开发并不一定从复杂架构开始。只要把业务对象、当前状态、用户动作、反馈结果四件事写清楚,一个小页面也能具备很好的可读性和扩展性。


相关链接:

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐