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

鸿蒙原生应用开发实战(五):个人中心与数据统计 — 电影清单App

鸿蒙原生应用开发实战(五):个人中心与数据统计 — 电影清单App

前言

这是本系列的最后一篇。前四篇文章完成了首页、添加电影、列表和详情页面,实现了完整的电影 CRUD(增删改查)。今天来开发个人中心页面,包括数据统计、分类分析和项目管理。

本文将覆盖:

  1. 个人中心页面设计
  2. 六维度统计卡片
  3. 分类占比条形图
  4. 功能菜单与数据重置
  5. 应用全貌回顾

一、个人中心设计

┌──────────────────────────────────┐ │ < 返回 我的 │ ├──────────────────────────────────┤ │ 🎬 电影收藏家 │ │ 记录每一部看过的电影 │ ├──────────────────────────────────┤ │ 🎬 总数 ✅ 已看 👀 想看 │ │ 8 3 2 │ │ ▶️ 在看 ⭐ 收藏 📊 完成率 │ │ 1 3 37.5% │ ├──────────────────────────────────┤ │ 分类统计 │ │ 🎭 剧情 ████████████░░░ 2部 │ ← 分类条形图 │ 🚀 科幻 ██████████░░░░ 2部 │ │ 🐭 动画 █████████████░ 2部 │ │ ❤️ 爱情 ██████░░░░░░░░ 1部 │ │ 😂 喜剧 ██████░░░░░░░░ 1部 │ ├──────────────────────────────────┤ │ 功能菜单 │ │ 📤 导出清单 │ │ 🔄 重置数据 │ │ ℹ️ 关于应用 │ └──────────────────────────────────┘

二、状态与数据模型

2.1 分类统计接口

interfaceGenreStat{genreId:string;icon:string;name:string;count:number;}

2.2 页面状态

@Entry@Componentstruct ProfilePage{@Statemovies:Movie[]=[];@StatetotalCount:number=0;@StatewatchedCount:number=0;@StatewantCount:number=0;@StatewatchingCount:number=0;@StatefavCount:number=0;@Stategenres:Genre[]=[];@StategenreStats:GenreStat[]=[];@StatemaxGenreCount:number=0;}

三、六维度统计卡片

3.1 统计计算

calcStats():void{lettotal=this.movies.length;letwatched=0,want=0,watching=0,fav=0;letgenreCounts:Record<string,number>={};for(leti=0;i<this.movies.length;i++){letm=this.movies[i];if(m.status===MovieStatus.WATCHED)watched++;if(m.status===MovieStatus.WANT_TO_WATCH)want++;if(m.status===MovieStatus.WATCHING)watching++;if(m.isFavorite)fav++;genreCounts[m.genreId]=(genreCounts[m.genreId]||0)+1;}this.totalCount=total;this.watchedCount=watched;this.wantCount=want;this.watchingCount=watching;this.favCount=fav;// 分类统计letgStats:GenreStat[]=[];letmaxG=0;for(leti=0;i<this.genres.length;i++){letg=this.genres[i];letcnt=genreCounts[g.id]||0;if(cnt>maxG)maxG=cnt;letgs:GenreStat={genreId:g.id,icon:g.icon,name:g.name,count:cnt};gStats.push(gs);}// 按数量降序gStats.sort((a,b)=>b.count-a.count);this.genreStats=gStats;this.maxGenreCount=maxG;}

3.2 统计卡片

两行六卡片的布局:

// 第一行Row(){this.statBox('🎬','总数',this.totalCount.toString())this.statBox('✅','已看',this.watchedCount.toString())this.statBox('👀','想看',this.wantCount.toString())}.width('94%').margin({bottom:6})// 第二行Row(){this.statBox('▶️','在看',this.watchingCount.toString())this.statBox('⭐','收藏',this.favCount.toString())if(this.totalCount>0){this.statBox('📊','完成率',((this.watchedCount/this.totalCount)*100).toFixed(0)+'%')}else{this.statBox('📊','完成率','0%')}}.width('94%').margin({bottom:14})

3.3 复用 @Builder

@BuilderstatBox(icon:string,label:string,value:string){Column(){Text(icon).fontSize(20)Text(value).fontSize(18).fontWeight(FontWeight.Bold).fontColor('#333333').margin({top:4})Text(label).fontSize(12).fontColor('#999999').margin({top:1})}.layoutWeight(1).padding(10).backgroundColor('#FFFFFF').borderRadius(10).margin({left:3,right:3}).alignItems(HorizontalAlign.Center)}

四、分类占比条形图

4.1 可视化的意义

条形图相比纯数字列表,一目了然地展示哪些分类收藏最多。我们使用 Column + Row 组合实现:

ForEach(this.genreStats,(gs:GenreStat)=>{if(gs.count>0){Row(){Text(gs.icon).fontSize(16).width(28)Text(gs.name).fontSize(14).fontColor('#666666').width(50)// 条形图主体Row(){Text('').height(16).width(((gs.count/this.maxGenreCount)*100)+'%').backgroundColor('#6C63FF').borderRadius(4)}.width('100%').backgroundColor('#F0F0F0').borderRadius(4).layoutWeight(1).margin({left:6,right:6})Text(gs.count+'部').fontSize(13).fontColor('#999999').width(40).textAlign(TextAlign.End)}.width('100%').margin({bottom:8})}},(gs:GenreStat)=>gs.genreId)

4.2 条形图原理

数量: 2部 最大: 2部 宽度: 100% ████████████████████████████████ 2部 剧情 数量: 1部 最大: 2部 宽度: 50% ████████████░░░░░░░░░░░░░░░░░░ 1部 爱情

五、功能菜单

5.1 菜单项

@BuildermenuItem(icon:string,title:string,desc:string,onClick:()=>void){Row(){Text(icon).fontSize(20).margin({right:10})Column(){Text(title).fontSize(15).fontWeight(FontWeight.Medium)Text(desc).fontSize(12).fontColor('#999999').margin({top:1})}.layoutWeight(1).alignItems(HorizontalAlign.Start)Text('>').fontSize(14).fontColor('#CCCCCC')}.width('100%').padding({top:10,bottom:10}).onClick(onClick)}

5.2 菜单列表

Column(){this.menuItem('📤','导出清单','将电影清单导出为文本',()=>{})this.menuItem('🔄','重置数据','清除所有电影数据',()=>{this.clearAllData();})this.menuItem('ℹ️','关于应用','电影清单 v1.0.0',()=>{})}

六、数据重置功能

clearAllData():void{AppStorage.set<Movie[]>('movies',[]);this.loadData();}

七、应用全貌回顾

7.1 项目结构

MyApplication/ └── entry/src/main/ets/ ├── models/ │ └── MovieData.ets ← 数据模型 + 示例数据 + 工具函数 ├── pages/ │ ├── Index.ets ← 首页:统计卡片 + 快捷操作 + 最近电影 │ ├── AddMovie.ets ← 添加电影:表单 + 状态 + 分类 Grid │ ├── ListPage.ets ← 电影列表:筛选标签 + 搜索 + 滑动删除 │ ├── DetailPage.ets ← 电影详情:状态切换 + 评分 + 收藏 + 影评 │ └── ProfilePage.ets ← 个人中心:六维统计 + 分类条形图 + 菜单 └── resources/ ← 颜色/字符串/尺寸资源

7.2 功能清单

模块页面功能
首页Index.ets🎬 统计卡片、快捷入口、最近电影、随机推荐
添加AddMovie.ets✏️ 表单录入、Grid分类选择、状态切换
列表ListPage.ets📋 五标签筛选、关键词搜索、滑动删除
详情DetailPage.ets📖 状态切换、⭐ 星级评分、💬 影评编辑、收藏
个人ProfilePage.ets📊 六维统计、分类占比、数据重置

7.3 技术栈

技术说明
框架HarmonyOS Stage 模型
语言ArkTS (严格模式)
SDKAPI 23 (6.1.0)
存储AppStorage (全局状态持久化)
路由@ohos.router
UIColumn/Row/List/Grid/TextInput/TextArea/Toggle

7.4 开发路线

第一天:项目搭建 + 数据模型 + 首页仪表盘 ↓ 第二天:添加电影 + Grid分类 + 表单交互 ↓ 第三天:电影列表 + 筛选搜索 + 滑动删除 ↓ 第四天:电影详情 + 评分 + 影评 + 收藏 ↓ 第五天:个人中心 + 统计 + 分类分析 + 项目总结

7.5 数据流全景

┌──────────────┐ │ AppStorage │ │ 全局持久存储 │ └──────┬───────┘ ┌────────────────┼────────────────┐ ▼ ▼ ▼ ┌──────────┐ ┌──────────┐ ┌──────────────┐ │ AddMovie │ │ ListPage │ │ DetailPage │ │ 写入 │ │ 读取+删 │ │ 读取+修改+删 │ └──────────┘ └──────────┘ └──────────────┘ │ │ │ ▼ ▼ ▼ ┌──────────┐ ┌──────────┐ ┌──────────────┐ │ Index │ │ProfilePage│ │ 所有页面 │ │ 首页展示 │ │ 统计+分析 │ │ onPageShow │ └──────────┘ └──────────┘ └──────────────┘

八、ArkTS 严格模式要点总结

经过五篇文章的开发,以下是 ArkTS 严格模式的核心要点总结:

8.1 禁止清单

语法替代方案
@Builder中的let声明内联表达式或提前计算
解构赋值let [a,b] = arr分别赋值let a = arr[0]
无类型字面量{a: 1}显式接口类型let x: T = {a: 1}
.bind()闭包() => this.method()
空数组[]带类型let x: T[] = []

8.2 最佳实践

  • 所有@State属性需要初始化
  • ForEach必须提供唯一 key 生成函数
  • Grid.columnsTemplate使用fr弹性单位
  • 接口定义在文件顶部,@Entry之前

九、总结与展望

9.1 本系列回顾

五篇文章从零开始构建了一个完整的电影清单鸿蒙应用:

  • UI 开发:Column/Row/List/Grid/TextInput/TextArea/Toggle
  • 交互逻辑:表单录入、筛选搜索、滑动删除、评分评价
  • 数据管理:AppStorage 持久化、@State 响应式
  • 数据可视化:自定义条形图、统计卡片
  • 状态管理:@State/@Builder/@StorageLink

9.2 扩展方向

如果想让应用更完善,可以考虑:

  • 云同步:接入华为云,多设备同步电影清单
  • 海报图片:使用 Image 组件展示电影海报
  • 电影搜索:接入公开 API 搜索真实电影信息
  • 社交分享:分享电影清单给好友
  • 桌面卡片:在桌面显示今日推荐电影
  • 原子化服务:快速记录电影

9.3 写在最后

HarmonyOS 的 Stage 模型 + ArkTS 开发体验流畅,组件丰富,对前端/移动端开发者都比较友好。希望这五篇文章能帮助想学习鸿蒙开发的朋友们快速上手。

如果你有任何问题或建议,欢迎在评论区交流!记得点赞收藏关注三连哦~


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

相关文章:

  • 大模型推理优化:从量化到 KV Cache 的性能调优实战
  • 从零到一:解锁安卓玩机新世界,TWRP刷写与第三方ROM实战避坑指南
  • BladeOne完整安装指南:从Composer到单文件部署的3种方法
  • 高效图表制作实战指南:一站式Mermaid编辑器深度解析
  • Edge.js 容器化部署:使用 Docker 打包 .NET-Node.js 混合应用
  • PoseCNN自定义TensorFlow层解析:深入理解平均距离损失与霍夫投票层实现
  • 解密医疗数据集成的瑞士军刀:Mirth Connect 3大架构模式深度解析
  • 中科闻歌携4.05亿收入叩开港交所大门,能否复制智谱高估值神话?
  • 3步掌握PlantDoc数据集:构建鲁棒的田间植物病害检测系统
  • 免费开源字幕神器:5分钟让TED演讲拥有专业双语字幕
  • MATLAB红外光谱预处理工具包:含平滑、导数、MSC、SNV等10种标准化与增强方法
  • 技能跃迁蓝图:500+实战项目重塑你的AI技术栈
  • NXP P60D025安全微控制器:硬件加密、PUF与MIFARE集成深度解析
  • 2026上海GEO服务商怎么选?一份能力坐标参考
  • PCA9530实战指南:I2C控制PWM调光与GPIO扩展详解
  • C#写的轻量IE浏览器,WinForms封装WebBrowser控件,开箱即用
  • 从查询到操作:MySQL实战训练进阶指南(141-160题精讲)
  • IRISMAN:让您的PS3游戏管理变得前所未有的简单高效
  • Visual Studio IntelliCode扩展功能详解:提升开发效率的10个技巧
  • 2026年多站点建站优选:主流站群 CMS 系统及落地方案解析
  • 2008-2026.5地市级、县域级极端低温数据
  • DDrawCompat:三步让经典游戏在现代Windows上完美运行的终极兼容方案
  • “一机一码”安全加密方案
  • 04、JAVAEE---多线程进阶、文件I/O、网络初识
  • OSPF综合实验(nat,汇总,特殊区域,加快收敛,安全认证)
  • 2026年AI人才市场火爆!这3个高薪岗位普通人也能入场?速收藏!
  • 哈希表冲突处理:开放寻址与拉链法的底层实现与工程选型
  • 深度解析AKShare Pro数据接口:从基础使用到高级配置
  • 企业微信自动化中验证环节的处理策略
  • 终极Project Sekai表情包制作指南:3分钟创建个性化Discord贴纸