鸿蒙 ArkTS 实战:家庭开窗通风计时的状态驱动界面与业务计算
鸿蒙 ArkTS 实战:家庭开窗通风计时的状态驱动界面与业务计算
前言
房间面积、居住人数、天气模式和已通风分钟,是一个围绕日常生活场景设计的鸿蒙单页工具。
本文以 entry/src/main/ets/pages/Index.ets 为入口,按照页面真实代码展开,重点说明状态字段、派生函数、输入控件、事件回调和结果展示之间的关系。

小工具的价值在于把清楚的生活规则转换成可操作界面。
输入、计算和反馈由同一组状态驱动,页面行为因此保持一致。
一、业务定位
1.1 使用场景
家庭开窗通风计时把原本需要手工估算或分散记录的信息集中到首屏。
用户通过滑块、文本框或按钮改变参数,页面立即显示新的计算结果。
1.2 功能闭环
-
录入当前场景的基础数据。
-
选择分类、模式或状态。
-
使用函数把输入转换为结果。
-
将结果和可读文案同步呈现。
1.3 当前实现
当前业务集中在 Index 组件中。
状态字段使用 @State 保存。
规则由普通成员函数表达。
事件通过 onChange 和 onClick 修改状态。
本文只讨论代码中已经出现的行为。
1.4 阅读维度
| 层次 | 关注内容 | 页面体现 |
|—|—|—|
| 数据 | 状态字段和接口 | 输入与记录 |
| 规则 | 派生函数 | 计算与文案 |
| 交互 | 控件和事件 | 状态改变 |
| 展示 | ArkUI 组件树 | 结果与列表 |
二、页面入口
2.1 装饰器
@Entry
@Component
struct Index {
// 状态、规则与 build 位于同一组件
}
@Entry 标识页面入口。
@Component 声明可渲染组件。
Index 同时承载状态、计算方法和 build。
2.2 构建函数
build() {
Column() {
Text('结果区域')
Text('参数区域')
}.width('100%').height('100%')
}
build 描述组件树。
结果区域位于前部,输入控件紧随其后。
2.3 容器组织
Column 负责纵向分区。
Row 负责横向排列。
Scroll 承载可增长列表。
layoutWeight 让主要文本吸收剩余宽度。
三、状态模型
3.1 字段清单
| 状态字段 | 页面含义 | 业务作用 |
|—|—|—|
| roomArea | 页面状态 | 参与家庭开窗通风计时 |
| peopleCount | 页面状态 | 参与家庭开窗通风计时 |
| weatherMode | 页面状态 | 参与家庭开窗通风计时 |
| elapsedMin | 页面状态 | 参与家庭开窗通风计时 |
这些字段共同构成 家庭开窗通风计时 的页面数据模型。
字段名称与界面文案保持相同语义。
3.2 初始状态
@State roomArea: number = 30
@State peopleCount: number = 2
@State weatherMode: number = 1
@State elapsedMin: number = 0
初始值决定应用打开时的第一组结果。
数值位于控件允许范围内。
布尔值决定模式按钮的初始文案。
3.3 响应式更新
Slider({ value: this.value, min: 0, max: 100, step: 1 })
.onChange((value: number) => {
this.value = Math.round(value)
})
@State 改变后,读取它的 UI 自动重新计算。
回调中取整可以让显示值和滑块步进一致。
3.4 字段职责
roomArea、peopleCount、weatherMode、elapsedMin 分别承担输入、模式、过程或结果来源。
页面没有额外复制一份派生结果状态。
这减少了状态不同步的可能。
四、核心业务规则
4.1 规则说明
目标时间为房间面积乘 0.8 加人数乘 4;寒冷乘 0.65,潮湿乘 1.25,最终目标不低于 8 分钟。
weatherText 映射天气,targetMinutes 计算目标,progress 计算百分比,addFive 累加分钟
规则集中在具名函数中。
组件树只负责读取返回值。
4.2 核心实现
targetMinutes(): number {
let target = this.roomArea * 0.8 + this.peopleCount * 4
if (this.weatherMode === 0) target *= 0.65
if (this.weatherMode === 2) target *= 1.25
return Math.max(8, Math.round(target))
}
该函数直接读取当前状态。
每次输入变化后都会得到新的返回值。
4.3 派生结果
progress(): number {
return Math.min(100, Math.round(
this.elapsedMin / this.targetMinutes() * 100
))
}
派生结果不需要单独保存。
这让计算逻辑保持单一来源。
4.4 边界保护
数值输入需要落在合法范围。
比例需要限制在 0 到 1 或 0 到 100。
计数不能出现负数。
空数组不能读取不存在的末项。
列表操作应返回新的数组引用。
五、输入控件
5.1 滑块输入
Slider({ value: this.value, min: 0, max: 100, step: 1 })
.onChange((value: number) => {
this.value = Math.round(value)
})
Slider 适合范围明确、步进固定的参数。
value 绑定页面状态。
onChange 接收用户拖动后的值。
5.2 文本输入
TextInput({ text: this.inputText, placeholder: '请输入' })
.onChange((value: string) => {
this.inputText = value
})
TextInput 适合名称或数字文本。
写入数组前需要去掉首尾空白。
数字文本进入公式前需要转换。
5.3 按钮交互
Button('执行操作')
.width('100%').height(48)
.onClick(() => this.handleAction())
Button 适合离散选择和明确命令。
按钮文案可以由布尔状态或索引状态派生。
5.4 项目交互
addFive(): void {
this.elapsedMin = Math.min(this.targetMinutes(), this.elapsedMin + 5)
}
这一片段体现了本项目真实的状态更新方式。
回调保持短小,复杂规则交给成员函数。
六、结果展示
6.1 结果优先
结果区域位于首屏前部。
字号高于参数标签。
状态文字与核心数值形成层级。
6.2 可读文案
计算函数输出数值。
映射函数输出自然语言。
两者共同帮助用户理解当前状态。
6.3 状态颜色
| 状态 | 视觉表达 | 含义 |
|—|—|—|
| 正常 | 绿色或青色 | 当前结果可接受 |
| 中间 | 灰色或黄色 | 仍需关注参数 |
| 超限 | 红色或深色 | 当前需要处理 |
6.4 稳定尺寸
按钮使用稳定高度。
结果数字使用稳定字号。
卡片使用一致内边距。
动态文本变化时布局不会明显跳动。
七、数组与列表
7.1 列表渲染
ForEach(this.items, (item: RecordItem) => {
Row() {
Text(item.name).layoutWeight(1)
Text(item.value.toString())
}
}, (item: RecordItem) => item.id.toString())
ForEach 使用稳定 ID 作为 key。
列表变化时节点复用更准确。
7.2 映射更新
targetMinutes(): number {
let target = this.roomArea * 0.8 + this.peopleCount * 4
if (this.weatherMode === 0) target *= 0.65
if (this.weatherMode === 2) target *= 1.25
return Math.max(8, Math.round(target))
}
```0
map 适合切换单条记录状态。
没有命中的条目保持原值。
### 7.3 过滤移除
```typescript
targetMinutes(): number {
let target = this.roomArea * 0.8 + this.peopleCount * 4
if (this.weatherMode === 0) target *= 0.65
if (this.weatherMode === 2) target *= 1.25
return Math.max(8, Math.round(target))
}
```1
filter 适合删除或消耗完毕的记录。
重新赋值让列表刷新路径清晰。
### 7.4 统计派生
总量由当前数组遍历得到。
统计值不需要重复维护。
新增、修改和移除后都能得到一致结果。
## 八、交互链路
### 8.1 完整流程
用户录入房间面积与人数,再切换天气模式。增加按钮累计通风时间,达到目标后进度不会超过 100%。
### 8.2 状态闭环
1. 页面读取初始状态。
2. 用户改变控件。
3. 回调写回 @State。
4. 派生函数重新执行。
5. 结果区域和列表同步刷新。
### 8.3 操作反馈
| 操作 | 状态变化 | 可见反馈 |
|---|---|---|
| 调整数值 | 数值字段更新 | 结果数字变化 |
| 切换模式 | 索引或布尔值更新 | 按钮样式变化 |
| 新增记录 | 数组增加元素 | 列表与统计变化 |
| 完成操作 | 重置或递增 | 状态文案刷新 |
### 8.4 事件职责
事件回调只做赋值或调用方法。
业务判断不堆积在 build 中。
页面结构因此容易扫描。
## 九、ArkUI 布局
### 9.1 Column
Column 将标题、结果、参数和列表纵向排列。
space 属性统一控制子项间距。
padding 留出触控和阅读空间。
### 9.2 Row
Row 把标题与关键结果并排放置。
layoutWeight 让标题区域适应剩余宽度。
数值区域保持稳定宽度。
### 9.3 Scroll
记录数量可能增长时使用 Scroll。
滚动条可以隐藏,但滚动能力仍然保留。
输入区域和记录区域位于同一纵向路径。
### 9.4 组件职责
| 组件 | 职责 | 内容 |
|---|---|---|
| Text | 展示信息 | 标题、数值、状态 |
| Slider | 调整数值 | 时间、重量、温度 |
| TextInput | 接收文本 | 名称、数量 |
| Button | 执行操作 | 切换、添加、完成 |
| ForEach | 渲染数组 | 清单与设备 |
| Scroll | 承载长内容 | 多条记录 |
## 十、代码可维护性
### 10.1 单一来源
输入保存在 @State。
结果由函数计算。
UI 只读取状态和函数返回值。
### 10.2 具名函数
weatherText 映射天气,targetMinutes 计算目标,progress 计算百分比,addFive 累加分钟
函数名称直接表达业务含义。
阅读者可以从 build 反向定位规则。
### 10.3 类型约束
数值字段明确使用 number。
文本字段明确使用 string。
记录数组通过接口固定结构。
回调参数带有显式类型。
### 10.4 边界一致性
滑块范围与业务范围保持一致。
文本转换失败时使用兜底值。
数组为空时阻止无效操作。
结果格式统一保留所需小数位。
## 十一、运行表现
### 11.1 首次打开
默认状态立即生成一组可读结果。
用户不操作也能理解工具用途。
顶部结果验证公式和布局是否正常。
### 11.2 参数变化
拖动滑块触发 onChange。
所有读取该状态的区域同步刷新。
页面不需要手动查找 Text 节点。
### 11.3 模式变化
按钮切换索引或布尔值。
文案和颜色从新状态重新派生。
计算因子也随模式改变。
### 11.4 列表变化
新增记录通常插入首位。
完成状态通过 map 更新。
删除状态通过 filter 更新。
统计函数基于最新数组重新计算。
## 十二、真实场景映射
### 12.1 信息采集
房间面积、居住人数、天气模式和已通风分钟 是用户可以直接观察或输入的信息。
字段越贴近实际,工具越容易操作。
### 12.2 规则计算
目标时间为房间面积乘 0.8 加人数乘 4;寒冷乘 0.65,潮湿乘 1.25,最终目标不低于 8 分钟。
公式没有隐藏在 UI 链式调用中。
它可以脱离界面单独验证。
### 12.3 行动结果
页面不仅显示数字。
状态文字把数字转换成下一步判断。
颜色进一步强调结果层级。
### 12.4 实现范围
当前页面聚焦单页交互和内存状态。
重新启动后是否保留记录不在当前实现中。
远程同步和多人共享也不在当前代码范围内。
## 十三、总结
家庭开窗通风计时展示了鸿蒙 ArkTS 小工具的典型实现路径:
- @State 保存输入与记录。
- 成员函数承载公式和状态判断。
- Slider、TextInput 与 Button 修改状态。
- ForEach 和数组方法管理列表。
- Text、Row、Column 组织结果优先的界面。
房间面积、居住人数、天气模式和已通风分钟 这类需求不依赖复杂架构。
关键是让数据、规则和反馈保持单向关系。
状态边界明确后,页面就能用较少代码形成完整体验。
**相关资源:**
- [ArkTS 语言基础](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts)
- [ArkUI 声明式开发](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-ui-development)
更多推荐




所有评论(0)