鸿蒙应用开发之家庭应急物资检查页深度实践:@Builder 三区拆解与状态色编码映射机制

1、引言

场景是这样的:我们团队最近在开发一款面向家庭用户的应急物资管理应用。产品经理提出了一个极其刚性的诉求——当用户打开"物资检查"页面时,不仅要一眼看到"哪些物资出了问题"的汇总数据(已过期几件、即将过期几件、库存不足几件),还需要在同一个屏幕上,逐条列出每件问题物资的明细,并给出"去补充"的处理入口。

在传统的移动端开发中,这种"告警总览 + 处理列表"的组合布局,往往需要用 Fragment 嵌套 RecyclerView,或者用多个自定义 View 拼装。状态色编码(红色已过期、橙色即将过期、橙色库存不足)散落在 Adapter 的 ViewHolder 里,维护起来极其痛苦——改一个颜色要翻三个文件。

幸运的是,在 HarmonyOS 的 ArkUI 声明式开发范式中,@Builder 装饰器给了我们一种极其优雅的方案:将页面拆分为三个独立的构建块(Header、SummaryCard、CheckList),每个块内部用 ForEach 驱动数据渲染,状态色通过数据模型的 color 字段动态注入,整页代码不到 100 行。

这篇实战手记,我们将从 emergency-kit/entry/src/main/ets/pages/Func2Tab.ets(共 97 行)切入,不仅教你如何用 @Builder 拆解检查页的三区结构,更重要的是,借此机会彻底拆解状态色编码在图标底、问题文字、类型标签三处的贯穿设计,以及告警卡计数与列表条数的数据自洽闭环。我们不背文档,不讲空话,直接看真实场景下的解法。

2、效果展示与页面结构

在正式深入代码之前,我们先看看最终的页面表现和工程底座。直观的视觉冲击能让你瞬间明白我们在解决什么问题。

检查页是"告警总览 + 处理列表"的组合布局,整体由 3 个 @Builder 块构成:

build() {
  Column() {
    this.Header()
    Scroll() {
      Column({ space: 14 }) {
        this.SummaryCard()
        this.SectionTitle('需要处理')
        this.CheckList()
      }
      .width('100%')
      .padding({ left: D.pad, right: D.pad, top: 14, bottom: D.pad + this.safeBottom + 20 })
    }
    .layoutWeight(1).scrollBar(BarState.Off).align(Alignment.Top)
  }
  .width('100%').height('100%').backgroundColor(C.bg)
}

这个结构的设计初衷是明确划分"总览"和"处理"的职责边界:

  1. Header — 单行品牌头(“物资检查”),白底固定,20 号加粗,点明本页职能
  2. SummaryCard — 橙棕渐变三列告警卡(已过期 / 即将过期 / 库存不足),24 号加粗白字
  3. CheckList — "需要处理"标题 + 4 条检查列表,每条含状态色图标、问题文字、"去补充"按钮

检查页回答的核心问题是"哪些物资有问题、怎么处理"——告警卡给结论(1 过期 / 1 临期 / 2 不足),列表给明细(4 条问题 + 去补充按钮),一页完成"发现问题 → 处理问题"的闭环。

检查页整体布局

3、Header:单行品牌头的标准范式
Header() {
  Row() { Text('物资检查').fontSize(20).fontWeight(FontWeight.Bold).fontColor(C.text) }
  .width('100%').height(this.safeTop + 56).padding({ top: this.safeTop, left: D.pad, right: D.pad })
  .backgroundColor(C.card).alignItems(VerticalAlign.Bottom)
}

功能页 Header 的标准范式:20 号字号、固定高度(安全区 + 56vp)、文字贴底对齐。与物资页、我的页同款——品牌一致性是系列应用的基本素养。

这里有一个极其关键的细节:height(this.safeTop + 56) 将状态栏安全区纳入 Header 高度,padding({ top: this.safeTop }) 把文字推到安全区之下。如果不做这层处理,在刘海屏设备上"物资检查"四个字会被状态栏遮挡——这是鸿蒙开发中最容易忽略的适配坑之一。

4、SummaryCard:三列告警渐变卡
SummaryCard() {
  Row() {
    Column({ space: 4 }) {
      Text('1').fontSize(24).fontWeight(FontWeight.Bold).fontColor('#FFFFFF')
      Text('已过期').fontSize(12).fontColor('#FFFFFF').opacity(0.9)
    }.alignItems(HorizontalAlign.Start).layoutWeight(1)
    Column({ space: 4 }) {
      Text('1').fontSize(24).fontWeight(FontWeight.Bold).fontColor('#FFFFFF')
      Text('即将过期').fontSize(12).fontColor('#FFFFFF').opacity(0.9)
    }.alignItems(HorizontalAlign.Start).layoutWeight(1)
    Column({ space: 4 }) {
      Text('2').fontSize(24).fontWeight(FontWeight.Bold).fontColor('#FFFFFF')
      Text('库存不足').fontSize(12).fontColor('#FFFFFF').opacity(0.9)
    }.alignItems(HorizontalAlign.Start).layoutWeight(1)
  }
  .width('100%').padding(20).borderRadius(D.rLg)
  .linearGradient({ angle: 135, colors: [[C.primary, 0.0], [C.accent, 1.0]] })
}

技术拆解

第一,橙棕渐变的品牌统一linearGradient({ angle: 135, colors: [[C.primary, 0.0], [C.accent, 1.0]] })——从 #D35400 渐变到 #E88050,135 度对角线方向。这与首页准备度卡同款渐变,告警卡不是独立组件,而是品牌视觉体系的一环。用户看到橙棕渐变就联想到"应急物资",这是色彩品牌化的设计目标。

第二,三列均分的布局策略Row + 三个 layoutWeight(1) Column 实现 1:1:1 等分。每列结构完全一致:24 号 Bold 白字数值 + 12 号 90% 透明度白字标签。数值大、标签小的字号对比,让用户扫一眼就能抓到关键数字(1/1/2),而不需要逐列阅读。

第三,三态告警的分类逻辑。三类问题(已过期 / 即将过期 / 库存不足)各占一列——这与 App 58 首页的三态(正常 / 临期 / 超期)不同,本页三列是"三类问题数量"的聚合统计,按问题类型而非物资状态分列。已过期是效期维度,库存不足是数量维度,两个维度的问题聚合在同一张卡上,因为对用户而言它们都是"需要处理的物资问题"。

第四,数据自洽的计数对账。1 + 1 + 2 = 4 条,恰好等于下方"需要处理"列表的 4 条——告警卡计数 = 列表条数。已过期 1 条(感冒药)、即将过期 1 条(午餐肉)、库存不足 2 条(罐头 + 急救包)。这个对账关系不是巧合,而是数据模型的设计约束:告警卡从同一份 items 数组统计,列表从同一份数组渲染,同源数据保证计数永远一致

5、CheckList:状态色编码与去补充列表
CheckList() {
  Column({ space: 10 }) {
    ForEach(this.items, (it: CheckItem) => {
      Row({ space: 12 }) {
        Row() { Text(it.emoji).fontSize(24) }
        .width(48).height(48).backgroundColor(it.color + '22').borderRadius(D.rSm).justifyContent(FlexAlign.Center)
        Column({ space: 4 }) {
          Text(it.name).fontSize(14).fontWeight(FontWeight.Medium).fontColor(C.text)
          Text(it.issue).fontSize(12).fontColor(it.color)
        }.alignItems(HorizontalAlign.Start).layoutWeight(1)
        Text('去补充').fontSize(11).fontColor('#FFFFFF')
          .padding({ left: 12, right: 12, top: 6, bottom: 6 }).backgroundColor(C.primary).borderRadius(D.rSm)
          .onClick(() => { promptAction.showToast({ message: '采购 ' + it.name }); })
      }
      .width('100%').padding(12).backgroundColor(C.card).borderRadius(D.rMd).border({ width: 1, color: C.stroke })
    }, (it: CheckItem) => it.id.toString())
  }.width('100%')
}

4 条检查数据(21-26 行):

物资 问题 类型 颜色
🥫 午餐肉罐头 将于 2026-09 过期 即将过期
💊 感冒药 已于 2026-07 过期 已过期
🥫 罐头食品 库存仅剩 6 件 库存不足
🩹 医药急救包 库存仅剩 1 个 库存不足

这段代码中有几处极其精妙的设计,是我们在多次迭代后沉淀出来的:

第一,状态色透明底图标的颜色编码backgroundColor(it.color + '22')——将状态色十六进制值拼接 '22'(13% 透明度)作为图标容器背景。过期物资的图标底是红色透明(#FF5A6E22),不足物资的图标底是橙色透明(#FF9F1C22)。图标底随问题类型变色,而非统一用主题色——这是检查页与首页的关键差异。首页的图标底用主题色软底(品牌统一),检查页的图标底用问题色(问题导向),强化"这条有问题"的视觉信号。

第二,问题文字的状态色注入Text(it.issue).fontSize(12).fontColor(it.color)——问题描述文字直接使用状态色。"将于 2026-09 过期"显示为橙色,"已于 2026-07 过期"显示为红色。文字颜色编码问题严重度:红色 = 已不能使用(最紧急),橙色 = 快到期限或快用完(次紧急)。用户扫列表时,红色文字最先被注意,优先处理。

第三,"去补充"按钮的行动导向。右侧橙色实心小按钮(C.primary 底 + 白字 + 圆角 + padding 12×6),点击弹 Toast"采购 午餐肉罐头"。这是检查页的核心交互——不只是"发现问题",还"给出动作"。过期物资"去补充"是买新的(旧的丢弃),不足物资"去补充"是补数量(6 → 12),即将过期物资"去补充"是提前补新货。三类问题的处理动作统一收敛为"去补充"一个入口,减少操作路径。

第四,状态色在三处的贯穿编码。同一状态色出现在三个位置:图标底(状态色 13% 透明)、问题文字(状态色实色)、类型标签(即将过期 / 已过期 / 库存不足)。三处同色系强化"这条有问题"的认知——图标底给"全局色感"、文字给"具体问题"、标签给"分类归属",同一颜色编码三处呈现,用户扫一眼就能判断"哪类问题、多严重"。

点击感冒药去补充按钮

6、跨页数据自洽:物资检查的全局闭环

检查页不是孤岛——它与首页、物资页、我的页形成四页数据闭环:

  • 检查页"1 已过期(感冒药)" ↔ 我的页"过期提醒"开关——感冒药已于 2026-07 过期,触发我的页"过期提醒"推送(开关开启时),过期检查 → 过期提醒
  • 检查页"午餐肉 2026-09 即将过期" ↔ 物资页"罐头"——午餐肉罐头是物资页"食品饮水"分类下的物品,保质期临近,库存管理 → 效期检查
  • 检查页"罐头食品 库存仅剩 6 件" ↔ 首页"罐头食品 偏少 6 件"、物资页"罐头 6"——数量完全一致(6 件),首页标"偏少"、检查页标"库存不足"(更紧急)、物资页可 + 补货,三页同一库存状态机
  • 检查页"医药急救包 库存仅剩 1 个" ↔ 首页"医药急救包 偏少 1 件"——数量一致(1 件),急救包是最关键应急物资(伤病急救),库存不足最需优先处理。
  • 检查页"2 库存不足" ↔ 首页"2 项物资需要补充"——数量一致(2 项),首页准备度卡的待补量 = 检查页库存不足数,首页缺口 → 检查页处理

这套闭环的核心是统一数据源——四页共享同一份物资数据模型,首页做"准备度总览"、物资页做"库存管理"、检查页做"问题处理"、我的页做"提醒配置",各司其职但数据同源。改一处库存,四页同步刷新。

7、多维度方案选型对比:检查处理页的三种形态

在实际的架构研讨中,有同学曾经发出过极其尖锐的质疑:“检查页为什么不直接用首页的’偏少列表’?为什么要单独做一个检查页?”

为了彻底解答这个选型问题,我们将系列中"检查 / 处理"型页面对比:

维度 App 60 检查页 App 58 日志页 App 53 保修页 App 56 费用页
核心定位 问题处理 历史沉淀 信息查询 数据统计
顶部卡片 三列告警渐变卡 统计卡 保修卡 月度卡 + 柱状图
列表内容 4 条问题 + 去补充按钮 时间轴记录 售后电话 费用明细
处理动作 有(去补充) 有(呼叫)
状态色编码 红 / 橙三态

60 的差异化在于**“去补充"处理按钮**——检查列表每条带橙色"去补充"按钮,是"检查 → 处理"的行动闭环。其他检查 / 统计页多为只读展示(日志页看历史、费用页看数据),而检查页不只是"发现问题”(告警卡)还"给出动作"(去补充按钮)。过期与不足都通过"去补充"解决(补新 / 补量),"发现问题 → 立即处理"的行动导向是应急场景的核心——物资问题不能拖。

8、避坑指南:检查页的四个防翻车细节

在将这套看起来优雅的检查页设计推向真实生产环境时,我们踩过了几个深浅不一的暗坑。以下是内部复盘后沉淀的防翻车铁律:

第一,过期判定必须动态化。“将于 2026-09 过期 / 已于 2026-07 过期"在演示中是写死文案——产品化必须由"当前日期 vs 有效期"动态判定。30 天内过期 = 即将过期、已过有效期 = 已过期。日期推进后状态自动变化(2026-09 到了,午餐肉从"即将过期"变"已过期”),检查列表实时刷新。如果写死文案,应用上线一个月后所有"即将过期"都变成了"已过期"但界面不更新,用户会完全失去信任。

第二,库存不足判定与首页联动。库存不足(罐头 6 / 急救包 1)与首页"偏少"是同一库存——产品化必须统一状态机。建议阈值:库存 ≤ 建议量 50% 标"偏少"(首页)、≤ 20% 标"库存不足"(检查页)。物资页加减后首页与检查页同步刷新,避免"首页偏少、检查页不足"阈值不一致导致用户困惑

第三,去补充的动作闭环。"去补充"只弹 Toast 是演示级别——产品化必须接入采购流程。点击"去补充"跳采购页 / 加购物清单 / 记录采购,采购后库存更新(罐头 6 → 12)、检查列表移除该条、首页状态刷新(偏少 → 充足)。"去补充 → 采购 → 库存更新 → 问题解决"的完整闭环,否则用户点了按钮但问题没解决,检查页就成了"只报问题不解决问题"的摆设。

第四,图标底的问题色辨识度it.color + '22' 状态色透明底在浅色 emoji 上对比清晰,但若物资图标是深色 emoji(🔦 手电)叠加红色底,辨识度可能下降。产品化可加"状态圆点"或"问题文字加粗"辅助,确保状态色在任何图标上都可辨识。另外,检查列表的排序应按紧急度——已过期(红)置顶 > 即将过期(橙)> 库存不足(橙),让"最紧急的问题"排在最上,用户打开检查页先看到红色已过期项优先处理。

点击午餐肉去补充按钮

9、常见问题 FAQ

问:检查页的 4 条问题是从哪来的?

答:演示数据设定 4 条待处理——午餐肉即将过期(2026-09)、感冒药已过期(2026-07)、罐头食品库存不足(6 件)、医药急救包库存不足(1 件),分别对应三类告警(即将过期 1、已过期 1、库存不足 2)。产品化后由物资数据动态生成:遍历所有物资,判断效期与库存生成检查项,新增物资 / 库存变化后自动更新。

问:为什么感冒药"已过期"是红色而库存不足是橙色?

答:紧急度分级——已过期(感冒药 2026-07)是"现在已经不能用了"(最紧急,红色 danger),即将过期(午餐肉 2026-09)与库存不足(罐头 / 急救包)是"快到了但还有时间"(橙色 warn)。红色 > 橙色的紧急度编码(与 App 53/55 的"超期红 / 临期橙"语义一致),用户扫列表时红色(已过期)最先被注意、优先处理。

问:"去补充"对已过期和库存不足的处理一样吗?

答:动作一样(都是"去补充"按钮)、内容不同——已过期的感冒药"去补充"是买新药(旧的丢弃),库存不足的罐头"去补充"是补数量(6 → 12),即将过期的午餐肉"去补充"是提前补新(旧的尽快吃)。统一用"去补充"入口(产品化后区分"丢弃 + 补充"流程),处理动作收敛为一个按钮减少操作路径。

问:已过期和即将过期有什么区别?

答:时间维度不同——已过期(感冒药 2026-07 已过)是"现在已经不能用了"(过期药必须丢弃,最紧急),即将过期(午餐肉 2026-09 快过)是"还没过期但快到了"(可尽快食用或提前补充,稍缓)。告警卡两列分开计数(1 已过期 / 1 即将过期),列表用红(已过期)/ 橙(即将过期)区分紧急度,红色优先处理

点击罐头食品去补充按钮

10、总结

纵观本篇对 emergency-kit 检查页(Func2Tab,97 行)的逐块拆解,@Builder 复用范式绝对不是大家在走马观花浏览 ArkUI 文档时所以为的"仅仅只是把代码拆成几个函数"那么简单。

检查页用三个 @Builder 块完成了"告警总览 + 处理列表"的组合布局:Header 点明职能、SummaryCard 给出问题汇总(1 已过期 / 1 即将过期 / 2 库存不足)、CheckList 给出明细与动作(4 条问题 + 去补充按钮)。整页的视觉语言围绕"状态色 + 行动"展开——红橙状态色编码紧急度、状态色透明图标底强化问题感、"去补充"橙色按钮突出行动,用户一页完成"发现问题 → 处理问题"的闭环。

从演示到产品,检查页的进化方向清晰:定期巡检(每月自动推送巡检结果)、分级处理(按紧急度排序 + 分级提醒)、采购清单联动(一键加入购物清单)、物资健康报告(月度健康报告)。状态色编码与去补充按钮是本页的核心,让应急物资的维护"问题可见、动作可达"。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐