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

鸿蒙原生应用实战(五):塔罗牌App开发 — 数据模型、构建配置与工程优化

鸿蒙原生应用实战(五):塔罗牌App开发 — 数据模型、构建配置与工程优化

前言

经过前四篇的逐步开发,我们已经完成了塔罗牌 App 的所有功能页面。本篇作为收官之作,将聚焦于那些"看不见但至关重要"的部分:

  • TarotData 全量数据模型:78 张塔罗牌的数据结构设计与内容填充
  • 资源文件管理:string / float / color 资源的定义与引用
  • 构建配置解析:build-profile / hvigor / module.json5 配置详解
  • API 版本适配:compatibleSdkVersion 与 targetSdkVersion 的策略
  • ArkTS 严格模式规则:常见编译错误及解决方案
  • 工程构建与发布:从代码到 HAP 包的完整流程

一、TarotData 数据模型深度解析

1.1 数据结构设计

塔罗牌分为两大类:

  • 大阿卡纳(Major Arcana):22 张(编号 0-XXI),代表重大人生课题
  • 小阿卡纳(Minor Arcana):56 张(分四花色 × 14 张),代表日常事务
exportinterfaceTarotCard{id:number;// 唯一标识(0-77)name:string;// 中文名称(如"愚者""魔术师")englishName:string;// 英文名称(如"The Fool""The Magician")arcana:string;// 分类:'大阿卡纳' 或 '小阿卡纳'number:string;// 编号(如'0'、'I'、'权杖一')keywords:string;// 关键词(如"开始、冒险、天真")meaningUp:string;// 正位含义meaningDown:string;// 逆位含义description:string;// 牌面描述color:string;// 显示颜色(十六进制)isFavorite:boolean;// 收藏状态(未使用,保留字段)}

1.2 大阿卡纳数据示例

以下是部分大阿卡纳牌的数据(共 22 张,编号 0-XXI):

exportconstTAROT_CARDS:TarotCard[]=[{id:0,name:'愚者',englishName:'The Fool',arcana:'大阿卡纳',number:'0',keywords:'开始、冒险、天真、无限可能',meaningUp:'愚者正位象征着全新的开始、天真的冒险精神和对未知的期待。这张牌鼓励你放下过去的包袱,勇敢地踏上新的旅程。它代表着一个充满可能性的新阶段,提醒你保持开放的心态和对世界的好奇心。',meaningDown:'愚者逆位暗示着鲁莽、不负责任的决策或对后果的忽视。你可能在逃避责任,或者在某个重要决定上过于草率。这张牌提醒你需要三思而后行,避免因冲动而后悔。',description:'愚者站在悬崖边缘,背上背着一个行囊,手中握着一朵白玫瑰,仰望天空。他即将迈出脚步,却完全不在意前方的危险。身边的小狗在吠叫,试图提醒他注意。画面中明亮的黄色背景象征着新的开始和无限的可能性。',color:'#F5E642',isFavorite:false},{id:1,name:'魔术师',englishName:'The Magician',arcana:'大阿卡纳',number:'I',keywords:'创造力、技能、资源、显化',meaningUp:'魔术师正位代表着你有能力将想法转化为现实。你拥有所需的一切资源和技能,只需要善加运用。这张牌鼓励你相信自己的能力,主动采取行动去创造你想要的现实。',meaningDown:'魔术师逆位暗示着才能被浪费、操控他人或缺乏方向。你可能没有充分利用自己的天赋,或者正在用不正当的手段达到目的。需要重新审视自己的动机和方法。',description:'魔术师站在桌前,一只手举向天空接收能量,另一只手指向大地,象征着"如上如下"的宇宙法则。桌上摆放着四大元素的象征物——圣杯、宝剑、星币和权杖。他头上有无限符号,代表着无限的潜力。',color:'#E8B832',isFavorite:false},// ... 共 78 张牌];

1.3 数据填充的工程化思考

为 78 张牌填写完整的中英文含义是一项大工程。在实际项目中,我有几点建议:

  1. 结构化优先:先确定接口字段,再填充数据
  2. 分批填充:按大阿卡纳(22 张)→ 权杖组(14 张)→ 圣杯组(14 张)→ 宝剑组(14 张)→ 星币组(14 张)分批完成
  3. 释义长度控制:正逆位释义各保持在 100-150 字之间,过长会影响阅读体验
  4. 颜色编码:为每张牌分配一个辨识色,增加视觉层次感

二、资源文件管理

2.1 字符串资源

// resources/base/element/string.json{"string":[{"name":"title_home","value":"命运之轮"},{"name":"title_cards","value":"全部牌义"},{"name":"title_spread","value":"牌阵解读"},{"name":"title_favorites","value":"我的收藏"},{"name":"today_card","value":"今日塔罗"},{"name":"module_desc","value":"塔罗牌占卜应用"},{"name":"EntryAbility_desc","value":"塔罗牌主入口"},{"name":"EntryAbility_label","value":"命运之轮"}]}

2.2 浮点尺寸资源

// resources/base/element/float.json{"float":[{"name":"app_title_size","value":"28fp"},{"name":"app_subtitle_size","value":"22fp"},{"name":"app_body_size","value":"16fp"},{"name":"app_small_size","value":"14fp"},{"name":"app_caption_size","value":"12fp"},{"name":"app_card_radius","value":"16vp"},{"name":"app_button_radius","value":"24vp"}]}

2.3 颜色资源

// resources/base/element/color.json{"color":[{"name":"start_window_background","value":"#1A0A2E"},{"name":"primary_text","value":"#FFFFFF"},{"name":"secondary_text","value":"#B8A8D0"}]}

为什么使用$r()引用资源?

  • 支持多语言适配(国际化)
  • 支持多设备适配(不同 dpi 自动缩放)
  • 编译时校验,避免硬编码错误

三、构建配置解析

3.1 项目级 build-profile.json5

{ "app": { "signingConfigs": [], "products": [ { "name": "default", "signingConfig": "default", "targetSdkVersion": "6.1.1(24)", // 目标 SDK "compatibleSdkVersion": "6.1.0(23)", // 兼容最低版本 "runtimeOS": "HarmonyOS", "buildOption": { "strictMode": { "caseSensitiveCheck": true, // 大小写检查 "useNormalizedOHMUrl": true // 归一化 OHM URL } } } ], "buildModeSet": [ { "name": "debug" }, { "name": "release" } ] } }

API 版本策略解读

参数含义
compatibleSdkVersion23最低支持 API 23 的设备
targetSdkVersion24最高使用 API 24 的特性
runtimeOSHarmonyOS仅运行在 HarmonyOS 上

3.2 模块级 build-profile.json5

{ "apiType": "stageMode", // Stage 模型 "buildOption": { "resOptions": { "copyCodeResource": { "enable": false // 不复制代码资源 } } }, "buildOptionSet": [ { "name": "release", "arkOptions": { "obfuscation": { // 代码混淆配置 "ruleOptions": { "enable": false, // 可开启以减小包体 "files": ["./obfuscation-rules.txt"] } } } } ] }

3.3 AppScope/app.json5 — 全局应用配置

{ "app": { "bundleName": "com.example.myapplication", // 包名,全局唯一 "vendor": "example", "versionCode": 1000000, // 内部版本号 "versionName": "1.0.0", // 对外版本名 "icon": "$media:layered_image", "label": "$string:app_name" // 应用名称(仅在 AppScope 定义一次) } }

注意app_name只需在AppScopestring.json中定义一次,不可在entry模块中重复定义,否则编译会报冲突。

3.4 module.json5 — 模块配置

{ "module": { "name": "entry", "type": "entry", // entry 类型(可独立运行) "mainElement": "EntryAbility", "deviceTypes": ["phone"], "deliveryWithInstall": true, "installationFree": false, "pages": "$profile:main_pages", // 路由配置引用 "abilities": [ { "name": "EntryAbility", "srcEntry": "./ets/entryability/EntryAbility.ets", "exported": true, "skills": [ { "entities": ["entity.system.home"], "actions": ["ohos.want.action.home"] } ] } ] } }

关键字段解释

  • type: "entry":可独立安装运行的应用入口模块
  • skills:声明 Ability 的能力,entity.system.home+ohos.want.action.home表明这是桌面入口
  • pages:引用main_pages.json中注册的所有页面路由

四、ArkTS 严格模式规则详解

在开发过程中,严格模式(arkts-no-untyped-obj-literals等)是误报最多的配置。以下是完整的规则及应对方案:

4.1 规则清单

规则含义触发场景
arkts-no-untyped-obj-literals对象字面量必须显式类型const obj = {a:1, b:2}
arkts-no-noninferrable-arr-literals数组字面量必须可推断类型const arr = [{a:1, b:2}]
arkts-no-nullable-array不允许可空数组类型arr?: string[]
arkts-no-any不允许 any 类型let x: any
arkts-no-eval不允许 evaleval(code)

4.2 实战解决方案

方案一:提取为具名变量

// ❌ 错误:字面量数组无法推断// ForEach(this.drawnCards, (item: NumberedCard) => { ... })// ✅ 正确:显式声明后使用constpositions=['过去','现在','未来'];

方案二:对象赋值使用类型断言

// ❌ 可能的不严格写法constcard={id:0,name:'愚者'};// ✅ 显式声明类型constcard:TarotCard={id:0,name:'愚者',englishName:'',arcana:'',// 全部字段number:'',keywords:'',meaningUp:'',meaningDown:'',description:'',color:'#FFF',isFavorite:false};

方案三:@Component 属性必须初始化

@Componentstruct MyComponent{// ✅ 所有属性必须赋默认值name:string='';count:number=0;items:string[]=[];callback?:()=>void;// 可选属性用 ?}

4.3 编译错误排查技巧

当 DevEco Studio 报编译错误时,按以下步骤排查:

  1. 看错误码arkts-no-xxx的格式直接表明违反的规则
  2. 定位行号:大多数错误会精确到行
  3. 检查字面量:70% 的错误是对象/数组字面量缺少类型声明
  4. 检查类型:组件属性、函数参数是否都有明确类型

五、构建与发布流程

5.1 本地构建命令

# 使用 DevEco Studio 内置的 hvigor 构建"D:\DevEco Studio\tools\node\node.exe"\"D:\DevEco Studio\tools\hvigor\bin\hvigorw.js"\--modemodule\-pmodule=entry@default\-pproduct=default\-prequiredDeviceType=phone\assembleHap\--analyze=normal\--parallel\--incremental\--daemon

参数解读

参数含义
--mode module模块级构建
-p module=entry@default构建 entry 模块的 default 产品
assembleHap打包为 HAP 文件
--parallel并行构建加速
--incremental增量编译
--daemon守护进程模式

5.2 构建产物

构建完成后,HAP 包位于:

entry/build/default/outputs/default/entry-default-unsigned.hap

5.3 签名与发布

  1. 在 DevEco Studio 中配置签名证书(.p12+.cer+.p7b
  2. 使用buildBuild HAP(s)生成已签名的 HAP
  3. 上传至华为应用市场 AppGallery Connect

六、项目经验总结

6.1 架构回顾

经过五篇文章的开发,我们的塔罗牌 App 形成了清晰的架构分层:

┌─────────────────────────┐ │ UI 层 (pages/) │ ← 5 个页面 ├─────────────────────────┤ │ 模型层 (model/) │ ← TarotData 数据 + 管理器 ├─────────────────────────┤ │ 能力层 (ability/) │ ← EntryAbility 入口 ├─────────────────────────┤ │ 配置层 (config/) │ ← build / module / resources └─────────────────────────┘

6.2 关键技术决策

决策选择理由
开发模型Stage 模型官方推荐,适合复杂应用
开发语言ArkTS类型安全,编译期校验
状态管理静态类管理器轻量、无第三方依赖
主题切换订阅发布模式解耦、可扩展
路由导入@ohos.routerAPI 23 兼容性

6.3 后续可扩展方向

  1. 数据持久化:Preferences 存储收藏数据
  2. 网络请求:对接塔罗牌 API 获取每日运势
  3. 动画效果:抽牌翻转动画、卡片入场动画
  4. 自定义牌阵:用户自由选择牌数和位置
  5. 多语言支持:英文版、日文版国际化
  6. Widget 服务卡片:桌面显示今日塔罗

结语

五篇文章,从环境搭建到发布上线,我们完整地走了一遍鸿蒙原生应用的开发流程。这个塔罗牌 App 虽小,但五脏俱全——路由导航、列表渲染、组件化、状态管理、主题切换、构建配置,覆盖了日常开发中的大多数场景。

鸿蒙生态正在快速发展,现在入局正当时。希望这个系列能成为你鸿蒙开发路上的实战参考。如果你有任何问题或想法,欢迎在评论区交流讨论!

项目代码: 基于 HarmonyOS API 23 + Stage 模型 + ArkTS
源码位置:entry/src/main/ets/
构建工具: DevEco Studio + hvigor
全文完🎉


附录:本系列目录

篇号标题核心内容
第一篇环境搭建与首页开发项目初始化、Stage 模型、Index 首页
第二篇牌义列表与路由导航CardListPage、CardDetailPage、路由传参
第三篇牌阵解读与交互设计SpreadPage、随机算法、正逆位判定
第四篇收藏功能与主题切换FavoriteManager、ThemeManager、FavPage
第五篇数据模型与工程优化TarotData、构建配置、严格模式、发布流程
http://www.cnnetsun.cn/news/2928197.html

相关文章:

  • MobiOffice(原OfficeSuite):比WPS更干净的移动办公神器,老外都在用的Office平替!
  • 远程办公救星:除了Putty,你的Windows Terminal/WSL2 SSH连接不稳?试试这个sshd服务端配置
  • HT1632C驱动IC的“暗黑”操作:避开C51/Arduino时序编程的5个常见坑
  • 告别‘无信号’!手把手教你用IUV搞定5G NSA/SA双模站点的无线数据配置
  • 网络排障新思路:用Wireshark抓包实战分析IPv6邻居发现(ND)协议
  • 麒麟V10 SP1 + Qt + Qpid Proton 连接 Apache Artemis 实战指南
  • 签到题【牛客tracker 每日一题】
  • AD5761R菊花链应用避坑指南:LDAC引脚用法、SPI时序与数据错位问题全解析
  • 新PM上任第一课:避开这5个质量策划“天坑”,用MSD和FP流程稳住项目基本盘
  • CC switch + codex 401问题修复
  • GCP上机器学习模型生产部署的四大生命线实践
  • Ubuntu 24.04桌面迁移实战:30天Windows替代全记录
  • Scikit-learn RidgeCV 报错怎么办?教你一招避坑
  • 非科班转码面华为:我的项目经历如何撑起了三轮技术面?
  • 千问怎么领取8元立减券,输入 新用户福利020738
  • 别再卡成PPT了!手把手教你解决VMware虚拟机跑Gazebo仿真帧率低的终极方案
  • 【Springboot毕设全套源码+文档】基于Java+springboot在线书籍商城系统的设计和开发(丰富项目+远程调试+讲解+定制)
  • Labelimg画框闪退?别急着重装!一个Python版本引发的‘血案’与精准修复指南
  • 避坑指南:在树莓派Pico上用MicroPython播放SD卡里的WAV音频,SPI和I2S配置这些细节别踩雷
  • 小红书品牌合作笔记被下架?SENTINEL-6H申诉攻略
  • 告别IntelliJ IDEA Python运行报错:手把手教你重建.iml文件与修复Module依赖
  • 告别设计盲区:一招搞定PowerDesigner物理模型表的注释同步与展示
  • 飞凌RK3568开发板Qt应用开发入门:从源码编译到‘Hello Qt’上板运行全记录
  • pandas多维聚合实战:从groupby到滚动窗口的工程化落地
  • Rust内存模型入门:所有权、借用与生命周期三权分立
  • 别再让Segmentation Fault折磨你:用GDB和Valgrind快速定位C/C++内存访问错误
  • 不只是Resize和Crop:用PyTorch transforms构建一个‘防呆’图像预处理流水线
  • VCSA 6.7证书过期别慌!手把手教你修改系统时间+续订证书(附STS证书修复脚本)
  • 别再让BrokenPipeError打断你的爬虫:requests和aiohttp库中的连接保持与异常处理实战
  • 别再只改后缀了!用Burp Suite实战iwebsec靶场03关,手把手教你Content-Type绕过(附四种MIME类型修改技巧)