鸿蒙 ArkTS 实战:Secondhand Book Exchange 从二手书交换站到社区换书应用完整解析
前言
二手书交换站 是一个典型的鸿蒙 ArkTS 生活服务类单页应用。它围绕“社区书架 A2 支持算法图解、小王子、旅行手账等书籍交换,页面展示匹配需求、当前书籍和信用分增长。”这个具体业务展开,用较少的状态字段完成了选择、确认、推进、计费或展示隐藏等常见交互。
这篇文章会从项目真实源码出发,拆解 Secondhand Book Exchange 的状态设计、数组渲染、条件样式、按钮事件和业务计算方式。重点不是泛泛介绍语法,而是看一个可运行页面如何把业务动作落到 ArkTS 代码中。
生活服务类页面最需要的是清楚、稳定、可感知:用户点一下,界面就应该立刻告诉他结果。
图示说明:本文围绕页面中的标题区、选择区、状态区和操作区展开,所有示例都对应项目中的真实交互模式。
一、项目背景与使用场景
1.1 业务背景
社区书架 A2 支持算法图解、小王子、旅行手账等书籍交换,页面展示匹配需求、当前书籍和信用分增长。
这类场景很适合用鸿蒙声明式 UI 实现,因为它们通常有明确的当前状态、固定的业务选项,以及一个或两个能够推进流程的按钮。
1.2 用户路径
用户进入页面后的典型路径是:
- 先查看标题、对象、时间或当前金额。
- 在列表、网格、标签或流程中选择目标项。
- 点击按钮完成确认、推进、增加数量或更新状态。
- 在当前页面看到结果变化。
1.3 页面目标
| 目标 | 页面表现 | 技术实现 |
|---|---|---|
| 识别业务 | 标题和摘要信息 | Text |
| 选择对象 | 卡片、标签、列表 | ForEach |
| 展示结果 | 状态、金额、进度 | @State 绑定 |
| 推动动作 | 确认、增加、更新 | Button / Toggle |
二、页面入口与基础结构
2.1 Entry 组件
项目页面以@Entry和@Component标识入口组件,build方法负责组织完整 UI。
@Entry@Componentstruct Index{build(){Column(){Text('二手书交换站')}}}2.2 为什么适合单页实现
二手书交换站 的业务闭环很短,核心状态只有 2 个:book、credit。单页实现能让开发者直接看到状态字段如何影响界面。
2.3 页面区域划分
| 区域 | 内容 | 设计目的 |
|---|---|---|
| 顶部区域 | 标题、业务对象、当前数值 | 建立上下文 |
| 主体区域 | 选项、流程、图形区域 | 承载主要交互 |
| 说明区域 | 地址、备注、时间、提醒 | 补充业务信息 |
| 底部动作 | 确认、推进、增加 | 完成状态变化 |
三、状态字段设计
3.1 State 字段
| 字段 | 类型 | 含义 |
|---|---|---|
| book | number | 当前匹配书籍索引 |
| credit | number | 信用分,默认 32 |
这些字段都是界面变化的来源。只要字段被更新,ArkTS 声明式 UI 会自动刷新依赖它的组件。
3.2 状态声明代码
@Entry@Componentstruct Index{@Statebook:number=0;// 当前匹配书籍索引@Statecredit: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 主要动作
- 点击书籍行切换 book
- 匹配说明读取 matches[index]
- 完成交换按钮让 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+27.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 方法:
@BuilderprivateStatusCard(title:string,active:boolean){Text(title).fontColor(active?'#FFFFFF':'#4F46E5')}十、调试方法
10.1 点击路径验证
调试时按以下路径走一遍:
- 初始页面是否显示默认状态。
- 点击选项后高亮是否变化。
- 按钮点击后数值或文案是否更新。
- 到达边界后是否保持稳定。
10.2 日志输出
Button('调试动作').onClick(()=>{console.info('state changed')})10.3 常见问题
| 问题 | 可能原因 | 解决方式 |
|---|---|---|
| 列表点击无效 | onClick 没有更新状态 | 检查赋值字段 |
| 高亮不跟随 | 条件判断字段错误 | 对齐 index 与状态 |
| 数字不变化 | Text 未读取 @State | 改为读取状态字段 |
| 状态越界 | 缺少最大值判断 | 增加 if 限制 |
十一、业务扩展设计
11.1 本地缓存
二手书交换站 可以将当前选择和操作结果保存到本地,方便下次继续处理。
interfaceSnapshot{selected:numberupdatedAt:string}11.2 服务端同步
接入服务端后,可以把状态字段作为订单、工单或预约记录的一部分。
{"business":"二手书交换站","stateFields":"book、credit","theme":"社区换书"}11.3 历史记录
对于服务类应用,历史记录可以帮助用户查看上一次预约、上一次费用和当前处理进度。
十二、体验优化
12.1 减少误触
移动端操作时,列表项和按钮应保持足够高度,尤其是外勤、门店、社区服务场景。
12.2 提升可读性
关键数字、状态和当前选择要用加粗或主题色强调,辅助说明使用较浅颜色。
12.3 保持反馈一致
同一页面中,选中态、确认态和按钮主色最好保持一致,降低用户理解成本。
十三、完整实现片段
13.1 状态骨架
@Entry@Componentstruct Index{@Statebook:number=0;// 当前匹配书籍索引@Statecredit:number=32;// 信用分,默认 32}13.2 数据配置
// books:算法图解、小王子、旅行手账;matches:想换 Python 入门、可换绘本、可换明信片privateprimaryColor: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 入门、可换绘本、可换明信片 作为业务数据来源,再通过按钮、列表和条件样式组成完整交互。
从这个项目可以学到,鸿蒙应用开发并不一定从复杂架构开始。只要把业务对象、当前状态、用户动作、反馈结果四件事写清楚,一个小页面也能具备很好的可读性和扩展性。
相关链接:
- HarmonyOS 应用开发指南
- ArkTS 快速入门
- ArkUI 声明式开发