# TabBar如何自定义下划线

#### 问题现象

TabBar页签下划线的样式如何自定义，如设置下划线的宽、高、颜色、圆角、间距等。

#### 背景知识

Tabs：通过页签进行内容视图切换的容器组件，每个页签对应一个内容视图。仅支持子组件TabContent，以及渲染控制类型if/else和ForEach，不建议自定义组件作为子组件。TabContent设置页签的接口有以下三个：

*   tabBar(options: string | Resource | CustomBuilder | TabBarOptions)：设置TabBar上显示内容，入参为string时有下划线效果。
*   tabBar(value: SubTabBarStyle | BottomTabBarStyle)：设置TabBar上显示内容。底部样式没有下划线效果。
*   tabBar(content: ComponentContent | SubTabBarStyle | BottomTabBarStyle | string | Resource | CustomBuilder | TabBarOptions)：设置TabBar上显示内容。

#### 解决方案

TabBar的下划线不仅是视觉焦点，更是用户感知当前页面的关键信号，下面将提供TabBar下划线的三种制作方案，开发者可以按需参考使用：

| 方案 | 使用场景 |
| --- | --- |
| 基于tabBar(SubTabBarStyle)接口实现下划线效果。 | 基于SubTabBarStyle实现下划线，实现简单，但是对于复杂的页签栏样式无法满足。 |
| 基于tabBar(CustomBuilder)接口实现下划线效果。 | 下划线需要自定义实现，实现难度中等，页签栏样式开放程度高于方案一。 |
| 使用其他组件如List等，组合实现TabBar页签。 | 页签栏和下划线均自定义实现，页签栏样式丰富程度高，但实现较复杂。 |

方案一：基于tabBar(SubTabBarStyle)接口实现。

*   TabBar自定义下划线的样式可以基于SubTabBarStyle实现，通过其indicator属性设置选中子页签的下划线风格。通过入参IndicatorStyle可以设置下划线的颜色、宽高、圆角以及和页签文字的间距。具体使用请参考官网示例设置子页签下划线基本属性。

方案二：基于tabBar(CustomBuilder)接口实现。有以下两种实现方式：

*   方式一：参考官网示例自定义页签切换联动的实现方式，每个页签的内容由Text文本和Divider下划线组成，若需要设置下划线宽度与页签文本宽度一致，则需要对官网示例做出以下修改：使用onAreaChange获取Text文本宽度数据并使用状态变量保存，通过状态变量设置下划线的宽度与文本宽度一致。
    
    ```
    @Entry
    @Component
    struct TabBarUnderLine {
      fontColor: string = '#182431';
      selectedFontColor: string = '#007DFF';
      @State currentIndex: number = 0;
      @State selectedIndex: number = 0;
      private controller: TabsController = new TabsController();
      @State dividerWidthArr: Length[] = [];
    
      @Builder
      tabBuilder(index: number, name: string) {
        Column() {
          Text(name)
            .fontColor(this.selectedIndex === index ? this.selectedFontColor : this.fontColor)
            .fontSize(16)
            .fontWeight(this.selectedIndex === index ? 500 : 400)
            .lineHeight(22)
            .margin({ top: 17, bottom: 7 })
            .onAreaChange((oldValue: Area, newValue: Area) => {
              console.info(`${JSON.stringify(oldValue.width)} ${JSON.stringify(newValue.width)}`);
              this.dividerWidthArr[index] = newValue.width;
            });
          Divider()
            .strokeWidth(2)
            .color('#007DFF')
            .opacity(this.selectedIndex === index ? 1 : 0)
            .width(this.dividerWidthArr[index]);
        }.width('100%');
      }
    
      build() {
        Column() {
          Tabs({ barPosition: BarPosition.Start, index: this.currentIndex, controller: this.controller }) {
            TabContent() {
              Column().width('100%').height('100%').backgroundColor('#00CB87');
            }.tabBar(this.tabBuilder(0, 'green'));
    
            TabContent() {
              Column().width('100%').height('100%').backgroundColor('#007DFF');
            }.tabBar(this.tabBuilder(1, 'blue'));
    
            TabContent() {
              Column().width('100%').height('100%').backgroundColor('#FFBF00');
            }.tabBar(this.tabBuilder(2, 'yellow'));
    
            TabContent() {
              Column().width('100%').height('100%').backgroundColor('#E67C92');
            }.tabBar(this.tabBuilder(3, 'pink'));
          }
          .vertical(false)
          .barMode(BarMode.Fixed)
          .barWidth(360)
          .barHeight(56)
          .animationDuration(400)
          .onChange((index: number) => {
            // currentIndex控制TabContent显示页签
            this.currentIndex = index;
            this.selectedIndex = index;
          })
          .onAnimationStart((index: number, targetIndex: number, event: TabsAnimationEvent) => {
            console.info(`${event}`);
            if (index === targetIndex) {
              return;
            }
            // selectedIndex控制自定义TabBar内Image和Text颜色切换
            this.selectedIndex = targetIndex;
          })
          .width(360)
          .height(296)
          .margin({ top: 52 })
          .backgroundColor('#F1F3F5');
        }.width('100%');
      }
    }
    ```
    
*   方式二：参考官网示例自定义TabBar切换动画的实现方式，不同于方式一为每个页签设置一个下划线，此示例使用Stack组件在Tabs上层设置一个下划线，在页签切换时改变下划线的位置，并保证下划线与当前页签文本宽度一致，且切换过程中实现了下划线的滑动动画。较方式一实现更复杂，但具有更好的动画效果。
*   说明：方式一、二仅改变了下划线宽度，若要实现方案一中颜色、间距等样式的定义，需设置下划线Divider的color、margin等属性。

方案三：不使用官方提供的tabBar接口，使用其它组件（如List、Column等）组合实现TabBar页签。

*   可以参考自定义Tabs样式，TabBar底部指示器如何对齐的实现，其使用List实现TabBar页签栏，通过设置Column宽高和背景色实现页签下划线。在此示例中要实现更多下划线样式，需修改Column的color、margin等属性。

#### 常见FAQ

Q：TabBar怎么取消文字下划线？

A：使用tabBar(value: SubTabBarStyle | BottomTabBarStyle)接口，入参为BottomTabBarStyle，底部样式没有下划线效果。或者使用tabBar(options: string | Resource | CustomBuilder | TabBarOptions)接口，入参为CustomBuilder，不绘制下划线，则可取消文字下划线效果。