承接上一篇项目基础,本篇实现APP核心主页架构:底部Tab导航栏 + 多页面滑动切换。采用鸿蒙官方原生 Tabs 组件开发,简化项目结构,无需额外冗余数据文件,所有代码就近编写、逻辑清晰,适合新手学习。

本篇学习目标

  • 规范项目目录,实现主页子页面统一分类管理

  • 掌握鸿蒙原生 Tabs / TabContent 底部导航组件使用方法

  • 掌握 interface 自定义类型,规范Tab数据格式

  • 学会SVG单图标动态变色技巧,实现Tab选中/未选中状态切换

  • 实现Tab点击、页面滑动双向联动高亮效果

一、最终项目目录结构

整理主页页面资源,将四个Tab子页面统一收纳,精简多余文件,目录清晰:

pages
 ├─ Index.ets        // 项目全局入口、全局导航栈挂载
 ├─ Start.ets        // 广告启动过渡页
 ├─ Layout.ets       // 主页容器、底部Tab导航核心页面
 └─ home             // 主页子页面统一文件夹
    ├─ Recommend.ets // 推荐页
    ├─ Find.ets      // 发现页
    ├─ Dynamic.ets   // 动态页
    └─ Mine.ets      // 我的页

二、创建四个主页子页面

pages/home 目录下新建4个空白ArkTS文件,均为纯展示组件,无需路由注册,仅由主页Tabs组件托管,必须添加 export导出供外部导入使用。

1. Recommend.ets 推荐页

/**
 * 推荐页
 * 主页Tab子页面,纯展示组件
 * 后续扩展:首页轮播、歌单推荐、热门音乐列表
 */
@Component
export struct Recommend {
  build() {
    Column() {
      Text('音乐推荐页')
        .fontSize(24)
        .fontColor('#ffffff')
        .fontWeight(FontWeight.Bold)
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#131215')
    .justifyContent(FlexAlign.Center)
  }
}

2. Find.ets 发现页

/**
 * 发现页
 * 主页Tab子页面,纯展示组件
 * 后续扩展:音乐榜单、分类歌单、活动推荐
 */
@Component
export struct Find {
  build() {
    Column() {
      Text('音乐发现页')
        .fontSize(24)
        .fontColor('#ffffff')
        .fontWeight(FontWeight.Bold)
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#131215')
    .justifyContent(FlexAlign.Center)
  }
}

3. Dynamic.ets 动态页

/**
 * 动态页
 * 主页Tab子页面,纯展示组件
 * 后续扩展:用户音乐动态、好友分享、资讯推送
 */
@Component
export struct Dynamic {
  build() {
    Column() {
      Text('音乐动态页')
        .fontSize(24)
        .fontColor('#ffffff')
        .fontWeight(FontWeight.Bold)
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#131215')
    .justifyContent(FlexAlign.Center)
  }
}

4. Mine.ets 我的页

/**
 * 我的页
 * 主页Tab子页面,纯展示组件
 * 后续扩展:个人中心、收藏歌单、本地音乐、系统设置
 */
@Component
export struct Mine {
  build() {
    Column() {
      Text('个人中心-我的页')
        .fontSize(24)
        .fontColor('#ffffff')
        .fontWeight(FontWeight.Bold)
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#131215')
    .justifyContent(FlexAlign.Center)
  }
}

三、Layout主页核心代码(最终版)

所有Tab数据、类型定义就近写在当前页面,使用官方 Tabs 组件实现底部导航,自带滑动切换、状态联动,代码精简无冗余,可直接编译运行。

import { Recommend } from './home/Recommend'
import { Find } from './home/Find'
import { Dynamic } from './home/Dynamic'
import { Mine } from './home/Mine'


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

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


@Component
export  struct Layout {
  navPathStack: NavPathStack = new NavPathStack()
  @State textColorIndex : number = 0;

  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')},
  ]

  @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) {
              Recommend()    // 推荐
            }
            else if (index === 1) {
              Find()      // 发现
            }
            else if (index === 2) {
              Dynamic()    // 动态
            }
            else if (index === 3) {
              Mine()    // 我的
            }
            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;
    })
  }
}

四、核心知识点讲解

1. Tabs 组件核心作用

        鸿蒙官方专用标签页组件,适配底部导航场景,原生支持手势滑动、页面切换动画、状态监听,无需手动封装滑动逻辑,稳定性和性能优于自定义布局。

2. interface 类型作用

        用于约束Tab数据结构,固定数据字段格式,书写数据时有代码提示、编译校验,避免字段写错、漏写导致的报错。

3. SVG动态变色原理

        通过 fillColor 属性,结合当前选中下标动态修改图标、文字颜色,仅需一套图标资源,即可实现选中/未选中两种视觉状态,精简资源和代码。

4. 双向联动逻辑

        点击Tab修改 currentIndex 实现页面切换;滑动页面触发 onChange 更新下标,同步Tab高亮状态,双向联动无延迟。

五、新手常见报错解决

  • 子页面模块报错:子页面 struct 必须添加 export 导出,导入路径补全 .ets 后缀

  • 图标不显示:确认SVG图标放入 rawfile 目录,文件名与代码一致

  • 高亮不生效:确认切换事件正常更新 currentIndex 状态变量

  • 路由异常:LayoutBuilder 必须保留 name、param 标准参数

本篇总结

        本篇完成了项目标准主页架构开发,通过原生Tabs组件实现完整底部导航功能,目录结构整洁、代码精简无冗余,掌握了鸿蒙底部导航的标准开发方式,为后续首页UI、音乐功能迭代打好基础。

下篇预告

        下一篇开发推荐页完整高颜值UI,实现顶部搜索栏、首页轮播图、热门歌单卡片布局。

Logo

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

更多推荐