鸿蒙原生应用实战(一):项目创建与首页开发 — 从零搭建数独游戏
鸿蒙原生应用实战一项目创建与首页开发 — 从零搭建数独游戏前言随着鸿蒙生态的快速发展越来越多的开发者开始投身鸿蒙原生应用开发。本系列文章将以一款经典数独游戏为实战项目从零开始带你体验鸿蒙原生应用HarmonyOS Next ArkTS的完整开发流程。本篇是第一篇我们将完成以下内容使用 DevEco Studio 创建 Stage 模型项目理解 ArkTS 组件化开发模式开发游戏首页实现难度选择与页面路由掌握资源文件string/color/float的最佳实践一、项目创建与环境配置1.1 创建 Stage 模型项目打开 DevEco Studio选择File → New → Create Project模板选择Empty AbilityStage模型。关键配置项配置项值Project NameMyApplicationBundle Namecom.sudoku.appCompile SDKAPI 9ModelStageLanguageArkTS为什么选择 Stage 模型Stage 模型是鸿蒙从 API 9 开始主推的 Ability 框架。相比于旧版的 FAFeature Ability模型Stage 模型的优势非常明显组件化设计Ability 和 UI 分离每个 Ability 有独立的module.json5配置进程管理支持多实例和进程级隔离后台任务更规范的后台任务管理机制生命周期清晰onCreate→onWindowStageCreate→onForeground→onBackground→onDestroy1.2 项目结构总览创建完成后项目结构如下MyApplication/ ├── AppScope/ # 全局配置 │ ├── app.json5 # 应用级配置bundleName, version等 │ └── resources/ ├── entry/ # 应用入口模块 │ ├── src/main/ │ │ ├── ets/ │ │ │ ├── entryability/ # Ability 生命周期 │ │ │ └── pages/ # 页面文件 │ │ ├── module.json5 # 模块配置 │ │ └── resources/ # 资源文件 │ └── build-profile.json5 # 模块构建配置 ├── hvigor/ # 构建工具配置 └── oh-package.json5 # 包依赖1.3 AppScope 全局配置AppScope/app.json5用来定义应用的全局属性{ app: { bundleName: com.sudoku.app, vendor: atomcode, versionCode: 1000000, versionName: 1.0.0, icon: $media:layered_image, label: $string:app_name } }注意app_name只需要在AppScope/resources/base/element/string.json中定义一次不能在entry模块中重复定义否则会编译冲突。二、理解 EntryAbility — 应用入口EntryAbility.ets是整个应用的入口能力文件。它继承自UIAbility负责管理应用的生命周期import{AbilityConstant,ConfigurationConstant,UIAbility,Want}fromkit.AbilityKit;import{hilog}fromkit.PerformanceAnalysisKit;import{window}fromkit.ArkUI;exportdefaultclassEntryAbilityextendsUIAbility{onCreate(want:Want,launchParam:AbilityConstant.LaunchParam):void{// 设置颜色模式this.context.getApplicationContext().setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_NOT_SET);}onWindowStageCreate(windowStage:window.WindowStage):void{// 加载首页windowStage.loadContent(pages/Index,(err){if(err.code){hilog.error(0x0000,testTag,Failed to load. Cause: %{public}s,JSON.stringify(err));}});}onForeground():void{/* 应用切到前台 */}onBackground():void{/* 应用切到后台 */}onDestroy():void{/* 应用销毁 */}}关键点通过windowStage.loadContent(pages/Index, callback)加载首页页面路径必须与main_pages.json中注册的一致。三、注册页面路由所有页面都需要在entry/src/main/resources/base/profile/main_pages.json中注册{src:[pages/Index,pages/GamePage,pages/LeaderboardPage,pages/StatsPage,pages/SettingsPage,pages/TutorialPage,pages/AchievementsPage,pages/CustomThemePage]}本项目共8个页面后续每开发一个页面都要先在这里注册。四、资源文件的最佳实践鸿蒙推荐使用$r语法引用资源而不是硬编码字符串和颜色。这样做的好处多语言适配只需替换资源文件主题切换运行时改变资源引用视觉规范统一所有尺寸/颜色集中在资源文件中4.1 字符串资源string.json{string:[{name:title_home,value:数独},{name:difficulty_easy,value:简单},{name:difficulty_medium,value:中等},{name:difficulty_hard,value:困难},{name:daily_challenge,value:每日挑战},{name:title_leaderboard,value:排行榜},{name:title_stats,value:统计},{name:title_settings,value:设置}]}4.2 颜色资源color.json{color:[{name:primary,value:#FF5C6BC0},{name:background,value:#FFF5F5F5},{name:card_bg,value:#FFFFFF},{name:text_primary,value:#FF333333},{name:text_secondary,value:#FF666666},{name:text_hint,value:#FF999999},{name:cell_given,value:#FF333333},{name:cell_user,value:#FF1565C0},{name:cell_error,value:#FFF44336},{name:cell_selected,value:#FFBBDEFB}]}4.3 字号资源float.json{float:[{name:page_title_font_size,value:22fp},{name:body_font_size,value:16fp},{name:small_font_size,value:13fp},{name:padding_medium,value:16vp},{name:card_corner_radius,value:12vp}]}使用方式$r(app.color.primary)、$r(app.string.title_home)、$r(app.float.body_font_size)注意fp是字体像素单位会根据系统字体缩放vp是虚拟像素单位用于布局尺寸保证不同屏幕密度下视觉一致。五、首页开发 — Index.ets首页是用户打开应用后看到的第一个界面。我们的数独首页包含标题区域emoji 应用名 副标题四个难度选择按钮简单/中等/困难/每日挑战底部导航链接排行榜/统计/设置5.1 完整的首页代码importrouterfromohos.router;interfaceRouteOpt{url:string;params?:Object;}interfaceDiffParams{difficulty:string;}EntryComponentstruct Index{build(){Column(){// 标题区域 Column(){Text().fontSize(64)Text($r(app.string.title_home)).fontSize(32).fontWeight(FontWeight.Bold).fontColor($r(app.color.text_primary)).margin({top:8})Text(经典数独 挑战大脑).fontSize($r(app.float.body_font_size)).fontColor($r(app.color.text_hint)).margin({top:4})}.width(100%).margin({top:80}).alignItems(HorizontalAlign.Center)// 难度选择按钮 Column(){// 简单Button($r(app.string.difficulty_easy)).width(80%).height(52).backgroundColor($r(app.color.primary)).borderRadius(26).fontColor(Color.White).fontSize(18).fontWeight(FontWeight.Medium).margin({bottom:16}).onClick((){router.pushUrl({url:pages/GamePage,params:{difficulty:easy}})})// 中等Button($r(app.string.difficulty_medium)).width(80%).height(52).backgroundColor($r(app.color.primary)).borderRadius(26).fontColor(Color.White).fontSize(18).margin({bottom:16}).onClick((){router.pushUrl({url:pages/GamePage,params:{difficulty:medium}})})// 困难Button($r(app.string.difficulty_hard)).width(80%).height(52).backgroundColor($r(app.color.primary)).borderRadius(26).fontColor(Color.White).fontSize(18).margin({bottom:24}).onClick((){router.pushUrl({url:pages/GamePage,params:{difficulty:hard}})})// 每日挑战描边按钮样式Button($r(app.string.daily_challenge)).width(80%).height(52).backgroundColor(Color.White).borderRadius(26).fontColor($r(app.color.primary)).fontSize(18).border({width:2,color:$r(app.color.primary)}).onClick((){router.pushUrl({url:pages/GamePage,params:{difficulty:daily}})})}.width(100%).margin({top:60}).alignItems(HorizontalAlign.Center)Blank()// 底部导航 Row(){Text( )Text($r(app.string.title_leaderboard))}.margin({bottom:12}).onClick((){router.pushUrl({url:pages/LeaderboardPage})})Row(){Text( )Text($r(app.string.title_stats))}.margin({bottom:12}).onClick((){router.pushUrl({url:pages/StatsPage})})Row(){Text(⚙️ )Text($r(app.string.title_settings))}.margin({bottom:40}).onClick((){router.pushUrl({url:pages/SettingsPage})})}.width(100%).height(100%).backgroundColor($r(app.color.background))}}5.2 技术要点解析1.Entry和Component装饰器Entry标记该组件为页面入口代表这是一个完整的页面Component声明这是一个可复用的 ArkTS 组件两者必须同时使用才能构成一个页面。2. 路由跳转 —ohos.router鸿蒙 ArkTS 的页面跳转通过router模块实现importrouterfromohos.router;// 带参数跳转router.pushUrl({url:pages/GamePage,params:{difficulty:easy}});接收参数constparamsrouter.getParams()asRecordstring,Object;constdifficultyparams[difficulty]asstring;严格模式下接口定义由于 ArkTS 严格模式不允许未类型化的对象字面量我们需要提前定义接口interfaceRouteOpt{url:string;params?:Object;}interfaceDiffParams{difficulty:string;}这样在onClick中创建对象时类型就是明确的。3. 链式调用风格ArkTS 组件广泛使用链式调用Builder Pattern来设置属性Button(简单).width(80%).height(52).backgroundColor($r(app.color.primary)).borderRadius(26).fontColor(Color.White).fontSize(18).fontWeight(FontWeight.Medium).margin({bottom:16}).onClick((){/* ... */})这种写法简洁且可读性强每个方法调用返回组件本身可以继续设置下一个属性。4. 描边按钮的设计每日挑战按钮使用了不同的设计——描边样式outline与实心按钮形成视觉对比起到引导用户注意的作用Button($r(app.string.daily_challenge)).backgroundColor(Color.White)// 白色背景.fontColor($r(app.color.primary))// 主题色文字.border({width:2,color:$r(app.color.primary)})// 主题色边框六、module.json5 配置entry/src/main/module.json5是模块的核心配置用于注册 Ability 和扩展能力{ module: { name: entry, type: entry, description: $string:module_desc, mainElement: EntryAbility, deviceTypes: [phone], pages: $profile:main_pages, // 引用页面路由配置 abilities: [ { name: EntryAbility, srcEntry: ./ets/entryability/EntryAbility.ets, description: $string:EntryAbility_desc, icon: $media:layered_image, label: $string:EntryAbility_label, startWindowIcon: $media:startIcon, startWindowBackground: $color:start_window_background, exported: true, skills: [ { entities: [entity.system.home], actions: [ohos.want.action.home] } ] } ], extensionAbilities: [ { name: EntryBackupAbility, srcEntry: ./ets/entrybackupability/EntryBackupAbility.ets, type: backup, exported: false } ] } }几个关键字段mainElement指定入口 AbilitydeviceTypes支持的设备类型当前仅phoneskillsentities/actions声明为桌面应用显示在桌面图标extensionAbilities扩展能力这里是备份功能七、ArkTS 严格模式注意事项鸿蒙 ArkTS 在 API 9 启用了严格模式有两个常见规则需要特别注意7.1arkts-no-untyped-obj-literals对象字面量必须有显式类型声明不能写// ❌ 错误无法推断类型router.pushUrl({url:pages/GamePage,params:{difficulty:easy}});必须提前定义接口// ✅ 正确显式类型interfaceRouteOpt{url:string;params?:Object;}letopt:RouteOpt{url:pages/GamePage,params:{difficulty:easy}};router.pushUrl(opt);7.2arkts-no-noninferrable-arr-literals数组字面量必须可推断类型解决方式是将数组定义为独立变量// ✅ 正确letachievements:Achievement[][{id:1,title:成就A,/* ... */},{id:2,title:成就B,/* ... */}];八、小结与预告本篇我们完成了✅ 鸿蒙 Stage 模型项目创建与结构理解✅ EntryAbility 生命周期学习✅ 页面路由注册与跳转✅ 资源文件管理string/color/float✅ 首页 UI 开发难度选择 底部导航✅ ArkTS 严格模式注意事项下一篇我们将进入这个项目的核心——数独游戏引擎的开发GamePage包括9×9 棋盘的 Grid 布局数独题目的动态生成算法单元格的选择、高亮和交互计时器与游戏状态管理敬请期待项目地址本系列所有代码基于 HarmonyOS Next ArkTS Stage 模型使用 DevEco Studio 开发API 23 编译。