HarmonyOS底部导航栏之tabs组件

812 阅读3分钟

Tabs

当需要对页面内容进行分开展示时,为了用户把关注点聚焦到当前页面,需要对页面内容进行分类,提高用户体验及页面的合理利用。Tabs组件可以在一个页面中实现快速切换,提高用户查找信息的效率。

Tabs-容器组件-组件参考(基于ArkTS的声明式开发范式)-ArkTS API参考-HarmonyOS应用开发

Tabs属性

barPosition

设置Tabs的页签位置(方向),默认值:BarPosition.Start

名称描述
Startvertical属性方法设置为true时,页签位于容器左侧;vertical属性方法设置为false时,页签位于容器顶部。
Endvertical属性方法设置为true时,页签位于容器右侧;vertical属性方法设置为false时,页签位于容器底部。

index

设置当前显示页签的索引,默认值:0

controller

设置Tabs控制器

布局方式

Tabs组件包含两部分,即TabBar和TabContent。TabBar是标签区域,TabContent是内容区域,设置不同的导航类型,那么布局也是不一样的,可以分别设置为顶部导航栏、底部导航栏、左侧导航栏和右侧导航栏。

顶部布局

@Entry
@Component
struct Index {
  // 导航数组
  @State list:string[] = ["首页","分类","应用","我的"]
  build() {
    Column() {
      Tabs({barPosition:BarPosition.Start}){
        ForEach(this.list,(item)=>{
          TabContent(){
            Text(`${item}内容`)
          }.tabBar(item)
        })
      }
      .vertical(false)
    }
    .height('100%')
  }
}

结果如下:

image.png

底部布局

@Entry
@Component
struct Index {
  // 导航数组
  @State list:string[] = ["首页","分类","应用","我的"]
  build() {
    Column() {
      Tabs({barPosition:BarPosition.End}){
        ForEach(this.list,(item)=>{
          TabContent(){
            Text(`${item}内容`)
          }.tabBar(item)
        })
      }
      .vertical(false)
    }
    .height('100%')
  }
}

结果如下:

image.png

左侧布局

@Entry
@Component
struct Index {
  // 导航数组
  @State list:string[] = ["首页","分类","应用","我的"]
  build() {
    Column() {
      Tabs({barPosition:BarPosition.Start}){
        ForEach(this.list,(item)=>{
          TabContent(){
            Text(`${item}内容`)
          }.tabBar(item)
        })
      }
      .vertical(true)
    }
    .height('100%')
  }
}

结果如下:

image.png

右侧布局

@Entry
@Component
struct Index {
  // 导航数组
  @State list:string[] = ["首页","分类","应用","我的"]
  build() {
    Column() {
      Tabs({barPosition:BarPosition.End}){
        ForEach(this.list,(item)=>{
          TabContent(){
            Text(`${item}内容`)
          }.tabBar(item)
        })
      }
      .vertical(true)
    }
    .height('100%')
  }
}

结果如下:

image.png

限制导航栏滑动切换

当我们在开发某些功能的时候,有的场景需要使用到滑动切换页面来聚焦内容,而有的场景是通过切换选项卡来聚焦内容。所以这里使用到scrollable属性来设置是否限制导航栏滑动切换操作。

scrollable:设置为true时可以通过滑动页面进行页面切换,为false时不可滑动切换页面。默认值:true

不限制滑动切换

拖动页面会切换到不同的导航栏

@Entry
@Component
struct Index {
  @State message: string = 'Hello World'

  build() {
    Column() {
      Tabs({barPosition:BarPosition.Start}){
        ...
      }
      .vertical(false)
      .scrollable(true)
    }
    .height('100%')
  }
}

限制滑动切换

拖动页面不会切换导航栏

@Entry
@Component
struct Index {
  @State message: string = 'Hello World'

  build() {
    Column() {
      Tabs({barPosition:BarPosition.Start}){
        ...
      }
      .vertical(false)
      .scrollable(false)
    }
    .height('100%')
  }
}

滚动导航栏

滚动导航栏需要设置tabs的borMode属性,默认设置是BarMode.Fixed,所有TabBar平均分配barWidth宽度,所以导航栏固定。设置为BarMode.Scrollable,每一个TabBar均使用实际布局宽度,超过总长度(横向Tabs的barWidth,纵向Tabs的barHeight)后可滑动。

@Entry
@Component
struct Index {
  // 导航数组
  @State list:string[] = ["新闻","财经","科技","体育","娱乐","汽车","博客"]
  build() {
    Column() {
      Tabs({barPosition:BarPosition.Start}){
        ForEach(this.list,(item)=>{
          TabContent(){
            Text(`${item}内容`)
          }.tabBar(item)
        })
      }
      .barWidth('80%')//横向Tabs的barWidth的宽度
      .barMode(BarMode.Scrollable)
    }
    .height('100%')
  }
}

结果如下: 拖动导航栏区域可以滑动导航栏

image.png

自定义导航栏

当tabs组件不满足我们开发各式各样需求的时候,官方也是支持自定义导航栏的,设置tabBar支持自定义函数组件@Builder,例如如下,自定义组件TabBuilder,传入要显示的参数(可根据自己的方式灵活传入),根据currentIndex字段来控制当前导航栏切换到第几个页面,渲染第几个页面的UI界面

@Entry
@Component
struct Index {
  // 导航数组
  @State list:string[] = ["首页","分类","应用","我的"]
  // 未选中颜色
  @State fontColor: string = '#182431'
  // 选中颜色
  @State selectedFontColor: string = '#007DFF'
  // 当前活动页面
  @State currentIndex: number = 0
  // 控制器
  private controller: TabsController = new TabsController()
  // 自定义导航栏
  @Builder TabBuilder(index: number, name: string) {
    Column() {
      Image($r('app.media.icon')).width(25).height(25).margin({bottom:5})
      Text(name)
        .fontColor(this.currentIndex === index ? this.selectedFontColor : this.fontColor)
        .fontSize(12)
    }
    .width('100%')
    .padding({top:5})
    .border({width:{top:1},color:"#e3e3e3"})
  }
  build() {
    Column() {
      Tabs({barPosition:BarPosition.End, controller: this.controller}){
        ForEach(this.list,(item,index)=>{
          TabContent(){
            Text(`${item}内容`)
          }.tabBar(this.TabBuilder(index,item.toString()))
        })
      }
      .onChange((index: number) => {
        this.currentIndex = index
      })
    }
    .height('100%')
  }
}

结果如下:

image.png

切换导航栏改变页面数据

默认的tabs组件是一开始就初始化所有导航栏页面,当我们需要在切换后再渲染内容或调用接口,那么页面是不会再执行初始化操作,所以接下来就实现切换后能到达再次渲染内容或调用接口的效果

在这里我们会使用到@State装饰器和@Link装饰器来进行父子组件数据同步,使用@Watch监听状态改变

1、创建自定义组件

创建view目录及article.ets、category.ets、home.ets和user.ets四个ets文件

注:页面内容会有报错,不要着急,下一步就编写主ets文件内容

image.png home.ets页面内容

@Component
export struct Home{
  // 监听currentIndex字段
  @Link @Watch('onCurrentIndex') currentIndex: number;
  @State message:string = "默认内容"
  // 监听currentIndex改变
  onCurrentIndex(){
    if(this.currentIndex==0){
      this.message = `当前第几个活动页面:${this.currentIndex},改变默认内容`
    }
  }
  build(){
    Column(){
      Text(this.message)
    }
  }
}

article.ets页面内容

@Component
export  struct Article{
  // 监听currentIndex字段
  @Link @Watch('onCurrentIndex') currentIndex: number;
  @State message:string = "默认内容"
  // 监听currentIndex改变
  onCurrentIndex(){
    if(this.currentIndex==1){
      this.message = `当前第几个活动页面:${this.currentIndex},改变默认内容`
    }
  }
  build(){
    Column(){
      Text(this.message)
    }
  }
}

category.ets页面内容

@Component
export struct Category{
  // 监听currentIndex字段
  @Link @Watch('onCurrentIndex') currentIndex: number;
  @State message:string = "默认内容"
  // 监听currentIndex改变
  onCurrentIndex(){
    if(this.currentIndex==2){
      this.message = `当前第几个活动页面:${this.currentIndex},改变默认内容`
    }
  }
  build(){
    Column(){
      Text(this.message)
    }
  }
}

user.ets页面内容

@Component
export  struct User{
  // 监听currentIndex字段
  @Link @Watch('onCurrentIndex') currentIndex: number;
  @State message:string = "默认内容"
  // 监听currentIndex改变
  onCurrentIndex(){
    if(this.currentIndex==3){
      this.message = `当前第几个活动页面:${this.currentIndex},改变默认内容`
    }
  }
  build(){
    Column(){
      Text(this.message)
    }
  }
}

2、pages/Index.ets文件内容如下

import { Home } from '../view/home'
import { Article } from '../view/article'
import { Category } from '../view/category'
import { User } from '../view/user'

@Entry
@Component
struct Index {
  // 当前活动页面
  @State currentIndex: number = 0
  // 控制器
  private controller: TabsController = new TabsController()

  build() {
    Column() {
      Tabs({barPosition:BarPosition.End, controller: this.controller}){
        TabContent() {
          Home({currentIndex:$currentIndex})
        }
        .tabBar(this.TabBuilder(0,'首页'))

        TabContent() {
          Article({currentIndex:$currentIndex})
        }
        .tabBar(this.TabBuilder(1,'分类'))

        TabContent() {
          Category({currentIndex:$currentIndex})
        }
        .tabBar(this.TabBuilder(2,'文章'))

        TabContent() {
          User({currentIndex:$currentIndex})
        }
        .tabBar(this.TabBuilder(3,'我的'))
      }
      .onChange((index: number) => {
        this.currentIndex = index
      })
    }
    .height('100%')
  }
  // 自定义导航栏
  @Builder TabBuilder(index: number, name: string) {
    Column() {
      Image($r('app.media.icon')).width(25).height(25).margin({bottom:5})
      Text(name)
        .fontColor(this.currentIndex === index ? '#007DFF' : '#182431')
        .fontSize(12)
    }
    .width('100%')
    .padding({top:5})
    .border({width:{top:1},color:"#e3e3e3"})
  }
}

结果: 当我们切换导航栏选项时,会同步currentIndex的内容,然后监听到currentIndex是否有改变,如果有改变则进行再次渲染内容或自定义方法等操作。

默认加载

image.png

切换监听

image.png