HarmonyOS智能家居——安防中心页面实现详解
安防中心是智能家居 App 里功能最独立的页面——它不依赖 DeviceData 服务层,所有数据都在页面内部的 @State 里管理。从截图看,页面分三个区域:顶部两个状态卡片(门锁和警报),中间摄像头列表,底部快捷操作按钮。实现上用了两个 Builder(StatusCard 和 ActionBtn),代码量不大但有几个值得注意的设计细节。
完整效果
数据结构
interface Cam { name: string; icon: string; online: boolean }
@State cams: Cam[] = [
{ name: '门口摄像头', icon: '📹', online: true },
{ name: '客厅摄像头', icon: '📷', online: true },
{ name: '车库摄像头', icon: '🎥', online: false },
]
@State alarm: boolean = true
@State doorLocked: boolean = true

三个 @State 变量管理所有安防数据:摄像头列表、警报状态、门锁状态。和首页的设备数据不同,安防数据完全独立——没有从 DeviceData 读取任何信息,也没有暴露给其他页面。这是合理的,因为安防系统的数据结构(摄像头在线状态、布防状态)和设备系统(开关、亮度、温度)差别很大,强行放在同一个数据层里反而增加复杂度。
门锁和警报的状态卡片
Row({ space: 12 }) {
this.StatusCard('🔒', '门锁', this.doorLocked ? '已锁定' : '未锁定',
this.doorLocked ? '#00B894' : '#FF6B6B')
this.StatusCard('🚨', '警报', this.alarm ? '已布防' : '已撤防',
this.alarm ? '#00B894' : '#FF6B6B')
}.width('100%').padding({ left: 16, right: 16 })

两个 StatusCard 并排显示,每个卡片有四层信息:图标、标签、状态文字、状态颜色。
StatusCard Builder
@Builder StatusCard(icon: string, label: string, status: string, color: string) {
Column() {
Text(icon).fontSize(28).margin({ bottom: 6 })
Text(label).fontSize(12).fontColor(T2)
Text(status).fontSize(14).fontWeight(FontWeight.Bold).fontColor(color).margin({ top: 4 })
}.layoutWeight(1).padding(16).backgroundColor('#FFFFFF').borderRadius(14)
}
四个参数分别控制图标、标签文字、状态文字、状态颜色。layoutWeight(1) 让两个卡片平分水平空间。
状态文字的颜色根据状态动态变化——安全状态用绿色(#00B894),不安全状态用红色(#FF6B6B)。这种颜色编码在安防场景里是通用约定:绿色=安全,红色=警告。用户不需要读文字,只看颜色就能判断当前安防状态。
门锁和警报的切换逻辑
// 门锁
this.ActionBtn('🔒', '锁门', () => { this.doorLocked = !this.doorLocked })
// 警报
this.ActionBtn('🚨', this.alarm ? '撤防' : '布防', () => { this.alarm = !this.alarm })
门锁按钮点击时直接切换 doorLocked 的布尔值。警报按钮更巧妙——按钮文字也是动态的:this.alarm ? '撤防' : '布防'。当警报处于布防状态时,按钮显示"撤防"(告诉用户点击后会做什么);当处于撤防状态时,按钮显示"布防"。
这种"按钮文字反映当前操作"的设计比固定的"开关"文字更直观——用户不需要先判断当前状态再决定要不要点,按钮本身已经告诉了他"点了之后会发生什么"。
摄像头列表
ForEach(this.cams, (c: Cam) => {
Row() {
Text(c.icon).fontSize(32).width(56).height(56).borderRadius(14)
.backgroundColor('#F5F5FA').textAlign(TextAlign.Center)
Column() {
Text(c.name).fontSize(15).fontWeight(FontWeight.Bold).fontColor(T1)
Text(c.online ? '🟢 在线' : '🔴 离线').fontSize(11).fontColor(T3)
}.alignItems(HorizontalAlign.Start).margin({ left: 12 }).layoutWeight(1)
SymbolGlyph($r('sys.symbol.chevron_right')).fontSize(14).fontColor(['#DDD'])
}.width('100%').padding(14).backgroundColor('#FFFFFF').borderRadius(14)
.margin({ left: 16, right: 16 })
})

每个摄像头卡片分三部分:左侧 56×56 的 emoji 图标容器、中间名称和在线状态、右侧右箭头。
在线状态用绿色圆点 + “在线” 或红色圆点 + “离线” 表示——和首页设备状态卡片的绿色圆点用的是同一套视觉语言。右箭头暗示"点击可查看详情",但目前没有绑定 onClick 事件,是一个 UI 占位。
Cam 接口的轻量设计
interface Cam { name: string; icon: string; online: boolean }
三个字段足够了——名称用于显示,emoji 用于图标,在线状态用于判断。没有 id 字段,因为摄像头列表不需要跳转到详情页(右箭头只是占位)。没有 room 字段,因为摄像头的位置信息已经包含在名称里(“门口”、“客厅”、“车库”)。
接口设计的原则是"只存需要的字段"——多余的字段不仅浪费内存,还会让后续维护者困惑"这个字段到底在哪里用到了"。
快捷操作按钮
Row({ space: 12 }) {
this.ActionBtn('🔒', '锁门', () => { this.doorLocked = !this.doorLocked })
this.ActionBtn('🚨', this.alarm ? '撤防' : '布防', () => { this.alarm = !this.alarm })
this.ActionBtn('📞', '报警', () => { })
}.width('100%').padding({ left: 16, right: 16 })
三个操作按钮横向排列,每个按钮接收图标、文字、回调三个参数。
ActionBtn Builder
@Builder ActionBtn(icon: string, label: string, action: () => void) {
Column() {
Text(icon).fontSize(28).margin({ bottom: 6 })
Text(label).fontSize(12).fontColor(T1)
}.layoutWeight(1).padding(14).backgroundColor('#FFFFFF').borderRadius(14)
.onClick(action)
}
和 StatusCard 的结构一样——emoji + 文字纵向排列,白底圆角卡片。区别是 StatusCard 是纯展示(没有 onClick),ActionBtn 是可交互的(绑定了 onClick)。
"报警"按钮的空操作
this.ActionBtn('📞', '报警', () => { })
报警按钮的回调是空函数——点击后什么都不会发生。这是合理的:真正的报警功能需要接入电话系统或紧急联系人,在原型阶段无法实现。先放一个占位按钮,告诉用户"这里以后可以报警",后续再接入实际功能。
整个页面的状态管理特征
安防页面的状态管理是所有页面里最简单的——没有数组操作、没有引用变更、没有跨页面同步。每个状态都是简单的布尔值切换:
this.doorLocked = !this.doorLocked // 门锁
this.alarm = !this.alarm // 警报
不需要"复制数组 + 替换引用"的套路,因为 @State 追踪的是变量本身的引用,布尔值的切换会被直接检测到。
这是因为安防页面的数据模型比其他页面简单得多——设备页面有 17 个设备的数组需要管理,场景页面有 5 个场景的激活状态需要排他处理,而安防页面只有两个布尔值和一个摄像头列表。数据模型的复杂度直接决定了状态管理的复杂度。
两个 Builder 的对比
| 维度 | StatusCard | ActionBtn |
|---|---|---|
| 参数数量 | 4(icon, label, status, color) | 3(icon, label, action) |
| 交互 | 无(纯展示) | 有(onClick) |
| 状态颜色 | 动态传入 | 固定黑色 |
| 使用次数 | 2 次 | 3 次 |
StatusCard 的参数比 ActionBtn 多一个 color,因为状态卡片需要根据安全状态改变文字颜色。ActionBtn 不需要颜色参数——按钮文字始终是黑色,视觉焦点在图标和交互上。
两个 Builder 的视觉结构几乎一样(emoji + 文字纵向排列 + 白底圆角卡片),但职责不同。如果要强行合并成一个通用 Builder,需要加一个 interactive 参数来区分是否绑定 onClick,代码会变得更复杂。在当前规模下,分开写两个 Builder 更清晰。
页面间的数据独立性
安防页面和其他页面之间没有数据交互:
- 不从 DeviceData 读取设备信息
- 不向 DeviceData 写入安防状态
- ProfilePage 的菜单里没有"安防"入口
- 首页的设备状态卡片不包含安防设备
这种独立性是刻意的——安防系统的数据结构和设备系统完全不同,强行关联会增加不必要的耦合。在真实的智能家居 App 里,安防系统可能有自己独立的后端服务和数据存储,前端只负责展示和控制。
当前版本的安防数据全部是前端 mock,没有持久化存储。刷新页面或退出 App 后,门锁和警报状态会重置为默认值(都是 true)。后续如果要做持久化,可以用 @ohos.data.preferences 把 doorLocked 和 alarm 存到本地。
更多推荐
所有评论(0)