HarmonyOS智能家居——从零搭建控制中心的开发实战总结

HarmonyOS智能家居——从零搭建控制中心的开发实战总结
做了五个页面之后回过头看整个智能家居项目的开发过程有一些在单篇讲解里分散的经验值得集中梳理。这篇从数据层设计、页面架构、交互模式三个维度做一次实战总结重点讲为什么这样做而不是做了什么。完整效果一、数据层设计为什么选模块级常量智能家居 App 的数据层是所有页面的基础。设计时面临一个选择数据存在哪里方案一每个页面各自管理自己的数据。首页的设备列表、房间页的设备列表、安防页的摄像头列表各自独立。好处是简单坏处是数据不一致——首页关了一个灯房间页还显示开着。方案二用一个全局状态管理库比如 AppStorage统一管理。好处是数据一致坏处是复杂度高需要学一套新的 API。方案三模块级常量 操作函数。所有数据在 DeviceData.ets 里定义所有页面导入同一份数据操作函数直接修改模块级数组。最终选了方案三。原因有两个第一智能家居的数据需要跨页面一致。用户在房间页关了一个灯回到首页必须看到状态变化。模块级常量天然支持这个需求——所有页面读写的是同一份内存数据。第二操作函数封装了修改逻辑。toggleDevice不只是简单地on !on它还返回了新的状态值调用方可以据此决定要不要刷新 UI。这种修改 返回的模式比只修改不返回更实用。// toggleDevice 的实现exportfunctiontoggleDevice(id:number):boolean{for(leti:number0;iDEVICES.length;i){if(DEVICES[i].idid){DEVICES[i].on!DEVICES[i].on;returnDEVICES[i].on}}returnfalse}方案三的代价是没有数据封装——任何导入 DeviceData 的页面都能直接修改 DEVICES 数组不经过操作函数。在 5 个页面的规模下这不是问题但如果页面增加到 20 个以上需要考虑引入更严格的数据访问控制。二、页面架构五种信息密度的处理方式五个页面的信息密度差异很大每种密度需要不同的处理方式低密度安防中心3 个区块导航栏 状态卡片门锁 警报 摄像头列表3 项 快捷操作3 个按钮信息少布局简单。整个页面不需要滚动——内容在一屏内就能展示完。State 只有两个布尔值状态管理最简单。中密度能耗监测4 个区块导航栏 统计卡片3 项 24小时柱状图 各房间能耗条形图 节能建议信息量中等需要用 Scroll 让内容可滚动。没有用户交互——所有数据是只读的不需要 State。高密度首页仪表盘5 个区块导航栏 Header问候语 头像 设备状态卡片 房间横向滚动 快捷控制网格 能耗柱状图 智能场景 底部导航栏信息密度最高需要在有限的屏幕空间里展示最多的内容。处理策略是分层展示——最重要的信息设备状态卡片放在最显眼的位置顶部大面积渐变次要信息能耗、场景放在下方用横向滚动节省垂直空间。交互密度高房间详情页导航栏 设备列表每个设备可能有滑块区块数量少但每个设备卡片的交互复杂度高——开关切换、亮度滑块、温度滑块。处理策略是条件渲染——设备关闭时只显示基本信息开启后展开滑块卡片高度自适应。菜单密度高设置中心导航栏 用户卡片 设备管理3 项 偏好设置5 项 系统4 项 退出登录12 个菜单项5 个开关4 个跳转链接。处理策略是分组 复用——用两套 BuilderLink 和 Toggle处理两种菜单形态用 Div 分割线分组。五种密度对应五种处理策略没有一刀切的方案。判断页面信息密度后选择对应的策略比套用固定模板更有效。三、交互模式四种设备控制方式智能家居 App 里的设备控制有四种方式每种对应不同的数据修改粒度方式一布尔开关toggleDevice.onClick((){toggleDevice(1);this.devCountgetActiveCount()})最简单的控制方式——点击一下切换开/关。适用于灯、电视、音箱等不需要参数调节的设备。操作后需要重新计算在线设备数。方式二数值滑块setDeviceValueSlider({value:d.value,min:1,max:100,step:1}).onChange((v:number){d.valueMath.round(v)this.devicesgetDevicesByRoom(this.room)})适用于需要精确控制数值的设备——灯的亮度、空调的温度。Slider 的 range 和 step 根据设备类型设定亮度 1-100 步长 1温度 16-30 步长 1。方式三场景激活activateSceneprivateactivateScene(idx:number):void{for(leti...){this.scenes[i].activefalse}this.scenes[idx].activetrue// 复制数组触发刷新}一键触发多个设备的联动操作。排他逻辑保证同一时间只有一个场景运行。操作后需要复制数组触发 State 刷新。方式四安防状态切换本地 Statethis.doorLocked!this.doorLockedthis.alarm!this.alarm最轻量的控制方式——直接切换布尔值不需要调用操作函数不需要重新查询数组。适用于安防、设置等独立页面的本地状态。四种方式的共同点是操作后刷新 UI区别在于刷新策略方式数据修改刷新策略复杂度布尔开关toggleDevice重新统计在线数低数值滑块setDeviceValue重新查询房间设备中场景激活直接修改 active复制数组替换引用高安防切换直接修改 boolean无需额外刷新低四、颜色编码的系统化整个项目的颜色不是随便选的有一套系统化的编码规则功能颜色颜色值含义使用场景主色#6C5CE7科技感、品牌色渐变背景、强调文字绿色#00B894安全、在线、正常门锁已锁定、设备在线红色#FF6B6B警告、离线、不安全门锁未锁定、设备离线橙色#FF9F43预警、暖色强调本月预估用电、空调滑块层次颜色颜色值用途T1#1E1B2E标题文字最高层级T2#888888副文字中间层级T3#BBBBBB辅助灰最低层级BG#F5F4FA页面背景CW#FFFFFF卡片背景功能颜色编码了状态含义——用户看到绿色就知道正常看到红色就知道有问题。层次颜色编码了信息层级——T1 最重要T2 次之T3 最弱。两套颜色系统互不干扰共同构成了页面的视觉秩序。颜色透明度的运用项目里大量使用了颜色 透明度的组合A25// 主色 14% 透明度 → 投影颜色A30// 主色 18% 透明度 → 房间卡片边框s.color15// 场景色 8% 透明度 → 场景卡片背景rgba(0,0,0,0.03)// 黑色 3% 透明度 → 返回按钮背景透明度让颜色更柔和——纯色太强烈加了透明度后变成淡淡的色调适合做大面积的背景色或边框色。五、State 刷新的三种模式整个项目里 State 的刷新可以归纳为三种模式模式一重新查询赋值// RoomPage - 切换设备后this.devicesgetDevicesByRoom(this.room)// Index - 切换场景后this.scenescopy// 复制后的新数组适用于数组类型的 State。修改源数据后通过重新查询或复制得到新数组替换 State 的引用。ArkTS 检测到引用变化后触发重新渲染。模式二重新计算赋值// Index - 设备状态变化后this.devCountgetActiveCount()适用于数值类型的 State。修改源数据后重新调用计算函数获取最新值赋值给 State。不需要复制数组直接赋值就行。模式三直接赋值// SettingsPage - 开关切换this.notifyOn!this.notifyOn// SecurityPage - 安防状态this.doorLocked!this.doorLocked适用于布尔类型的 State。基本类型值的直接赋值会被 State 检测到不需要额外处理。三种模式的判断标准数组用模式一数值用模式二布尔用模式三。记住这个规则就覆盖了 90% 的 State 刷新场景。六、Builder 的设计原则整个项目用了 7 个 Builder总结出三条设计原则原则一只抽出现两次以上的结构如果一个结构只在页面里出现一次内联写就行不需要抽 Builder。出现两次以上时再抽取——过早抽取会让代码变得碎片化。原则二参数只传会变的// 好的只传会变的参数BuilderActionBtn(icon:string,label:string,action:()void)// 不好的传了不会变的样式参数BuilderActionBtn(icon:string,label:string,color:string,fontSize:number,action:()void)样式参数颜色、字号、间距通常在页面内是固定的不需要作为 Builder 的参数。只有不同实例之间会不同的值才应该传参。原则三回调参数用 () void当 Builder 需要执行不同操作时用action: () void回调参数而不是在 Builder 内部硬编码操作逻辑。这让 Builder 保持通用性调用方决定点了之后做什么。七、项目结构的组织方式entry/src/main/ets/ pages/ Index.ets 首页入口 五个区块 RoomPage.ets 房间详情设备控制 SecurityPage.ets 安防中心状态展示 EnergyPage.ets 能耗监测数据可视化 SettingsPage.ets 设置中心菜单列表 services/ DeviceData.ets 数据定义 操作函数页面按功能命名一目了然。服务层只有一个文件因为数据量不大不需要拆分。文件命名的规则页面用XxxPage.ets服务层用XxxData.ets或XxxService.ets。这种命名约定让打开文件目录就能知道每个文件的职责。如果后续页面增加可以考虑按功能模块拆分文件夹pages/ home/Index.ets room/RoomPage.ets security/SecurityPage.ets ...但在 5 个页面的规模下平铺结构更简单不需要提前设计文件夹层级。八、开发顺序的建议如果从零开始做这个项目建议的开发顺序是DeviceData.ets先把数据层搭好定义接口和 mock 数据Index.ets首页是入口先做出来能看到整体效果RoomPage.ets最核心的功能页面设备控制在这里SecurityPage.ets最简单的页面验证 State 基本用法EnergyPage.ets纯展示页面验证数据可视化布局SettingsPage.ets菜单最多的页面验证 Builder 复用先做数据层再做页面先做核心功能再做辅助功能。每做完一个页面就预览一次确认 UI 效果后再做下一个。不要试图一次性把所有页面都写完——分步开发能让你在早期发现问题避免后期大规模返工。