大家好,欢迎来到鸿蒙音乐播放器实战系列的第一篇。

        这个系列我们会从零开始,手把手带大家做一个连贯完整、可迭代商用的鸿蒙原生音乐播放器,全程不做单页演示、不写废弃占位代码。从项目骨架、页面路由、导航架构,到UI布局、播放能力、后台保活,循序渐进完成完整项目开发。

        作为开篇第一篇,我们核心完成项目底层骨架搭建:统一页面创建规范、配置全局路由体系、搭建全局导航栈、实现商用全屏广告启动页,同时完整搭建主页基础骨架,保证APP启动、跳转全流程闭环可运行,为后续所有功能迭代打底。

本篇学习目标

  • 理解鸿蒙Stage模型的项目结构,理清页面、路由、入口函数的对应关系

  • 掌握 router_map.json 路由表配置规则,独立完成多页面路由注册

  • 理解 Navigation + NavPathStack 导航原理,搭建全局统一导航架构

  • 分清 pushPathByNamereplacePathByName 的业务使用场景

  • 实现全屏沉浸式广告启动页(3秒自动跳转+手动跳过)

  • 掌握鸿蒙安全区适配、无报错页面创建、路由注册全套规范

  • 解决导入报错、路由找不到、页面空白、返回退回广告页等常见问题

一、先理清:我们的项目整体结构(连贯开发)

        本项目为完整连贯工程,所有页面逐层迭代开发,无临时占位、无废弃代码,核心四大页面结构固定:

  1. Index 入口页:APP根容器,仅承载全局导航栈,不展示业务UI

  2. Start 广告启动页:APP首个展示页,负责启动过渡、自动/手动跳转主页

  3. Layout 布局主页:APP核心主页,后续迭代底部Tab、多页面切换、首页UI

  4. Play 播放页:后续开发,歌曲播放、动画、播控、播放列表页面

        全局统一采用Navigation + NavPathStack 官方导航方案,所有页面跳转、栈管理统一受控,保证项目架构统一规范。

二、第一步:路由配置,给每个页面办「身份证」

        鸿蒙Stage模型硬性规则:所有页面必须提前在路由表注册,否则无法跳转、系统识别不到页面。

1. 声明路由表文件

        打开项目配置文件src/main/module.json5,在 module 节点下新增路由表声明,告诉系统路由配置文件位置:

{
 "module": { 
// ... 原有默认配置 

"routerMap": "$profile:router_map",

 // ... 原有默认配置 
.
.
.

        作用:绑定 resources/base/profile/router_map.json 为项目全局唯一路由表。

2. 手把手创建路由表文件(零报错步骤)

严格按步骤手动创建,杜绝路径、文件不存在报错:

1. 定位项目目录:src/main/resources/base

2. 无 profile 文件夹则:右键 base → New → Directory,命名为 profile

3. 选中 profile 文件夹:右键 → New → File,全名创建 router_map.json

路由表核心字段说明:

  • name:路由别名(代码跳转调用,大小写严格敏感)

  • pageSourceFile:页面ETS文件完整绝对路径

  • buildFunction:页面路由入口Builder函数名(必须和页面导出函数一致)

粘贴完整路由配置,一次性注册本篇所有页面,保证链路闭环:

{ 
"routerMap": [ 
{ "name": "Start",
 "pageSourceFile": "src/main/ets/pages/Start.ets",
 "buildFunction": "StartBuilder" },
 { "name": "Layout", 
"pageSourceFile": "src/main/ets/pages/Layout.ets",
 "buildFunction": "LayoutBuilder" 
    } 
  ] 
}

3. 全局页面创建统一强制规范(全程通用)

本项目所有页面统一创建规则,全程不变

✅ 唯一正确方式:右键 pages 目录 → New → ArkTS File,新建空白ETS文件

❌ 绝对禁止:使用右键自带的 Page、列表页、标签页等模板

(也可根据个人习惯)

删除线模板报错解释(新手必看)

        模板带删除线 = 废弃不兼容,这类模板是老旧FA模型专属,我们使用的是最新Stage模型,强行使用会出现API报错、路由失效、代码冗余冲突。

所有页面手写空白文件,架构干净、完全适配我们的自定义导航路由体系,无兼容问题。

4. 路由页面 Builder 函数强制规范

        所有路由注册页面,必须导出全局 @Builder 入口函数,否则系统找不到页面,路由直接报错。

函数作用:作为路由跳转的唯一入口,供系统反射调用,绑定页面组件。

固定标准写法(所有页面通用):

// @Builder:构建器装饰器,用于路由注册页面入口 
// export:必须导出,否则外部路由无法识别 
@Builder
    export function 页面名Builder(name: string, param: string) { 
// 固定写法:实例化当前页面,接收路由传参 
    页面名({ name: name, value: param }); 
}

新建页面固定四步流程:新建空白ArkTS文件 → 编写页面组件 → 编写导出Builder入口函数 → 路由表注册。

重要说明@Builder、@Component、@Entry、Stack、Image、Button、Alignment、Color 均为鸿蒙全局内置API,无需手动导入,强行导入会直接编译报错。

三、第二步:搭建全局导航骨架(Index入口页)

Index页面是整个APP的唯一入口根容器,核心作用是通过AppStorageV2全局挂载导航栈,实现全项目页面导航栈共享,彻底解决多页面栈冲突、跳转失效问题,统一托管所有页面路由跳转。

Index.ets 创建与完整代码

文件位置:src/main/ets/pages/Index.ets

创建方式:默认自带,缺失则右键pages新建ArkTS文件,命名Index

清空默认代码,粘贴以下带完整注释、可直接运行的标准代码:

// AppStorageV2:全局状态持久化工具,实现跨页面共享导航栈
import { AppStorageV2 } from '@kit.ArkUI';

/**
 * 首页路由入口构建函数
 * 适配全局路由体系,作为Index首页的路由注册入口
 */
@Builder
export function IndexBuilder() {
  Index();
}

// APP全局唯一入口页面
@Entry
@Component
struct Index {
  /**
   * 全局共享导航栈
   * 通过AppStorageV2全局挂载,实现项目所有页面跨页面共用同一个导航栈
   * 避免多栈冲突、页面跳转失效、页面栈错乱问题
   */
  pageStack: NavPathStack = AppStorageV2.connect(NavPathStack, 'navStack', () => new NavPathStack())!;

  build() {
    // 全局导航根容器,托管项目所有路由页面
    Navigation(this.pageStack) {
      // 根页面无需自定义UI,空列布局占位即可
      Column() {}
    }
    // 铺满全屏
    .width('100%')
    .height('100%')
    // 设置根容器透明,不遮挡下级页面UI
    .backgroundColor(Color.Transparent)
    // 全局扩展安全区,适配沉浸式全屏效果
    .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM])
    // 隐藏系统原生顶部导航栏,全程自定义页面样式
    .hideNavBar(true)
    // 根页面加载完成触发
    .onAppear(() => {
      // 延迟500ms执行跳转,规避Navigation初始化未完成导致的跳转失效问题
      setTimeout(() => {
        // 控制台打印日志,方便调试查看跳转状态
        console.info('开始跳转到广告页');
        // 根据路由名跳转至广告启动页
        this.pageStack.pushPathByName("Start", null, false);
      }, 500);
    })
  }
}

四、第三步:广告启动页完整开发(Start.ets)

实现效果:全屏沉浸式广告背景 + 右上角跳过按钮 + 3秒自动跳转主页 + 手动点击立即跳转,符合主流APP启动逻辑。

1. 新建页面

文件位置:src/main/ets/pages/Start.ets

创建方式:右键pages新建空白ArkTS文件,命名Start

2. 核心规则

所有路由页面必须被 NavDestination 包裹,否则无法获取导航栈上下文,页面空白、跳转失效。

3. 最终带详细注释完整代码

//第一步 建立自定义入口函数
@Builder
export function StartBuilder(name: string, param: string) {
  Start({ name: name, value: param });
}

// 广告启动页组件
@Component
export struct Start {
  // 定义导航栈实例,用于当前页面跳转
  navPathStack: NavPathStack = new NavPathStack();

  /**
   * 页面生命周期:页面即将显示时触发
   * 作用:开启3秒自动跳转定时器
   */
  aboutToAppear(): void {
    setTimeout(()=>{
      this.navPathStack.replacePathByName("Layout",null,false)
    },3000)
  }
  // 路由接收参数
  name: string = '';
  @State value: string = '';

  build() {
    // 路由页面专属容器,必须包裹所有UI,用于承接导航上下文
    NavDestination() {
      // 堆叠布局:实现底层背景图 + 上层按钮层级效果
      Stack({alignContent:Alignment.TopEnd}){
        // 全屏广告背景图
        Image($rawfile("ad.png"))
          .width("100%").height("100%")
        // 安全区扩展:图片延伸至状态栏、底部手势栏,消除上下白边
          .expandSafeArea([SafeAreaType.SYSTEM],[SafeAreaEdge.TOP,SafeAreaEdge.BOTTOM])
        // 右上角跳过按钮
        Button("跳过").backgroundColor(Color.Gray)
          .margin(15)
          .onClick(()=>{
            //this.navPathStack.replacePathByName("Layout",null,false)
            this.navPathStack.pushPathByName("Layout",null,false)
          })
      }
    }
    //.title("广告页")
    // ctx.pathStack 导航控制器放入this.navPathStack 
    .onReady((ctx: NavDestinationContext) => {
      this.navPathStack = ctx.pathStack;
    })
  }
}

4. 跳转方式核心避坑详解

  • pushPathByName(弃用):入栈跳转,保留广告页栈,用户返回键会退回广告页,不符合商用APP逻辑

  • replacePathByName(全程使用):替换当前页面,销毁广告页栈,进入主页后返回直接退出APP,符合主流APP启动逻辑

五、第四步:搭建主页基础骨架(Layout.ets)

为保证项目完整连贯可运行,本篇直接落地Layout主页基础架构,绝非临时占位。下一篇博客将在此骨架上迭代底部Tab、首页轮播、推荐卡片等完整UI,全程复用本篇代码,无重构、无废弃。

1. 新建页面

文件位置:src/main/ets/pages/Layout.ets

创建方式:右键pages新建空白ArkTS文件,命名Layout

2. 带详细注释零报错完整骨架代码

// import { Recommend1 } from '../component/Recommend1'
// import { FindPage } from '../component/FindPage';

// 定义一个类
interface TabClass {
  text : string,
  icon : ResourceStr
}

@Builder
export function LayoutBuilder() {
  Layout(); // 对应你的 struct 名字
}


@Component
export  struct Layout {
  navPathStack: NavPathStack = new NavPathStack()
  tabData: TabClass[] =[
    {text:'推荐', icon: $rawfile('ic_recommend.svg')},
    {text:'发现',icon:$rawfile('ic_find.svg')},
    {text:'动态',icon:$rawfile('ic_moment.svg')},
    {text: '我的', icon: $rawfile('ic_mine.svg')}
  ]

  @State textColorIndex : number = 0;

  @Builder tarBuilder(item: TabClass, index: number) {
    Column({ space: 5 }) {
      Image(item.icon).width(24).fillColor(this.textColorIndex===index?'#E85a88':'#63AAAA')
      Text(item.text).font({ size: 14 }).fontColor(this.textColorIndex===index?'#E85a88':'#63AAAA')
    }
  }

  build() {
    NavDestination() {
      // barPosition: BarPosition.End 菜单至于底部。
      Tabs({ barPosition: BarPosition.End }) {
        ForEach(this.tabData, (item: TabClass, index) => {
          TabContent() {
            // 推荐页直接用你写好的 Recommend1 组件
            if (index === 0) {
              // Recommend1()    // 推荐
            }
            else if (index === 1) {
              // FindPage()      // 发现
            }
            else {
              // 其他页面先放个占位文本
              Column() {
                Text(`${item.text}页面`).fontSize(20)
              }.width('100%').height('100%').justifyContent(FlexAlign.Center)
            }
          }.tabBar(this.tarBuilder(item, index))
          .backgroundColor('#131215')
        })
      }.backgroundColor('#3B3F42')
      .onChange((index) => {
        this.textColorIndex = index
      })
    }.onReady((ctx: NavDestinationContext) => {
      // 获取共享的导航栈
      this.navPathStack = ctx.pathStack;
    })
  }
}

至此,项目完整启动链路彻底跑通:APP启动 → Index加载导航栈 → 自动进入Start广告页 → 3秒/手动跳转Layout主页,无报错、无空白、无异常回退。

效果展示:

六、新手专属:全套报错解决方案

  1. 报错:buildFunction 不存在 排查:页面是否导出对应Builder函数、函数名和路由表大小写完全一致、文件路径无错误。

  2. 问题:广告页上下有白边,无法全屏 排查:Image宽高100%、是否添加 expandSafeArea 安全区扩展代码。

  3. 问题:跳转后页面空白 排查:路由页面是否包裹 NavDestination、Builder函数是否添加 export 导出。

  4. 问题:返回键退回广告页 排查:所有启动页跳转统一使用 replacePathByName,禁止使用push。

  5. 问题:导入代码编译报错 解决方案:组件、装饰器(Stack/Image/Button/@Builder/@Component等)均为全局内置,无需导入,仅保留导航、安全区专属API导入即可。

本篇总结

本篇完成了完整可运行的项目底层架构:统一页面创建规范、配置全局路由表、搭建全局导航栈、实现商用级全屏广告启动页、落地主页基础骨架,全程无冗余代码、无报错、无临时占位,保证项目连贯可迭代。

本篇所有代码、规范、架构将贯穿整个系列,后续所有功能均基于本篇骨架迭代,无需重构。

下篇预告

下一篇将基于当前Layout主页骨架,开发底部Tab选项卡、图标文字高亮、多页面切换、首页搜索栏、轮播图、音乐推荐卡片等完整静态UI界面。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐