概述

在日常开发中,开发者经常遇到使用Tabs作为导航的场景,包括多层嵌套的Tabs、自定义Tabs样式、Tabs数据加载和动态变更显示的Tabs等。

开发者在实际开发中往往需要处理多个功能点的配合,以及与其他组件或数据的互动。为了帮助开发者更直观和全面地理解Tabs组件,本文通过将这些场景整合到一个应用首页的具体实例中,展示Tabs组件的各项功能及其协同效果,以及与其他组件或数据的联动。

Tabs显示排版

在Tabs组件的应用场景中,开发者通常会自定义Tabs的布局和样式。本章节将介绍Tabs组件提供的几种常用的布局和样式功能。

Tabs导航样式

常见的应用页签导航效果包括底部导航、顶部导航和侧边导航。

在这里插入图片描述

底部导航栏通常用于应用的主导航,其标签数量相对固定,不涉及TabBar滑动。作为应用的主导航,开发者通常会自定义TabBar的样式。底部导航栏可通过设置Tabs的barPosition参数来实现,需将barPosition设置为BarPosition.End。

Tabs({
  barPosition: BarPosition.End,
  // ...
}) {
  // ...
}

顶部导航栏主要用于主栏目的二级导航。由于二级导航可能包含较多的页签项,其TabBar通常设计为可滚动显示,并能动态调整所显示的页签。同样地,顶部导航栏通过将Tabs的barPosition参数设置为BarPosition.Start来实现。

Tabs({
  barPosition: BarPosition.Start,
  // ...
}) {
  // ...
}

侧边导航栏常见于横屏界面的导航。由于横屏界面尺寸规格的差异,导航条的页签需要适配宽度和高度,以确保更佳的显示效果。侧边导航栏的实现方式有所不同,需要将Tabs的vertical属性设置为true,而Tabs的barPosition参数则用于控制导航栏显示在左侧或右侧。

Tabs({
  // ...
}) {
  // ...
}
.vertical(false) // true to make the tab bar in side

页签对齐方式

当页签数量不足,无法铺满屏幕宽度或高度,或者铺满后影响到UI美观时,Tabs提供了自定义导航条页签对齐方式的API。例如,在应用的二级导航中,如果页签较少,可以考虑将页签居左对齐。

在这里插入图片描述

实现原理

通过barModifier属性设置tabBar的align参数,可以实现页签对齐布局效果。类似于文本对齐,开发者可以自行设置居中、居上、居下、居左或者居右对齐。

说明

  • 只有在TabBar的barMode为BarMode.Scrollable时,这些设置才会生效。除此之外,还可以通过barModifier参数设置一系列的通用属性。
  • 居上居下对齐仅在侧边导航栏中生效。若要控制顶部和底部导航栏中页签与顶部的距离,同样可以使用barModifier设置padding属性,以保持页签与TabBar顶部的特定间距。

开发步骤

定义tabBarModifier属性,并将其作为参数构造Tabs,然后通过tabBarModifier设置对齐方式。

@Component
export default struct InTabsComponent {
  // ...
  @State tabBarModifier: CommonModifier = new CommonModifier();
  // ...
  async aboutToAppear() {
    // ...
    this.tabBarModifier.margin({ right: 56 }).align(Alignment.Start);
    // ...
  }
  // ...
  build() {
    // ...
            Tabs({
              // ...
              barModifier: this.tabBarModifier
            }) {
              // ...
            }
            // ...
  }
}

自定义页签

对于底部导航栏,通常用于应用主页面的功能区分。为了更好的用户体验,开发者通常会自定义页签样式。开发者可以使用Tabs组件提供的定制页签样式的API,将页签自定义为图标加文字标题的形式,并且在选中和非选中的状态下,提供不同的样式。

在这里插入图片描述

实现原理

Tabs组件的tabBar()方法接受联合类型的参数,可以将由@Builder修饰的UI构建函数作为参数传入,以自定义TabBar的样式。因此,开发者可以定义一个UI构建函数tabBuilder(),作为参数传递给tabBar()方法。由于选中的页签和未选中的页签需要不同的样式,还需定义一个由@State修饰的数值型变量currentIndex,用于在tabBuilder()函数中判断当前页签是否被选中。当currentIndex发生变化时,能够触发tabBar样式的更新。最后,注册Tabs组件的onchange函数,在该函数中更新currentIndex的值。

开发步骤

  1. 定义currentIndex属性。

    @Component
    export default struct OutTabsComponent {
      @State currentIndex: number = 0;
      // ...
    }
    
  2. 定义@Builder装饰器修饰的自定义样式构建方法tabBuilder()。

    @Builder
    tabBuilder(index: number, name: string | Resource, icon: Resource) {
      Column() {
        // set special styles if selected
    
        SymbolGlyph(icon).fontColor([this.currentIndex === index
          ? $r('app.color.out_tab_bar_font_active_color')
          : $r('app.color.out_tab_bar_font_inactive_color')])
          .fontSize(25)
    
        Text(name)
          .margin({ top: 4 })
          .fontSize(10)
          .fontColor(this.currentIndex === index
            ? $r('app.color.out_tab_bar_font_active_color')
            : $r('app.color.out_tab_bar_font_inactive_color'))
      }
      .justifyContent(FlexAlign.Center)
      .height(Constants.FULL_HEIGHT)
      .width(Constants.FULL_WIDTH)
      .padding({ bottom: 60 })
      // .backgroundColor($r('app.color.out_tab_bar_background_color'))
    }
    
  3. 将tabBuilder()方法传入Tabs,并在Tabs注册onChange()函数,并在其中更新currentIndex属性。

    Tabs({
      // ...
    }) {
      TabContent() {
        InTabsComponent({ switchNext: this.switchNext })
      }.tabBar(this.tabBuilder(0, $r('app.string.out_bar_text_home'), $r('sys.symbol.house')))
      // ...
    }
    // ...
    .onChange((index: number) => {
      this.currentIndex = index;
    })
    

Tabs吸顶

在一些二级导航栏页面中,二级页签的内容上方通常会放置一些banner位或其他优先级较高的内容,并且在向上滑动时会退出显示区域。为了提供更好的用户体验,建议在上划的过程中,导航条能够吸附在顶部,便于用户进行内容切换。

在这里插入图片描述

实现原理

开发者可以通过设置滑动组件的属性nestedScroll来控制父子组件的滑动顺序,从而实现吸顶效果。具体而言,需确保TabContent内容是可滑动的,并且Tabs的上层父组件也必须是可滑动的。为内容组件添加nestedScroll属性,设置为当向上滑动时父组件先动,而向下滑动时自己先动,从而实现滑动吸顶效果。

开发步骤

在Tabs父组件上嵌套Scroll组件,TabContent中的List组件显示内容,List组件本身是可滑动的,仅需设置其滑动触发行为即可。

Scroll() {
  Column() {
    BannerComponent()

    Stack({ alignContent: Alignment.TopEnd }) {
      // ...
      Column() {
        Tabs({
          // ...
        }) {
          // bind selected tabs to ui
          ForEach(this.selectTabsViewModel.selectedTabs, (tab: TabItemViewModel, index: number) => {
            if (index === this.selectTabsViewModel.selectedTabs.length - 1) {
              TabContent() {
                List({ space: 10 }) {
                  // ...
                }
                // ...
                // set the sliding behavior to move up parent first, and move down self first
                .nestedScroll({
                  scrollForward: NestedScrollMode.PARENT_FIRST,
                  scrollBackward: NestedScrollMode.SELF_FIRST
                })
              }
              // ...
            } else {
              // ...
            }
          }, (tab: TabItemViewModel, index: number) => index + '_' + JSON.stringify(tab))
        }
        // ...
      }
      .width(Constants.FULL_WIDTH)
      .height(Constants.FULL_HEIGHT)
      .backgroundColor($r('app.color.out_tab_bar_background_color'))
    }
  }
}

TabsBar显示效果

在某些UI设计风格中,可能需要为TabBar采用特殊样式,比如首页导航栏的毛玻璃背景效果等。

  • 通过设置Tabs组件的barOverlap属性,可以实现TabBar变模糊并叠加在TabContent之上,并且配合barBackgroundBlurStyle

    属性实现毛玻璃效果。

    Tabs({
      // ...
    }) {
      // ...
    }
    // ...
    .barOverlap(true)
    .barBackgroundBlurStyle(BlurStyle.Thin)
    

    底部导航栏覆盖在内容上方,并具有毛玻璃效果。

    在这里插入图片描述

  • 通过barModifier设置tabBar的clip属性,实现页签超出tabBar区域显示效果。

    @Component
    export default struct OutTabComponent {
      // ...
      private controller: TabsController = new TabsController();
    
      aboutToAppear(): void {
        this.tabBarModifier.clip(false);
      }
    
      // ...
    
      build() {
        Column() {
          Tabs({
            // ...
            barModifier: this.tabBarModifier
          }) {
            // ...
          }
          // ...
    
        }
        .width('100%')
        .height('calc(100% + 60vp)')
        .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.BOTTOM])
      }
    }
    

    底层导航栏图标可超出导航条范围。

    在这里插入图片描述

  • 通过配置fadingEdge(true)实现TabBar边缘渐隐。

    Tabs({controller: this.subController}){
      // ...
    }
    .fadingEdge(this.isFadingEdge) // true set tab bar edge fade
    

    顶部导航栏页签靠近两侧会模糊化。

    在这里插入图片描述

  • 通过TabsController的setTabBarTranslate()、setTabBarOpacity()方法可以设置TabBar偏移量及透明度。

    @Component
    export default struct InTabComponent {
      // ...
      private subController: TabsController = new TabsController();
    
      onDidBuild(): void {
        if (this.isSetTabBarTranslateAndOpacity) {
          this.subController.setTabBarTranslate({x:-20,y:30});
          this.subController.setTabBarOpacity(0.5);
        }
      }
      // ...
    
      build() {
        Tabs({controller: this.subController}){
          // ...
        }
        // ...
        .barMode(BarMode.Scrollable)
      }
    }
    

    顶部导航栏位置向左下偏移,并且呈现半透明效果。

    在这里插入图片描述

说明

在以下情况下,该设置无法生效:当显示内容过长时,通常会将其置于可滚动容器组件中,并在向上滑动时隐藏TabBar,向下滑动时显示。此时,会使用bindTabsToScrollable或bindTabsToNestedScrollable等接口将Tabs组件与可滚动容器组件绑定。由于TabBar的控制与滚动组件联动,通过setTabBarOpacity接口设置的TabBar偏移量和不透明度将不再生效。

Logo

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

更多推荐