当前位置: 首页 > news >正文

鸿蒙原生应用实战(五):数据统计与个人中心——柱状图实现、统计计算与设置面板

鸿蒙原生应用实战(五):数据统计与个人中心——柱状图实现、统计计算与设置面板

本文是系列终篇,讲解快递追踪 App 中最后两个页面:包裹统计(PackageStatsPage)和个人中心(ProfilePage)。涵盖纯 ArkTS 柱状图实现、统计指标计算、Toggle 设置、主题色体系等完整内容,并总结全项目架构。


一、PackageStatsPage — 包裹统计页

1.1 功能需求

统计页是 App 的数据分析中心,需要展示:

  1. 总览卡片:总包裹数、已签收数、平均时效
  2. 月度趋势图:每月收发数量 + 柱状条可视化
  3. 快递公司分布:各公司使用数量 + 横向柱状条

1.2 数据模型

interfaceMonthlyStats{month:string;// 月份标识,如 "2024-08"count:number;// 当月包裹总数delivered:number;// 当月已签收数avgDays:number;// 当月平均时效(天)}interfaceCompanyStats{name:string;// 公司名count:number;// 使用次数color:string;// 柱状图颜色}

注意CompanyStatscolor是字符串类型(如'#FF4A90D9'),因为在 ArkTS 中动态颜色字符串可以通过fill()直接传入。

1.3 数据初始化

使用aboutToAppear生命周期进行数据初始化:

aboutToAppear():void{// 月度数据letm1:MonthlyStats={month:'2024-08',count:3,delivered:3,avgDays:2.5};letm2:MonthlyStats={month:'2024-09',count:5,delivered:4,avgDays:3.0};// ...this.monthlyData=[m1,m2,m3,m4,m5,m6];// 公司分布letc1:CompanyStats={name:'顺丰速运',count:8,color:'#FF4A90D9'};// ...this.companyStats=[c1,c2,c3,c4,c5,c6];}

为什么在aboutToAppear而不是build中初始化?

  • 分离数据准备和 UI 构建
  • 数据初始化只执行一次,而非每次渲染

1.4 ArkTS 严格对象字面量规则

在 ArkTS 严格模式下(arkts-no-untyped-obj-literals),不能直接写:

// ❌ 编译错误:对象字面量必须有显式类型this.monthlyData=[{month:'2024-08',count:3,delivered:3,avgDays:2.5}];

必须将每个对象字面量赋值给类型化变量后再引用:

// ✅ 正确做法:提取为独立类型变量letm1:MonthlyStats={month:'2024-08',count:3,delivered:3,avgDays:2.5};this.monthlyData=[m1];

这个规则初看繁琐,但有助于提升代码可读性和类型安全。

1.5 总览卡片

Column(){Text('总览')Row(){Column(){Text(this.totalPackages.toString()).fontSize(32).fontWeight(FontWeight.Bold).fontColor($r('app.color.primary'))Text('总包裹数')}Column(){Text(this.totalDelivered.toString()).fontSize(32).fontWeight(FontWeight.Bold).fontColor($r('app.color.status_delivered'))Text('已签收')}Column(){Text(this.overallAvgDays+'天').fontSize(24).fontWeight(FontWeight.Bold).fontColor($r('app.color.rating_star'))Text('平均时效')}}}

三个指标,三种颜色

指标字体大小颜色含义
总包裹数32fp主题蓝核心指标
已签收数32fp签收绿正向指标
平均时效24fp (稍小)星标黄参考指标

1.6 计算属性(getter)

gettotalPackages():number{letsum=0;for(letmofthis.monthlyData){if(m)sum+=m.count;}returnsum;}gettotalDelivered():number{letsum=0;for(letmofthis.monthlyData){if(m)sum+=m.delivered;}returnsum;}getoverallAvgDays():string{letsum=0;letcount=0;for(letmofthis.monthlyData){if(m&&m.delivered>0){sum+=m.avgDays*m.delivered;count+=m.delivered;}}returncount>0?(sum/count).toFixed(1):'0';}

加权平均计算avgDays是按包裹数加权的,即(avgDays1 * delivered1 + avgDays2 * delivered2 + ...) / totalDelivered,这样更准确。


二、纯 ArkTS 柱状图实现

没有使用第三方图表库,完全用 ArkTS 原生组件绘制。

2.1 月度趋势柱状图

ForEach(this.monthlyData,(month:MonthlyStats)=>{Column(){// 行标签:月份 + 数量Row(){Text(month.month).width(70)Blank()Text(month.count+'件')}// 柱状条Row(){Column(){Row(){Column().width((month.count/8)*100+'%')// ← 比例宽度.height(16).backgroundColor($r('app.color.primary')).borderRadius({topLeft:8,bottomLeft:8})}.width('100%').backgroundColor('#FFF0F0F0')// 灰色背景条.borderRadius(8)}.layoutWeight(1)}// 辅助信息Text('已签收: '+month.delivered+' | 平均 '+month.avgDays+'天')}},(month:MonthlyStats)=>month.month)

实现原理

  1. 设定最大值8(数据中最大包裹数),作为 100% 基准
  2. 每个柱的宽度 =(count / 8) * 100%
  3. 灰色背景条(#FFF0F0F0)总是 100% 宽,蓝色前景条按比例填充
  4. borderRadius({ topLeft: 8, bottomLeft: 8 })让柱状条左侧圆角

2.2 快递公司分布图

ForEach(this.companyStats,(stat:CompanyStats)=>{Row(){// 颜色圆点 + 公司名Circle().width(12).height(12).fill(stat.color)Text(stat.name).width(80).margin({left:8})// 柱状条Column(){Row(){Column().width((stat.count/8)*100+'%').height(14).backgroundColor(stat.color)// ← 使用公司专属颜色.borderRadius(7)}.width('100%').backgroundColor('#FFF0F0F0').borderRadius(7)}.layoutWeight(1)// 数量标签Text(stat.count+'件').width(40).textAlign(TextAlign.End)}},(stat:CompanyStats)=>stat.name)

与月度趋势的不同

  • 柱状条使用公司专属颜色而非统一蓝色
  • 左侧多了颜色圆点 + 公司名
  • 右侧多了数量文字标签
  • 柱状条高度 14vp(稍细)

2.3 柱状图比例计算的风险

.width((stat.count/8)*100+'%')

如果最大值8是硬编码的,当数据变化(比如某月有 10 个包裹),柱状条会超出 100%。更健壮的方式:

// 计算实际最大值getmaxCount():number{letmax=1;// 避免除以 0for(letmofthis.monthlyData){if(m.count>max)max=m.count;}returnmax;}// 动态计算宽度.width((month.count/this.maxCount)*100+'%')

但为了示例简洁,当前版本使用了固定最大值。


三、ProfilePage — 个人中心

3.1 功能需求

  1. 用户信息卡片:头像 + 昵称 + 追踪数量
  2. 设置项:通知开关、常用快递公司、关于
  3. 包裹统计:运输中 / 已签收 / 异常 数量
  4. 快捷操作:反馈、评分、分享、同步

3.2 用户信息卡片

Column(){Circle().width(64).height(64).fill($r('app.color.primary'))Text('快递追踪用户')Text('已追踪 8 个包裹').fontSize($r('app.float.small_font_size')).fontColor($r('app.color.text_hint'))}.alignItems(HorizontalAlign.Center)

使用圆形头像 + 居中布局,简洁的用户信息展示。

3.3 设置项列表

Column(){// 通知设置(带 Toggle 开关)Row(){Text($r('app.string.notification_setting'))Blank()Toggle({type:ToggleType.Switch,isOn:this.notifyEnabled}).onChange((value:boolean)=>{this.notifyEnabled=value;})}.height(52)Divider().width('100%')// 常用快递公司(带跳转箭头)Row(){Text($r('app.string.courier_company'))Blank()Text('顺丰、圆通、中通 ...').fontColor($r('app.color.text_hint'))Text('>').fontColor($r('app.color.text_hint'))}.height(52)Divider()// 关于Row(){Text($r('app.string.about_app'))Blank()Text('v1.0.0').fontColor($r('app.color.text_hint'))}.height(52)}

设置项设计模式

  • 每行固定高度 52vp,保证点击区域足够大
  • Divider分隔每一项
  • 右侧统一使用text_hint灰色显示辅助信息
  • >箭头暗示可点击跳转

3.4 包裹统计卡片

Column(){Text('包裹统计').fontWeight(FontWeight.Medium)Row(){Column(){Text('3').fontColor($r('app.color.primary'))Text('运输中')}Column(){Text('5').fontColor($r('app.color.status_delivered'))Text('已签收')}Column(){Text('1').fontColor($r('app.color.status_exception'))Text('异常')}}}

与首页状态颜色保持一致:运输中(蓝色)、已签收(绿色)、异常(红色)。三个统计值之和为 3+5+1=9,与首页数据对应。

3.5 快捷操作区

Row(){Column(){Text('📋');Text('问题反馈')}Column(){Text('⭐');Text('评分')}Column(){Text('📤');Text('分享')}Column(){Text('🔄');Text('同步')}}

四宫格布局设计

  • 使用layoutWeight(1)均匀分布
  • Emoji 图标 + 小字标签
  • 每个 Column 居中排列
  • 四列等宽,视觉平衡

四、颜色与主题体系总结

4.1 全项目颜色映射

资源名色值用途
primary#FF4A90D9主题蓝:标题、按钮、选中态
background#FFF5F5F5页面背景
card_bg#FFFFFF卡片背景
text_primary#FF333333主文字
text_secondary#FF666666次要文字
text_hint#FF999999提示文字
divider#FFE0E0E0分割线
status_transit#FFFF8C00运输中(橙色)
status_delivered#FF4CAF50已签收(绿色)
status_exception#FFF44336异常(红色)
rating_star#FFFFC107星标黄

4.2 字号体系

资源名用途
page_title_font_size22fp页面标题
body_font_size16fp正文/列表标题
small_font_size13fp次要信息
badge_font_size11fp状态标签

4.3 间距体系

资源名用途
padding_small8vp小间距
padding_medium16vp卡片内边距
padding_large24vp大间距

五、全项目架构总结

5.1 页面关系图

Index (首页) ├── → AddPackagePage (添加包裹) ├── → TrackDetailPage (物流详情,带参数) ├── → SearchPage (搜索页) ├── → PackageStatsPage (统计页) └── → CompanyManagePage (公司管理) ProfilePage (个人中心) ├── 通知设置 (Toggle) ├── 常用公司 → CompanyManagePage └── 快捷操作 SearchPage (搜索) ├── TextInput + 筛选标签 └── → TrackDetailPage (搜索结果点击) HistoryPage (历史记录) └── 清空 + 底部统计

5.2 技术栈一览

技术维度使用方案
开发模型Stage 模型
UI 语言ArkTS(ArkUI 声明式语法)
状态管理@State 装饰器
路由@ohos.router(API 23)
资源管理$r() 引用 string/color/float
构建工具Hvigor
列表渲染List + ForEach + ListItem
生命周期aboutToAppear / build

5.3 关键设计决策

  1. 纯原生组件,零第三方依赖:所有 UI 均使用 ArkUI 内置组件,未引入 OHPM 第三方包
  2. 资源集中管理:颜色、字号、间距统一在element/JSON 中定义,通过$r()引用
  3. TypeScript 严格模式适配:对象字面量必须有显式类型,数组必须可推断
  4. 防御性编程:路由参数判空、默认值兜底
  5. 模拟数据驱动:所有数据使用内联 mock 数据,便于后续接入后端 API

5.4 可扩展方向

  • 数据持久化:当前数据在内存中,关闭应用即丢失。可集成@ohos.data.preferences@ohos.data.relationalStore实现持久化
  • 网络请求:接入真实快递查询 API,使用@ohos.net.http发起 HTTP 请求
  • 推送通知:集成通知服务,包裹状态变化时推送
  • 多端适配:当前仅适配 phone,可扩展支持 tablet、wearable
  • 动画优化:添加列表项入场动画、状态切换过渡动画

六、写在最后

6.1 本系列回顾

五篇文章完整记录了一个鸿蒙原生应用从零到一的开发过程:

篇目核心内容技术要点
第一篇项目初始化与工程架构Stage 模型、module.json5、路由注册
第二篇首页与列表开发List + ForEach、@State、空状态
第三篇表单交互与搜索筛选表单验证、多条件搜索、Toggle 开关
第四篇物流时间线与历史记录时间线 UI、router 传参、Line 组件
第五篇数据统计与个人中心柱状图、统计计算、设置面板

6.2 开发心得

  1. ArkTS 声明式语法非常直观。写过 React/Vue 的开发者上手极快,@State+ 数据驱动 UI 的模式与前端框架一脉相承。

  2. 鸿蒙的资源管理值得学习。通过$r()引用资源,配合 JSON 配置文件,实现了设计 token 的集中管理。修改一个全局颜色只需改一处。

  3. List 组件性能优秀。相比 Scroll + Column,List 的虚拟化机制在大列表场景下优势明显。

  4. API 版本的兼容性需要注意。不同 API 版本的模块导入路径不同(如@ohos.routervs@kit.AbilityKit),开发前要确认目标 API 版本。

  5. 严格模式是双刃剑arkts-no-untyped-obj-literals等规则增加了代码量,但提升了运行时稳定性,值得接受。

6.3 下一步

鸿蒙生态正在快速发展,API 版本持续迭代,开发工具日趋完善。建议读者关注以下方向:

  • ArkTS 的新语法特性(如@Prop@Link双向绑定)
  • 鸿蒙元服务(Atomic Service)开发
  • 分布式能力(跨设备流转、数据同步)
  • 鸿蒙原生三方库生态的建设进展

项目源码:快递追踪 App(PackageTracker)
开发环境:DevEco Studio 6.x + HarmonyOS API 23/24
系列索引

  • 第一篇:项目初始化与工程架构
  • 第二篇:首页与列表开发实战
  • 第三篇:表单交互与搜索筛选
  • 第四篇:物流时间线与历史记录
  • 第五篇:数据统计与个人中心(本文·终篇)

感谢阅读!欢迎在评论区交流鸿蒙开发经验。

http://www.cnnetsun.cn/news/2905715.html

相关文章:

  • 碧蓝航线自动化脚本终极指南:5分钟上手全自动游戏管家
  • 嵌入式ADC与看门狗实战:从寄存器配置到系统级应用
  • Blender building_tools架构剖析:参数化建筑生成引擎深度解析
  • AI 测试赋能全流程实战 | Agent Skill + AI 赋能「需求分析」
  • 联想刃7000k BIOS深度解锁:从用户模式到管理员权限的进阶调优指南
  • 保姆级教程:用Gaussian 16和Antechamber搞定RESP电荷拟合(以甲烷为例)
  • 2026照片去水印免费软件App有哪些?免费照片去水印软件App推荐+排行榜
  • 2026年探秘丝杆模组源头厂家,解读其背后的生产奥秘与行业优势!
  • MonaServer:高性能实时通信服务器的终极解决方案
  • 2026证件照换衣服用什么软件?免费换装工具手把手教程
  • Vue3 + Unity WebGL 双向通信保姆级教程(2024版,含跨域和版本适配)
  • 你的MPU6050数据飘了吗?STM32实战避坑:从硬件滤波到软件卡尔曼滤波的完整调优指南
  • 如何用AndroidCupsPrint实现安卓设备无线打印?完整指南
  • 072、上下文窗口管理:长对话的自动压缩策略与关键信息保留技巧
  • 用Breakfast数据集复现动作分割?先搞定这5个Python预处理脚本(附代码)
  • 2026手把手教你做一寸证件照!免费制作软件与使用教程大全
  • 苏州晟雅泰电子:关于MT41K256M16TW-107:P 这个物料的参数规格及应用领域剖析
  • 深入解析MC68SZ328 MMC/SD控制器:从寄存器编程到安全机制实战
  • OBS源独立录制插件:彻底改变你的多源视频制作工作流
  • 【普中STM32F1xx开发攻略--标准库版】-- 第 49 章 FLASH 字库实验
  • Dts简介
  • R语言实战:用GD包和栅格数据跑通地理探测器全流程,从数据导入到可视化出图
  • LeetCodeHot100——155.最小栈
  • 微信聊天记录永久保存终极指南:掌握你的数字记忆主权
  • 5分钟构建专业级拼多多爬虫:Scrapy框架下的电商数据采集实战方案
  • AI 助手调试踩坑:5 轮瞎猜定位 4s budget 兜底路径(含 Hindsight 反思账本使用指南)
  • Keil5搭配STLink调试ARM工程,这几个隐藏设置能让你的效率翻倍(Reset and Run/速度优化)
  • VRoidStudio汉化插件终极指南:三步安装+个性化定制完整教程
  • 非遗正筋大师裴志刚走进哈萨克斯坦 患者不做手术感受中医绝技
  • 如何免费获取九大网盘直链下载链接:LinkSwift 完整使用指南