鸿蒙原生应用实战:四色占比进度条驱动的分类记录页三态操作设计

发布时间:2026/9/4 22:08:30
鸿蒙原生应用实战:四色占比进度条驱动的分类记录页三态操作设计 鸿蒙原生应用实战四色占比进度条驱动的分类记录页三态操作设计App 15「智能垃圾分类」分类 TabFunc2Tab是分类历史记录页。整页用 Header 4 个胶囊筛选 Tab全部/已分类/待处理/错误分类 3 栏统计概览 4 类彩色占比进度条可回收 30% 蓝/厨余 48% 绿/有害 12% 红/其他 10% 灰 6 条记录时间线每条记录按状态显示不同操作待处理→立即分类、错误分类→重新分类、已分类→✓ 已正确分类组成。本篇基于15-trash-classify/entry/src/main/ets/pages/Func2Tab.ets约 248 行逐段拆解附 4 张实机截图。一、整体结构筛选 统计 占比 时间线Func2Tab 与 App 14 预约页同构Header TabBar 固定、Scroll 内容流但多了分类占比模块build() { Column() { this.Header() this.TabBar() Scroll() { Column({ space: 16 }) { this.StatsOverview() this.CategoryProgress() this.SectionTitle(分类记录) this.RecordTimeline() } .width(100%) .padding({ left: D.pad, right: D.pad, top: 16, bottom: D.pad this.safeBottom 20 }) } .layoutWeight(1).scrollBar(BarState.Off).align(Alignment.Top) } .width(100%).height(100%).backgroundColor(C.bg) }4 个模块StatsOverview3 栏统计→CategoryProgress4 类占比——本页特色→ SectionTitle(分类记录) → RecordTimeline6 条时间线。与 App 14 预约页的差异App 14 是统计 → 时间线两段App 15 是统计 → 占比 → 时间线三段——多了一层分类构成分析。占比图让用户一眼看到我分类的垃圾都是什么类型是数据可视化的进阶用法。项目源码开源https://gitee.com/codenestFlow/HarmonyOSHub二、Header TabBar4 个胶囊筛选Header 单行标题分类记录TabBar 是 4 个胶囊全部/已分类/待处理/错误分类Builder 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) } Builder TabBar() { Row() { ForEach(this.tabs, (t: FilterTab) { Text(t.label) .fontSize(13) .fontColor(this.activeTab t.id ? C.primary : C.textSub) .fontWeight(this.activeTab t.id ? FontWeight.Bold : FontWeight.Normal) .padding({ left: 14, right: 14, top: 8, bottom: 8 }) .backgroundColor(this.activeTab t.id ? C.primarySoft : transparent) .borderRadius(16) .onClick(() { this.activeTab t.id; }) }, (t: FilterTab) t.id.toString()) } .width(100%) .padding({ left: D.pad, right: D.pad, top: 12, bottom: 12 }) .backgroundColor(C.card) }4 个 Tab 的 id 与状态值绑定App 14 同款设计private tabs: FilterTab[] [ { id: 0, label: 全部 }, { id: 1, label: 已分类 }, { id: 2, label: 待处理 }, { id: 3, label: 错误分类 } ];id status 值id1 已分类records 里 status1、id2 待处理status2、id3 错误分类status3——筛选逻辑r.status this.activeTab一行搞定。与 App 14 的差异App 14 的 Tab 顺序是全部/待确认/已完成/已取消按待办优先排列App 15 是全部/已分类/待处理/错误分类按数量优先排列已分类 302 条最多放第 2——Tab 顺序反映业务重心。三、StatsOverview3 栏统计StatsOverview 与 App 14 同构3 栏数字 标签Builder StatsOverview() { Row() { ForEach(this.stats, (s: StatItem) { Column({ space: 4 }) { Text(s.value).fontSize(22).fontWeight(FontWeight.Bold).fontColor(C.text) Text(s.label).fontSize(11).fontColor(C.textDim) } .layoutWeight(1) .alignItems(HorizontalAlign.Center) }, (s: StatItem) s.label) } .width(100%) .padding({ top: 16, bottom: 16 }) .backgroundColor(C.card).borderRadius(D.rMd) .border({ width: 1, color: C.stroke }) }数据总记录 328 / 已分类 302 / 待处理 26。302 26 328数据自洽已分类 待处理 总记录错误分类记录在 302 里计数还是单独demo 里错误分类是 status3如果算进已分类口径有歧义——但 328 302 26 至少总 已 待成立。3 栏同色C.text深色——颜色留给状态原则占比进度条和状态标签才是彩色的。四、CategoryProgress四色占比进度条本页特色CategoryProgress 是 4 类垃圾的占比进度条每类一条彩色进度条Builder CategoryProgress() { Column({ space: 12 }) { Text(分类占比).fontSize(15).fontWeight(FontWeight.Bold).fontColor(C.text).width(100%) Row({ space: 8 }) { Text(♻️ 可回收).fontSize(11).fontColor(C.textDim).width(60) Progress({ value: 30, total: 100 }).color(#3B82F6).backgroundColor(C.cardSoft).layoutWeight(1).height(6) Text(30%).fontSize(11).fontColor(C.textSub) } Row({ space: 8 }) { Text( 厨余).fontSize(11).fontColor(C.textDim).width(60) Progress({ value: 48, total: 100 }).color(#22C55E).backgroundColor(C.cardSoft).layoutWeight(1).height(6) Text(48%).fontSize(11).fontColor(C.textSub) } Row({ space: 8 }) { Text(⚠️ 有害).fontSize(11).fontColor(C.textDim).width(60) Progress({ value: 12, total: 100 }).color(#EF4444).backgroundColor(C.cardSoft).layoutWeight(1).height(6) Text(12%).fontSize(11).fontColor(C.textSub) } Row({ space: 8 }) { Text(️ 其他).fontSize(11).fontColor(C.textDim).width(60) Progress({ value: 10, total: 100 }).color(#9CA3AF).backgroundColor(C.cardSoft).layoutWeight(1).height(6) Text(10%).fontSize(11).fontColor(C.textSub) } } .width(100%) .padding(14) .backgroundColor(C.card).borderRadius(D.rMd) .border({ width: 1, color: C.stroke }) }4.1 4 条进度条的数据厨余 48%绿色——占比最高符合厨余垃圾是日常生活产生最多垃圾的事实可回收 30%蓝色有害 12%红色其他 10%灰色30 48 12 10 100%数据完美闭合。占比数据有现实依据厨余最多、有害最少——中国家庭垃圾构成确实如此。4.2 四色进度条的实现Row({ space: 8 }) { Text(♻️ 可回收).fontSize(11).fontColor(C.textDim).width(60) Progress({ value: 30, total: 100 }).color(#3B82F6).backgroundColor(C.cardSoft).layoutWeight(1).height(6) Text(30%).fontSize(11).fontColor(C.textSub) }每行三要素左侧emoji 分类名固定 60vp 宽四行对齐、中间Progress.color(#3B82F6)每类颜色写死、右侧百分比数字。与 App 14 的差异App 14 是一组同色进度条App 15 是四色进度条——颜色与分类一一对应蓝/绿/红/灰延续首页的四色编码系统。四色占比图本质是堆叠条形图的横排版——用 4 条独立进度条表达各部分占总体的比例比一个环形图实现简单得多不用画弧形视觉上也清晰。真实项目可升级为环形占比图ProgressType.CircularProgress({ value: 48, total: 100, type: ProgressType.Circular }) .color(#22C55E).width(80).height(80)但 4 个环形图太占空间4 条横排进度条是构成占比的最佳空间方案一个卡片展示 4 类。每行的百分比 进度条双通道30% 数字 蓝色进度条色弱用户能读数字。五、RecordTimeline三态操作时间线RecordTimeline 是 6 条分类记录的时间线每条记录按状态显示不同操作Builder RecordTimeline() { Column({ space: 0 }) { ForEach(this.filtered(), (r: Record, idx: number) { Column({ space: 0 }) { if (idx 0) { Row().width(2).height(12).backgroundColor(C.stroke).margin({ left: 18 }) } Row({ space: 12 }) { Column() { Row() { Text(r.emoji).fontSize(18) } .width(36).height(36) .backgroundColor(r.catColor 1A).borderRadius(18) .justifyContent(FlexAlign.Center) } .width(36) Column({ space: 8 }) { Row({ space: 8 }) { Text(r.item).fontSize(14).fontWeight(FontWeight.Medium).fontColor(C.text) Text(r.amount).fontSize(11).fontColor(C.textDim) Blank() Text(r.statusText) .fontSize(11).fontColor(r.statusColor) .padding({ left: 8, right: 8, top: 3, bottom: 3 }) .backgroundColor(C.primarySoft).borderRadius(8) } .width(100%) Row({ space: 6 }) { Text(r.category) .fontSize(11).fontColor(r.catColor) .padding({ left: 6, right: 6, top: 2, bottom: 2 }) .backgroundColor(r.catColor 1A).borderRadius(4) Text(r.date r.time).fontSize(12).fontColor(C.textDim) } if (r.status 2) { Row({ space: 8 }) { Button(立即分类) .fontSize(12).fontColor(#FFFFFF).backgroundColor(C.primary) .borderRadius(D.rSm).height(30) .onClick(() { promptAction.showToast({ message: 处理 r.item }); }) Text(待处理).fontSize(11).fontColor(C.warn) } .width(100%) } if (r.status 3) { Row({ space: 8 }) { Button(重新分类) .fontSize(12).fontColor(#FFFFFF).backgroundColor(C.danger) .borderRadius(D.rSm).height(30) .onClick(() { promptAction.showToast({ message: 重新分类 r.item }); }) Text(分类有误).fontSize(11).fontColor(C.danger) } .width(100%) } if (r.status 1) { Text(✓ 已正确分类).fontSize(11).fontColor(C.ok) } } .alignItems(HorizontalAlign.Start).layoutWeight(1) } .width(100%) .padding({ left: 14, right: 14, top: 14, bottom: 14 }) .backgroundColor(C.card).borderRadius(D.rMd) .border({ width: 1, color: C.stroke }) } .width(100%) }, (r: Record) r.id.toString()) } .width(100%) }5.1 数据模型分类色 状态色双通道interface Record { id: number; emoji: string; item: string; category: string; catColor: string; date: string; time: string; status: number; statusText: string; statusColor: ResourceColor; amount: string; }Record 同时携带catColor分类色和statusColor状态色——两类颜色语义分类色catColor垃圾属于哪类蓝/红/绿/灰→ 图标块背景 分类标签状态色statusColor记录处理状态绿已分类/橙待处理/红错误→ 状态标签分类和状态是两个维度用两套颜色表达互不干扰——**多维度信息用多套颜色编码**是数据可视化的关键。5.2 双标签设计Text(r.category) .fontSize(11).fontColor(r.catColor) .padding({ left: 6, right: 6, top: 2, bottom: 2 }) .backgroundColor(r.catColor 1A).borderRadius(4) Text(r.date r.time).fontSize(12).fontColor(C.textDim)一行两个标签左侧分类标签catColor文字 catColor 1A同色浅底如蓝字淡蓝底可回收物 右侧日期时间。分类标签让用户不用点进详情就知道这是什么类型的垃圾。5.3 三态操作区if (r.status 2) { Row({ space: 8 }) { Button(立即分类) .fontSize(12).fontColor(#FFFFFF).backgroundColor(C.primary) .borderRadius(D.rSm).height(30) .onClick(() { promptAction.showToast({ message: 处理 r.item }); }) Text(待处理).fontSize(11).fontColor(C.warn) } .width(100%) } if (r.status 3) { Row({ space: 8 }) { Button(重新分类) .fontSize(12).fontColor(#FFFFFF).backgroundColor(C.danger) .borderRadius(D.rSm).height(30) .onClick(() { promptAction.showToast({ message: 重新分类 r.item }); }) Text(分类有误).fontSize(11).fontColor(C.danger) } .width(100%) } if (r.status 1) { Text(✓ 已正确分类).fontSize(11).fontColor(C.ok) }3 个状态分支3 种操作状态按钮颜色右侧文字待处理status2立即分类绿C.primary待处理 橙错误分类status3重新分类红C.danger分类有误 红已分类status1无按钮—✓ 已正确分类 绿关键差异——错误分类的按钮是红色Button(重新分类) .fontSize(12).fontColor(#FFFFFF).backgroundColor(C.danger)App 14 预约页的所有操作按钮都是主题绿C.primaryApp 15 的重新分类用了危险红——错误用红色强化警示用户看到红按钮知道这条有问题要处理。**操作按钮颜色跟随状态严重性**是状态驱动 UI的进阶——正常操作绿、错误修复红。✓ 已正确分类是完成态标识绿字勾不需要操作按钮——无需操作的状态只显示确认信息。5.4 数据与筛选filtered()与 App 14 同款activeTab 0返回全部否则filter(status activeTab)——真筛选。6 条记录已分类 4 条废电池/旧报纸/香蕉皮/大骨头 待处理 1 条塑料瓶 错误分类 1 条灯泡——三态都有数据切换 Tab 能看到不同列表。6 条记录按日期倒序08-14 → 08-09时间线呈现最近 → 最远顺序。六、与 App 14 预约记录页的对比维度App 14 预约记录App 15 分类记录Tab待确认/已完成/已取消已分类/待处理/错误分类统计3 栏3 栏特色模块无四色占比进度条图标块主题绿同色分类色四色分类标签无有分类色标签状态按钮取消/再次预约绿立即分类绿/重新分类红App 15 的两个进步① 新增占比进度条构成分析② 引入分类色 状态色双色编码系统。记录列表模板在系列中不断进化——每次加一个新能力真筛选→时间线→占比→双色编码。七、四色编码系统在记录页的完整应用App 15 的四色编码系统蓝可回收/红有害/绿厨余/灰其他在本页的 4 处应用占比进度条4 条进度条各用各的颜色时间线图标块r.catColor 1A同色浅底废电池红色浅底、旧报纸蓝色浅底……分类标签catColor文字 同色浅底与首页联动首页入口/指南卡也是四色一个颜色系统贯穿全 App——用户在任何页面看到蓝色♻️就知道是可回收物。跨页颜色一致性是品牌识别度的最高形式。实现catColor字段存数据里#EF4444红渲染时r.catColor 1A拼浅底。颜色进数据让记录页零硬编码。八、扩展建议占比图联动筛选点击厨余 48%进度条 → 自动切换到厨余筛选真实项目可实现环形占比图新增占比总览卡片用ProgressType.Circular环形图展示总量构成分页加载328 条记录应分页.onReachEnd()上滑加载详情页立即分类/重新分类应跳转识别页并预填物品名跨 Tab 联动删除操作记录应支持左滑删除SwipeAction组件记录 占比 操作模板可复用到垃圾分类、记账分类、消费构成、时间统计——任何按类别记录 构成分析 状态操作的场景。九、分类占比的业务价值分类占比模块不只是好看它承载 3 层业务价值1. 反馈层——我的分类构成。用户看到厨余 48%会想我厨余垃圾最多下次多注意可回收。占比图让用户建立我每天都在分类什么的自我认知——这是环保教育的第一步知道自己分类的对象。2. 统计层——社区/城市的分类结构。真实项目里占比数据可以聚合为全校/全小区分类构成厨余 48% 说明该区域餐饮垃圾多辅助环保部门调整垃圾分类设施多放厨余桶还是可回收桶。个人占比 → 群体占比是数据产品的增值路径。3. 激励层——行为引导。如果可回收 30%偏低App 可提示回收利用率低于全国平均试试把纸箱压扁再投递——占比 建议驱动行为改变比单纯记录高级得多。实现注意当前占比是硬编码30/48/12/10真实项目应从分类记录实时聚合records.filter(r r.category 可回收物).length / records.length * 100——占比跟着记录走新增一条记录占比自动更新。**展示数据永远从业务数据推导**是数据架构的铁律。十、时间线的分类色 状态色双通道无障碍本页每一条记录同时携带两个颜色维度分类色 catColor 状态色 statusColor这对色弱用户特别重要分类信息图标块背景色红浅底/蓝浅底… 分类标签文字红字/蓝字…——即使分不清红蓝也能读有害垃圾可回收物文字状态信息状态标签文字待处理橙/分类错误红/已分类绿 按钮文案立即分类重新分类——颜色只是辅助文字才是主通道颜色 文字双通道是本系列反复强调的无障碍原则本页是执行最彻底的分类、状态、数量、时间全部有文字颜色只是锦上添花的强化。色弱用户约 8% 人口不靠颜色也能完整使用本页。对比反例如果只靠图标块颜色深浅区分分类不写可回收物文字色弱用户就完全无法使用。先文字后颜色是数据展示的底线。十一、状态机的待处理 → 已分类 → 错误分类流转本页 3 个状态背后的业务状态机新记录 → 待处理 → 已分类✓ 正确 ↘ 错误分类 → 重新分类 → 已分类待处理status2AI 识别后待人工确认 → 立即分类按钮处理已分类status1人工确认完成 → 显示✓ 已正确分类错误分类status3AI 识别错误 → 重新分类红色按钮纠错待处理 → 立即分类 → 已分类和错误分类 → 重新分类 → 已分类两条路径都通向终态已分类——状态机有汇合convergence是真实业务的常见形态AI 先识别、人工后确认。UI 的操作按钮就是状态机的事件立即分类处理事件、重新分类纠错事件。真实项目应做状态流转校验待处理不能直接变错误分类、错误分类不能变待处理——UI 按钮只是入口状态机逻辑才是真相。十二、小结App 15 分类记录页解析完毕。四色占比进度条构成分析 分类色/状态色双编码 三态操作绿处理/红纠错/绿确认 状态机流转是四大亮点。