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

《鸿蒙原生应用开发实战》第五篇:收藏功能、资源管理与构建发布

《鸿蒙原生应用开发实战》第五篇:收藏功能、资源管理与构建发布

前言

经过前四篇的开发,我们的「光遇·心境」应用已经有了完整的框架、数据模型、UI 设计和页面导航。本篇将完成最后的功能闭环 —— 收藏功能的完整实现、资源管理的规范、以及如何将应用构建发布为 HAP 包。

本文将涵盖:

  • 收藏功能的完整实现(3 个页面的联动)
  • AppStorage 在收藏功能中的应用
  • 资源文件管理规范(string/color/float)
  • 构建配置详解
  • HAP 包构建与优化
  • 项目总结与迭代方向

一、收藏功能完整实现

收藏功能涉及 3 个页面的联动:首页(进入)、详情页(切换收藏)、收藏页(展示和管理)。我们一步步拆解。

1.1 数据层面设计

// model/SceneData.ets// AppStorage keyexportconstFAV_KEY:string='fav_scenes';

收藏的数据结构非常简单:用AppStorage存储一个number[]数组,每个元素是被收藏场景的id

1.2 首页初始化(Index.ets)

aboutToAppear():void{// 确保 AppStorage 中的收藏列表已初始化if(!AppStorage.has(FAV_KEY)){AppStorage.set<number[]>(FAV_KEY,[]);}}

这一步很关键 —— 在其他页面读取FAV_KEY之前,确保 key 已存在于 AppStorage 中,否则AppStorage.get()会返回undefined

1.3 详情页收藏切换(DetailPage.ets)

详情页是用户执行收藏/取消收藏操作的地方:

@Componentstruct DetailPage{@StateisFav:boolean=false;// 页面出现时检查收藏状态aboutToAppear():void{constparams=router.getParams()asRecord<string,Object>;if(params&&params['sceneId']){constid=params['sceneId']asnumber;this.scene=getSceneById(id);if(this.scene){this.checkFavStatus();}}}// 从 AppStorage 读取收藏列表,判断当前场景是否在列表中checkFavStatus():void{constfavList:number[]=AppStorage.get<number[]>(FAV_KEY)||[];this.isFav=this.scene?favList.indexOf(this.scene.id)>=0:false;}// 切换收藏状态toggleFav():void{if(!this.scene)return;letfavList:number[]=AppStorage.get<number[]>(FAV_KEY)||[];constidx=favList.indexOf(this.scene.id);if(idx>=0){favList.splice(idx,1);// 取消收藏this.isFav=false;}else{favList.push(this.scene.id);// 添加收藏this.isFav=true;}// 写回 AppStorage(必须重新 set 才能触发更新)AppStorage.set<number[]>(FAV_KEY,favList);}}

UI 上,收藏按钮的显示状态动态跟随@State isFav

// 收藏按钮Text(this.isFav?'❤️':'🤍').fontSize(26).onClick(()=>{this.toggleFav();})

1.4 收藏页展示与管理(FavPage.ets)

@Componentstruct FavPage{@StatefavScenes:SceneItem[]=[];@StatefavCount:number=0;// 每次进入页面时重新加载收藏列表aboutToAppear():void{this.loadFavScenes();}// 从 AppStorage 读取所有收藏 ID,组装成 SceneItem 数组loadFavScenes():void{constfavIds:number[]=AppStorage.get<number[]>(FAV_KEY)||[];constlist:SceneItem[]=[];for(constidoffavIds){constscene=getSceneById(id);if(scene){list.push(scene);}}this.favScenes=list;this.favCount=list.length;}// 取消收藏removeFav(sceneId:number):void{letfavList:number[]=AppStorage.get<number[]>(FAV_KEY)||[];constidx=favList.indexOf(sceneId);if(idx>=0){favList.splice(idx,1);AppStorage.set<number[]>(FAV_KEY,favList);this.loadFavScenes();// 重新加载列表}}}

收藏列表的卡片渲染:

@BuilderFavCard(item:SceneItem){Row(){// 左侧彩色竖条(渐变色)Row().width(6).height('100%').borderRadius(3).linearGradient({direction:GradientDirection.Bottom,colors:[[item.colors[0],0],[item.colors[2],1]]}).margin({right:14})// 中间:名称 + 描述 + 标签Column(){Text(item.name).fontSize(18).fontColor(Color.White).fontWeight(FontWeight.Bold)Text(item.desc).fontSize(12).fontColor($r('app.color.text_secondary'))Row(){Text(item.category).fontSize(10)Text(`${item.duration}分钟`).fontSize(10)}}.layoutWeight(1)// 右侧:取消收藏按钮Column(){Text('❤️').fontSize(22).onClick(()=>{this.removeFav(item.id);})Text($r('app.string.cancel_fav')).fontSize(9)}}}

1.5 个人中心展示收藏数(ProfilePage.ets)

aboutToAppear():void{constfavIds:number[]=AppStorage.get<number[]>(FAV_KEY)||[];this.favCount=favIds.length;}

1.6 数据流总结

点击收藏 ❤️ ↓ DetailPage.toggleFav() ├── 读取 AppStorage(FAV_KEY) → number[] ├── 添加或移除当前 sceneId └── 写回 AppStorage(FAV_KEY) → 触发全局更新 ↓ FavPage.aboutToAppear() ProfilePage.aboutToAppear() ↓ ↓ loadFavScenes() 读取 favCount ↓ ↓ 渲染收藏列表 显示统计数字

设计的巧妙之处:使用 AppStorage 作为数据总线,各页面在aboutToAppear中读取最新数据,不需要复杂的观察者模式或事件总线。


二、资源文件管理规范

2.1 三资源体系

资源类型文件名用途
字符串string.json所有用户可见文本
颜色color.json主题色、文字色、背景色
尺寸float.json字号、间距、圆角

2.2 字符串资源(string.json)

{"string":[{"name":"index_title","value":"今日光感"},{"name":"index_subtitle","value":"用光影治愈心灵"},{"name":"my_fav","value":"我的收藏"},{"name":"fav_empty","value":"还没有收藏的场景"},{"name":"fav_empty_desc","value":"去探索页面发现喜欢的光影场景吧"},{"name":"color_analysis","value":"色彩分析"},{"name":"recommend_sound","value":"推荐白噪音"},{"name":"cancel_fav","value":"取消收藏"},{"name":"scene_explore","value":"场景探索"},{"name":"scene_explore_desc","value":"发现属于你的光影世界"}]}

命名规范

  • 按功能模块加前缀:index_fav_scene_
  • 使用小写字母 + 下划线

使用方式

Text($r('app.string.my_fav'))// → "我的收藏"

2.3 颜色资源(color.json)

{"color":[{"name":"start_window_background","value":"#1a1a2e"},{"name":"text_primary","value":"#FFFFFF"},{"name":"text_secondary","value":"#B0B0C0"},{"name":"text_accent","value":"#FFD700"},{"name":"fav_active","value":"#FF4757"},{"name":"fav_inactive","value":"#88FFFFFF"}]}

使用方式

.fontColor($r('app.color.text_secondary')).backgroundColor($r('app.color.card_bg'))

2.4 尺寸资源(float.json)

{"float":[{"name":"title_font_size","value":"28fp"},{"name":"subtitle_font_size","value":"16fp"},{"name":"body_font_size","value":"14fp"},{"name":"caption_font_size","value":"12fp"},{"name":"card_radius","value":"16vp"},{"name":"large_radius","value":"24vp"},{"name":"padding_small","value":"8vp"},{"name":"padding_medium","value":"16vp"},{"name":"padding_large","value":"24vp"}]}

使用方式

.fontSize($r('app.float.body_font_size'))// 14fp.borderRadius($r('app.float.card_radius'))// 16vp.padding($r('app.float.padding_large'))// 24vp

2.5 深浅色适配

支持深色模式,在resources/dark/element/中定义深色专有颜色:

resources/ ├── base/element/color.json # 默认(浅色或基础值) └── dark/element/color.json # 深色模式覆盖值

三、构建配置详解

3.1 项目级 build-profile.json5

{ "app": { "products": [ { "name": "default", "signingConfig": "default", "targetSdkVersion": "6.1.1(24)", "compatibleSdkVersion": "6.1.0(23)", "runtimeOS": "HarmonyOS", "buildOption": { "strictMode": { "caseSensitiveCheck": true, "useNormalizedOHMUrl": true } } } ] }, "modules": [ { "name": "entry", "srcPath": "./entry", "targets": [{"name": "default", "applyToProducts": ["default"]}] } ] }

关键配置解释:

  • compatibleSdkVersion: 23:最低支持 API 23 的设备
  • targetSdkVersion: 24:目标 SDK 版本
  • caseSensitiveCheck: true:启用大小写检查(ArkTS 严格模式)
  • useNormalizedOHMUrl: true:规范化模块 URL 引用

3.2 模块级 build-profile.json5

{ "apiType": "stageMode", "buildOptionSet": [ { "name": "release", "arkOptions": { "obfuscation": { "ruleOptions": { "enable": false }, "files": ["./obfuscation-rules.txt"] } } } ] }

3.3 构建命令解析

hvigorw--modemodule\-pmodule=entry@default\-pproduct=default\-prequiredDeviceType=phone\assembleHap\--analyze=normal\--parallel\--incremental\--daemon
参数说明
--mode module模块级构建
-p module=entry@default构建 entry 模块的 default 分发包
-p product=defaultdefault 产品类型
-p requiredDeviceType=phone目标设备类型
assembleHap生成 HAP 包
--analyze=normal代码分析级别
--parallel并行构建
--incremental增量构建(仅编译修改的文件)
--daemon守护进程模式(加速后续构建)

3.4 构建产物目录

build/ ├── output/ │ └── default/ │ ├── entry-default-unsigned.hap # 未签名包 │ └── entry-default-signed.hap # 已签名包(发布用) └── ...

四、调试与测试

4.1 hilog 日志输出

import{hilog}from'@kit.PerformanceAnalysisKit';constDOMAIN=0x0000;// 输出日志hilog.info(DOMAIN,'testTag','页面加载成功: %{public}s',pageName);hilog.error(DOMAIN,'testTag','加载失败: %{public}s',JSON.stringify(err));

日志格式:hilog.级别(域, 标签, 格式化字符串, 参数...)

4.2 DevEco Studio 调试

DevEco Studio 提供了完整的调试工具:

  • 断点调试(Breakpoints)
  • 变量监视(Watch)
  • 调用堆栈(Call Stack)
  • Profiler 性能分析

4.3 ohosTest 单元测试

// entry/src/main/ohosTest/ets/test/import{describe,it,expect}from'@ohos/hypium';describe('SceneDataTest',()=>{it('getSceneById_should_return_correct_scene',0,()=>{constscene=getSceneById(1);expect(scene).not().undefined();expect(scene.name).assertEqual('黎明破晓');});it('getScenesByCategory_should_filter_correctly',0,()=>{constscenes=getScenesByCategory('海洋');expect(scenes.length).assertEqual(2);});});

五、发布前的检查清单

HAP 包优化建议

  1. 移除未使用的资源:检查rawfile/media/中是否有未使用的图片
  2. 代码混淆:Release 构建时启用混淆(obfuscation.enable: true
  3. 缩减包体积
    • 图片用 WebP 格式替代 PNG
    • 移除调试日志
    • 移除 ohosTest 模块

发布检查清单

  • bundleName 已改为正式包名(非 com.example.xxx)
  • versionCode 和 versionName 已更新
  • 签名配置已完成(.p12/.csr/.cer/.p7b
  • 应用名称和图标已替换为正式资源
  • 已测试过 Release 构建
  • 已适配不同分辨率设备
  • 隐私权限声明完整

六、项目总结与迭代方向

已完成功能

「光遇·心境」应用 v1.0.0 ├── 首页(分类入口 + 每日推荐 + 精选推荐横向滚动) ├── 场景探索列表(5 分类标签筛选 + 网格卡片) ├── 场景详情页(沉浸渐变背景 + 色彩分析 + 白噪音推荐) ├── 我的收藏(收藏列表 + 取消收藏 + 空状态) └── 个人中心(旅行统计 + 功能菜单 + 设置)

可迭代方向

  1. 白噪音播放器:集成 Audio 模块,在查看场景时播放对应白噪音
  2. 动画过渡:页面切换时添加共享元素过渡动画
  3. 字体切换:支持更多中文字体选择
  4. 场景编辑:允许用户自定义场景的色彩和描述
  5. 云同步:收藏列表跨设备同步(使用云数据库)
  6. Widget 卡片:桌面小组件显示每日推荐
  7. 动效增强:粒子系统模拟晨光、星光等动态效果

七、踩坑记录

坑1:AppStorage 未初始化导致读取失败

现象:收藏页面进入时报错
原因:FavPage 读取FAV_KEY时,该 key 还未在 AppStorage 中注册
解决:在首页的aboutToAppear中确保初始化:

if(!AppStorage.has(FAV_KEY)){AppStorage.set<number[]>(FAV_KEY,[]);}

坑2:Release 构建失败

现象assembleHap --mode module在 release 模式下失败
原因:混淆规则文件obfuscation-rules.txt配置了不存在的规则
解决:在 release 构建中暂时禁用混淆,或检查规则文件的语法

坑3:HAP 包安装后闪退

现象:真机安装后打开立即闪退
原因:常见于 manifest 中配置了不存在的 Ability 或资源引用错误
解决:使用hilog查看闪退日志,检查module.json5中的srcEntry路径


连载总结

至此,五篇连载全部完成。我们从零开始构建了一个完整的鸿蒙原生应用,覆盖了从框架搭建到发布的全流程:

篇次主题核心内容
第一篇项目框架与路由Stage 模型、Ability、路由注册
第二篇数据模型与状态@State、AppStorage、@Builder
第三篇沉浸式 UI 设计渐变、光晕、卡片、布局
第四篇导航与参数传递router API、页面生命周期
第五篇收藏功能与发布功能闭环、资源管理、构建发布

希望通过这五篇文章,你能掌握鸿蒙原生应用开发的核心技能,能够独立构建完整的 Stage 模型应用。鸿蒙生态正在快速发展,现在是最好的入局时机。

Happy coding with HarmonyOS! 🚀

http://www.rkmt.cn/news/1524887.html

相关文章:

  • 昆明奢侈品回收市场深度调研:3家实体门店实测,2026年6月最新行情与交易指南 - 钦扬网络
  • 2026最新英语写作批改AI系统 核心功能及使用避坑指南汇总
  • 2026年驻马店市PMP培训机构哪家好?官方授权R.E.P.报考指南 - 众智商学院课程中心
  • 突破局部逻辑的枷锁:现代 C++ Lambda 表达式的演进与闭包艺术
  • 3个简单步骤让BongoCat音效系统彻底改变你的桌面互动体验
  • 2026深圳龙岗宝安龙华黄金回收实测:全城11区免费上门,30分钟响应当场结算 - 逸程
  • 2026最新 英语老师亲测推荐适合学生用的优质英语听力APP
  • 逆向工程实战:如何打造你自己的微信QQ防撤回补丁
  • 昆明奢侈品回收指南:3家实体门店实地测评,2026年6月最新行情 - 钦扬网络
  • BiliBili-Manga-Downloader:跨平台漫画下载解决方案的技术架构与实践指南
  • 影刀RPA新手教程_网页表格数据提取完全指南HTML表格到Excel的标准流程
  • 揭阳管道疏通马桶疏通 口碑甄选服务商合集|2026 本地推荐指南 - 金修达家庭维修
  • 捕捉时间的切片:4D 高斯溅射如何让“全息视频”成为现实
  • 高效歌词同步工具LRCGET:如何10分钟内为数千首音乐批量下载精准歌词?
  • 2026深圳福田CBD黄金回收行情速递:大盘价减5元/克 - 逸程
  • 如何实现3步实时人脸替换:Deep-Live-Cam完整指南
  • ATM反向复用技术原理与MPC8323E IMA模块配置实战
  • 2026年最新可布置作业的英语教学软件 老师选款实用指南
  • 青岛名包回收口碑排名 本地 6 家门店实测盘点 - 讯息早知道
  • 3分钟学会AI图像超分辨率:让模糊照片变清晰的终极方案
  • caj2pdf-qt:解决CAJ文件阅读难题的专业转换方案
  • 如何快速搭建个人数字图书馆:Open Library完整开源解决方案指南
  • 【共创季稿事节】鸿蒙原生 ArkTS 布局精讲:foregroundColor 前景色统一着色
  • 2026 东莞代理记账公司推荐榜 广东万创实力领先 注册公司/进出口退税/合规财税/内账外包服务实力机构 TOP 推荐 - 变量人生001
  • 2026年众智商学院官网怎么找、400电话怎么拨打、冯老师微信怎么加、课程怎么报名 - 众智商学院官方
  • Sunshine游戏串流终极指南:为什么你应该立即搭建自己的云游戏服务器
  • 2026年众智商学院PMP题库资料怎么领取?报名费用35学时班期和报考指导怎么确认,冯老师 - 众智商学院职业教育
  • MPC8540 L1缓存与MMU寄存器实战:从原理到调试的嵌入式开发指南
  • 2026年六氟化硫气体检测仪选购全攻略及高人气产品推荐榜单 - 资讯焦点
  • Lumia设备终极解锁指南:WPinternals完整解锁与Root访问技术解析