鸿蒙原生应用 ArkUI 实战:2 列彩色网格 + 分类色金额 —— 票据分类页的统计架构

App 52「电子票据管理」分类页(Func2Tab),主题色 #0066CC 科技蓝,4 个 Tab 分别为首页(🧾)、上传(📤)、分类(📂)、我的(👤)。本篇基于 52-e-receipt/entry/src/main/ets/pages/Func2Tab.ets(共 70 行)逐段拆解,附 4 张实机截图。

分类页整体布局

一、整体结构:两区"Header + 分类网格"布局

分类页是极简的"品牌头部 + 统计网格"两区布局——2 个 @Builder 块:

build() {
  Column() {
    this.Header()
    Scroll() {
      Column({ space: 12 }) {
        this.CatGrid()
      }
      .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)
}

2 块结构:Header(单行品牌头)→ CatGrid(6 分类 2 列网格)。分类页是"统计页"——用网格把 48 张票据按 6 个消费分类展示(每类多少张、花了多少钱),让用户一眼看清"钱都花在哪类上"。

二、CatItem 数据模型:6 分类 + 分类色

interface CatItem {
  id: number;
  emoji: string;
  name: string;
  count: number;
  amount: number;
  color: string;
}
private cats: CatItem[] = [
  { id: 1, emoji: '🍽️', name: '餐饮', count: 12, amount: 1288, color: '#FF6B6B' },
  { id: 2, emoji: '🚕', name: '交通', count: 8, amount: 456, color: '#4A90D9' },
  { id: 3, emoji: '🏨', name: '住宿', count: 3, amount: 1560, color: '#9B51E0' },
  { id: 4, emoji: '🛒', name: '购物', count: 15, amount: 2340, color: '#27AE60' },
  { id: 5, emoji: '💼', name: '办公', count: 6, amount: 680, color: '#F2994A' },
  { id: 6, emoji: '📦', name: '其他', count: 4, amount: 320, color: '#95A5A6' }
];

CatItem 六字段含 color——每类一个专属颜色(红/蓝/紫/绿/橙/灰),分类色体系是本页最大特色。6 分类覆盖餐饮/交通/住宿/购物/办公/其他,与上传页 cats 数组完全一致。

三、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 号、safeTop + 56 固定高、文字贴底)——功能页 Header 复用范式。

四、CatGrid:六分类 2 列彩色网格

CatGrid() {
  Grid() {
    ForEach(this.cats, (c: CatItem) => {
      GridItem() {
        Column({ space: 10 }) {
          Row() { Text(c.emoji).fontSize(28) }
          .width(52).height(52).backgroundColor(c.color + '22').borderRadius(26).justifyContent(FlexAlign.Center)
          Text(c.name).fontSize(15).fontWeight(FontWeight.Bold).fontColor(C.text)
          Text(c.count + ' 张票据').fontSize(11).fontColor(C.textDim)
          Text('¥' + c.amount).fontSize(16).fontWeight(FontWeight.Bold).fontColor(c.color)
        }
        .width('100%').padding(16).backgroundColor(C.card).borderRadius(D.rLg).border({ width: 1, color: C.stroke })
        .onClick(() => { promptAction.showToast({ message: c.name + ' ' + c.count + ' 张' }); })
      }
    }, (c: CatItem) => c.id.toString())
  }.columnsTemplate('1fr 1fr').columnsGap(12).rowsGap(12).width('100%')
}

逐点拆解:

  1. 52×52 分类色透明底圆标:backgroundColor(c.color + '22')——HEX + "22" = 约 13% 透明度的分类色底(#FF6B6B22 = 红 13% 透明),emoji 28 号居中,圆形(26 半径)。每类一个专属颜色,视觉一眼区分消费类别。
  2. 分类名 + 张数:15 号 Bold 分类名 + 11 号淡灰"12 张票据"——名称主标识、张数弱化处理。
  3. 分类色金额:fontColor(c.color) ——16 号 Bold ¥1288 用分类本身的颜色——金额颜色 = 分类颜色,金额与分类强绑定,比全用主题蓝更有信息量。
  4. 2 列网格:columnsTemplate('1fr 1fr') + 12 间距——6 分类排为 3 行 2 列。
  5. 整卡点击:onClick 弹 Toast"餐饮 12 张"——产品化后进该分类的票据列表。

点击餐饮分类卡片

五、HEX 透明度拼接技巧

          .width(52).height(52).backgroundColor(c.color + '22').borderRadius(26).justifyContent(FlexAlign.Center)
          Text('¥' + c.amount).fontSize(16).fontWeight(FontWeight.Bold).fontColor(c.color)

同一 color 字段派生两种用法:

  • c.color + '22':8 位色值(#FF6B6B22)= 13% 透明度浅底——图标背景。
  • c.color:6 位色值(#FF6B6B)= 实色——金额文字颜色。

一个颜色字段同时驱动图标底色和金额颜色,保证"同色系"一致性——这是色值字符串复用的技巧,也是本页"类别色体系"的核心实现。

点击购物分类卡片

六、统计校验:六分类数字的自洽性

  { id: 1, emoji: '🍽️', name: '餐饮', count: 12, amount: 1288, color: '#FF6B6B' },
  { id: 2, emoji: '🚕', name: '交通', count: 8, amount: 456, color: '#4A90D9' },
  { id: 3, emoji: '🏨', name: '住宿', count: 3, amount: 1560, color: '#9B51E0' },
  { id: 4, emoji: '🛒', name: '购物', count: 15, amount: 2340, color: '#27AE60' },
  { id: 5, emoji: '💼', name: '办公', count: 6, amount: 680, color: '#F2994A' },
  { id: 6, emoji: '📦', name: '其他', count: 4, amount: 320, color: '#95A5A6' }

两组交叉验证:

  • 张数合计:12 + 8 + 3 + 15 + 6 + 4 = 48 张 = 我的页"票据总数 48"。
  • 金额合计:1288 + 456 + 1560 + 2340 + 680 + 320 = 6644 元 ≈ 我的页"¥6.6k"。
  • 单类均额:餐饮 1288÷12 ≈ 107 元/张(高频低额)、住宿 1560÷3 = 520 元/张(低频高额)——数据分布符合真实消费逻辑。

点击住宿分类卡片

七、系列横向对比:分类/统计网格的四种形态

统计页代表 App统计方式色彩体系核心特色
彩色网格App 52 分类2 列网格 + 分类色金额6 分类 6 色金额着色
柱状图App 47 统计手写柱状图 + 进度条主题单色图表驱动
话题网格App 42 树洞2 列网格 + 话题卡主题单色纯类别浏览
投票结果App 49 结果冠军卡 + 投票条主题单色单选高亮

差异点:App 52 的"一卡一色 + 金额着色"——每个分类有自己的颜色(红/蓝/紫/绿/橙/灰),金额也用分类色(而非主题蓝),是系列中少见的"类别色体系"——信息编码比单色方案更丰富。

八、开发者视角:调试与验证技巧

  1. HEX 透明度拼接:c.color + '22' 是字符串拼接——注意透明度值必须两位十六进制(22 ≈ 13%),如果分类色来自接口数据需确认格式(无 # 前缀会失效)。
  2. 分类色可读性:金额用分类色(如灰 #95A5A6)在白色卡片上对比度偏低——浅色分类色建议金额加深 30%(#95A5A6 → #7F8C8D)。
  3. 网格卡片高度:Grid 行高由内容撑开——6 卡 3 行 2 列在小屏可能溢出,当前 Scroll 包裹已兜底。
  4. 整卡点击:onClick 弹 Toast"餐饮 12 张"——产品化后跳该分类的票据列表(餐饮下 12 张明细)。
  5. key:c.id.toString() 稳定唯一(1-6)。

九、演进方向

方向当前状态产品化演进
动态聚合硬编码 6 类数据按消费类型分组聚合(filter + reduce)
分类跳转Toast"餐饮 12 张"进该分类票据列表(12 张明细)
二级分类6 类扁平餐饮下细分堂食/外卖/饮品
占比图表纯网格环形图/柱状图展示各类占比
空分类无无票据的分类显示空状态引导

十、一句话总结

分类页以"2 列彩色网格"完成了票据的归类总览。c.color + '22' HEX 透明度拼接派生图标浅底、fontColor(c.color) 分类色金额着色(一卡一色 + 金额同色)、columnsTemplate('1fr 1fr') 2 列网格 3 行排布——70 行代码,一个"钱都花在哪类一目了然"的统计页,也是分类色体系的典型实现。

项目源码开源:https://gitee.com/codenestFlow/HarmonyOSHub

Logo

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

更多推荐