鸿蒙学习实战之路-Tabs 标签页组件全攻略 🥦

最近好多朋友问我:“鸿蒙里的页面切换怎么弄?就像微信底部那几个标签栏一样的效果”,别慌!今天咱们就来聊聊鸿蒙里的 Tabs 组件,这个组件可是实现页面分类切换的神器~ 😊

一、Tabs 组件是什么?

Tabs 组件是鸿蒙里的一个容器组件,当页面内容比较多时,可以用它来分类展示。比如咱们手机里常见的:

  • 微信底部的"微信"“通讯录”“发现”“我”
  • 淘宝首页的各种分类标签
  • 新闻 App 顶部的"推荐"“热点”"财经"等

这些效果,用 Tabs 组件都能轻松实现!

二、Tabs 组件基础用法

咱们先从最简单的 Tabs 用法开始,就像学炒菜先学会开火一样~ 🔥

1. 核心结构

Tabs 组件的核心结构非常简单,主要由两部分组成:

  • Tabs():外层容器,包裹所有标签页
  • TabContent():每个标签页的内容,需要配合.tabBar()设置标签文字

基础代码示例:

@Entry
@Component
struct TabsBasicDemo {
  build() {
    Tabs() { // 外层容器
      TabContent() { // 第一个标签页
        Text('首页内容')
          .fontSize(30)
          .textAlign(TextAlign.Center)
      }
      .tabBar('首页') // 设置标签文字

      TabContent() { // 第二个标签页
        Text('推荐内容')
          .fontSize(30)
          .textAlign(TextAlign.Center)
      }
      .tabBar('推荐')

      TabContent() { // 第三个标签页
        Text('发现内容')
          .fontSize(30)
          .textAlign(TextAlign.Center)
      }
      .tabBar('发现')

      TabContent() { // 第四个标签页
        Text('我的内容')
          .fontSize(30)
          .textAlign(TextAlign.Center)
      }
      .tabBar('我的')
    }
  }
}

运行这段代码,你就能看到一个简单的标签页切换效果啦!是不是超简单?

效果预览:

三、Tabs 组件常用属性

默认的 Tabs 已经能用了,但咱们还可以通过一些属性来调整它的外观和行为,就像给菜调味一样~ 😋

通过这些属性,我们可以实现各种不同的导航效果:

1. 垂直导航 & 导航位置

默认的 Tabs 是水平导航栏在顶部,咱们可以调整:

  • vertical:设置导航方向为水平或垂直
  • barPosition:设置导航栏位置(开头/结尾)

代码示例:

@Entry
@Component
struct TabsAttributeDemo {
  build() {
    Tabs() {
      TabContent() {
        Text('首页内容')
          .fontSize(30)
      }
      .tabBar('首页')

      TabContent() {
        Text('推荐内容')
          .fontSize(30)
      }
      .tabBar('推荐')

      TabContent() {
        Text('发现内容')
          .fontSize(30)
      }
      .tabBar('发现')

      TabContent() {
        Text('我的内容')
          .fontSize(30)
      }
      .tabBar('我的')
    }
    .vertical(true) // 设置为垂直导航
    .barPosition(BarPosition.End) // 导航栏在末尾
    .scrollable(false) // 禁用滑动切换
    .animationDuration(500) // 切换动画时长
  }
}

2. 滚动导航栏

当标签太多,屏幕放不下时,咱们可以把导航栏设置为滚动的,这样就能容纳更多标签了~ 👇

效果预览:

代码示例:

@Entry
@Component
struct ScrollableTabsDemo {
  // 定义多个标签标题
  titles: string[] = ['首页', '关注', '热门', '军事', '体育', '八卦', '数码', '财经', '美食', '旅行']

  build() {
    Tabs() {
      // 使用ForEach循环生成多个TabContent
      ForEach(this.titles, (title: string) => {
        TabContent() {
          Text(title + '的内容')
            .fontSize(30)
            .textAlign(TextAlign.Center)
        }
        .tabBar(title)
      })
    }
    .barMode(BarMode.Scrollable) // 设置为滚动导航栏
  }
}

四、自定义 TabBar(重点!)

默认的 TabBar 只有文字,不够美观。咱们可以自定义 TabBar,添加图标、调整样式,让它更符合咱们的设计需求~ 🎨

1. 自定义 TabBar 外观

咱们可以使用@Builder装饰器来创建自定义的 TabBar 组件,这样就能添加图片、调整布局了。

TabBar 的 tabBar 属性支持两种类型:string 和 CustomBuilder。当我们需要自定义复杂的 TabBar 时,可以使用 CustomBuilder:

让咱们实现一个包含图标和文字的自定义 TabBar:

代码示例:

@Entry
@Component
struct CustomTabBarDemo {
  build() {
    Tabs() {
      TabContent() {
        Text('首页')
          .fontSize(30)
      }
      // 使用自定义TabBar
      .tabBar(this.tabBarBuilder($r('app.media.ic_tabbar_icon_0'), '首页'))

      TabContent() {
        Text('我的')
          .fontSize(30)
      }
      .tabBar(this.tabBarBuilder($r('app.media.ic_tabbar_icon_3'), '我的'))
    }
    .barPosition(BarPosition.End) // 导航栏在底部
  }

  // 自定义TabBar的Builder函数
  @Builder
  tabBarBuilder(img: ResourceStr, text: string) {
    Column() { // 垂直布局
      Image(img) // 图标
        .width(30)
        .height(30)
      Text(text) // 文字
        .fontSize(12)
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
  }
}

2. 实现 TabBar 高亮效果

自定义 TabBar 后,默认的高亮效果就没有了,咱们需要自己实现。这时候就需要用到 Tabs 的事件和状态管理了~ ✨

效果预览:

核心思路:

  1. @State变量保存当前选中的索引
  2. onChangeonTabBarClick事件中更新索引
  3. 在自定义 TabBar 中根据索引切换高亮样式

代码示例:

@Entry
@Component
struct HighlightTabBarDemo {
  // 保存当前选中的索引
  @State selectedIndex: number = 0

  build() {
    Column() {
      Tabs() {
        TabContent() {
          Text('首页内容')
            .fontSize(30)
        }
        // 传递默认图标、高亮图标、文字和索引
        .tabBar(this.tabBarBuilder($r('app.media.ic_tabbar_icon_0'), $r('app.media.ic_tabbar_icon_0_selected'), '首页', 0))

        TabContent() {
          Text('我的内容')
            .fontSize(30)
        }
        .tabBar(this.tabBarBuilder($r('app.media.ic_tabbar_icon_3'), $r('app.media.ic_tabbar_icon_3_selected'), '我的', 1))
      }
      .layoutWeight(1)
      .barPosition(BarPosition.End)
      // 滑动切换时触发
      .onChange((index) => {
        this.selectedIndex = index
      })
      // 点击TabBar时触发
      .onTabBarClick((index) => {
        this.selectedIndex = index
      })
    }
  }

  // 自定义TabBar的Builder函数
  @Builder
  tabBarBuilder(img: ResourceStr, selectedImg: ResourceStr, text: string, index: number) {
    Column() {
      // 根据选中状态切换图片
      Image(this.selectedIndex == index ? selectedImg : img)
        .width(30)
        .height(30)
      // 根据选中状态切换文字颜色
      Text(text)
        .fontSize(12)
        .fontColor(this.selectedIndex == index ? '#efc07e' : Color.Black)
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
  }
}

3. 优化 TabBar 参数(使用接口)

当自定义 TabBar 的参数越来越多时,直接传递多个参数容易出错。咱们可以使用接口来优化参数传递~ 📝

代码示例:

// 定义TabBar参数的接口
interface TabBarParams {
  img: ResourceStr // 默认图标
  selectedImg: ResourceStr // 高亮图标
  text: string // 标签文字
  index: number // 标签索引
}

@Entry
@Component
struct OptimizedTabBarDemo {
  @State selectedIndex: number = 0

  build() {
    Column() {
      Tabs() {
        TabContent() {
          Text('首页内容')
            .fontSize(30)
        }
        // 使用对象传递参数
        .tabBar(this.tabBarBuilder({
          img: $r('app.media.ic_tabbar_icon_0'),
          selectedImg: $r('app.media.ic_tabbar_icon_0_selected'),
          text: '首页',
          index: 0
        }))

        TabContent() {
          Text('我的内容')
            .fontSize(30)
        }
        .tabBar(this.tabBarBuilder({
          img: $r('app.media.ic_tabbar_icon_3'),
          selectedImg: $r('app.media.ic_tabbar_icon_3_selected'),
          text: '我的',
          index: 1
        }))
      }
      .layoutWeight(1)
      .barPosition(BarPosition.End)
      .onChange((index) => {
        this.selectedIndex = index
      })
      .onTabBarClick((index) => {
        this.selectedIndex = index
      })
    }
  }

  // 使用接口类型的参数
  @Builder
  tabBarBuilder(params: TabBarParams) {
    Column() {
      Image(this.selectedIndex == params.index ? params.selectedImg : params.img)
        .width(30)
        .height(30)
      Text(params.text)
        .fontSize(12)
        .fontColor(this.selectedIndex == params.index ? '#efc07e' : Color.Black)
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
  }
}

五、实战案例:小米有品底部导航

咱们来做一个实战案例,模仿小米有品的底部导航效果,这个效果在很多 App 里都很常见哦~ 🛒

效果预览:

切换效果:

需求分析

  1. 底部有 5 个标签:首页、分类、中间特殊按钮、购物车、我的
  2. 中间按钮是特殊样式(突出显示)
  3. 选中时图标和文字会高亮

代码实现

// 定义TabBar参数接口
interface XMTabBarParams {
  img: ResourceStr
  selectedImg: ResourceStr
  text: string
  index: number
}

@Entry
@Component
struct XiaomiYoupinDemo {
  // 保存当前选中的索引
  @State selectedIndex: number = 0

  build() {
    Column() {
      Tabs() {
        // 首页
        TabContent() {
          Image($r('app.media.ic_xiaomi_content_00'))
            .width('100%')
            .height('100%')
        }
        .tabBar(this.tabBarBuilder({
          img: $r('app.media.ic_tabbar_icon_0'),
          selectedImg: $r('app.media.ic_tabbar_icon_0_selected'),
          text: '首页',
          index: 0
        }))

        // 分类
        TabContent() {
          Image($r('app.media.ic_xiaomi_content_01'))
            .width('100%')
            .height('100%')
        }
        .tabBar(this.tabBarBuilder({
          img: $r('app.media.ic_tabbar_icon_1'),
          selectedImg: $r('app.media.ic_tabbar_icon_1_selected'),
          text: '分类',
          index: 1
        }))

        // 中间特殊按钮
        TabContent() {
          Image($r('app.media.ic_xiaomi_content_02'))
            .width('100%')
            .height('100%')
        }
        .tabBar(this.centerTabBarBuilder()) // 使用特殊的TabBar

        // 购物车
        TabContent() {
          Image($r('app.media.ic_xiaomi_content_03'))
            .width('100%')
            .height('100%')
        }
        .tabBar(this.tabBarBuilder({
          img: $r('app.media.ic_tabbar_icon_2'),
          selectedImg: $r('app.media.ic_tabbar_icon_2_selected'),
          text: '购物车',
          index: 3 // 注意这里的索引是3,因为中间按钮占用了索引2
        }))

        // 我的
        TabContent() {
          Image($r('app.media.ic_xiaomi_content_04'))
            .width('100%')
            .height('100%')
        }
        .tabBar(this.tabBarBuilder({
          img: $r('app.media.ic_tabbar_icon_3'),
          selectedImg: $r('app.media.ic_tabbar_icon_3_selected'),
          text: '我的',
          index: 4
        }))
      }
      .layoutWeight(1)
      .barPosition(BarPosition.End)
      .onChange((index) => {
        this.selectedIndex = index
      })
      .onTabBarClick((index) => {
        this.selectedIndex = index
      })
    }
  }

  // 普通TabBar的Builder
  @Builder
  tabBarBuilder(params: XMTabBarParams) {
    Column({ space: 5 }) {
      Image(this.selectedIndex == params.index ? params.selectedImg : params.img)
        .width(30)
        .height(30)
      Text(params.text)
        .fontSize(12)
        .fontColor(this.selectedIndex == params.index ? '#efc07e' : Color.Black)
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
  }

  // 中间特殊TabBar的Builder
  @Builder
  centerTabBarBuilder() {
    Image($r('app.media.ic_xiaomi_center_tabBar'))
      .width(60)
      .height(60)
      .borderRadius(30)
  }
}

六、Tabs 组件的事件

Tabs 组件提供了两个常用事件,咱们可以用来监听标签切换:

事件名 功能描述
onChange(index: number) 标签切换后触发(滑动或点击都触发)
onTabBarClick(index: number) 点击标签栏时触发

代码示例:

@Entry
@Component
struct TabsEventsDemo {
  build() {
    Tabs() {
      TabContent() {
        Text('首页')
          .fontSize(30)
      }
      .tabBar('首页')

      TabContent() {
        Text('推荐')
          .fontSize(30)
      }
      .tabBar('推荐')
    }
    .onChange((index) => {
      console.log('标签切换到了:' + index)
    })
    .onTabBarClick((index) => {
      console.log('点击了标签:' + index)
    })
  }
}

🥦 西兰花小贴士

  1. TabContent 的子组件:每个 TabContent 只能有一个直接子组件,如果需要多个子组件,要使用容器组件(如 Column、Row)包裹
  2. 导航栏位置:设置barPosition(BarPosition.End)可以让导航栏在底部,适合做 App 的主导航
  3. 滑动切换:可以通过scrollable(false)禁用滑动切换,只能通过点击标签切换
  4. 动画时长:使用animationDuration()可以调整标签切换的动画时长
  5. 自定义 TabBar:自定义 TabBar 后,需要自己实现高亮效果,记得同时监听onChangeonTabBarClick事件

📚 官方文档

如果想了解更多关于 Tabs 组件的内容,可以查看官方文档:
鸿蒙 Tabs 组件官方文档

总结

今天咱们学习了 Tabs 组件的全部用法,从基础用法到自定义 TabBar,再到实战案例,是不是感觉已经掌握了这个组件的精髓?

Tabs 组件就像一个分类收纳盒,把不同的内容整理得井井有条,让用户能快速找到自己想要的内容。在实际开发中,它可是咱们的得力助手~ 🛠️

我是盐焗西兰花,
不教理论,只给你能跑的代码和避坑指南。
下期见!🥦

Logo

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

更多推荐