# 如何设置TabBar布局

#### 问题现象

TabBar的布局效果可以根据不同的设计需求来实现多种样式。以下是一些常见的TabBar布局效果：

*   场景一：如何实现不同方向的TabBar布局效果（如侧边垂直布局、底部水平布局等）？
*   场景二：如何实现页签的居中或左右对齐的布局效果？
*   场景三：如何实现TabBar左右分布的布局效果？
*   场景四：如何实现每个页签宽度均不相同的布局效果？
*   场景五：如何实现页签布局根据数量不同，进行动态调整的效果？

#### 背景知识

*   Tabs组件：通过页签进行内容视图切换的容器组件，每个页签对应一个内容视图。
*   tabBar：设置TabBar上显示内容。

#### 解决方案

本文将基于上述场景，逐一阐述其具体实现方式。

| 问题场景 | 问题描述 | 解决方案 |
| --- | --- | --- |
| 场景一 | 实现不同方向的TabBar布局效果。 | 通过barPosition属性和vertical属性，实现TabBar不同方向的布局。 |
| 场景二 | 实现页签的居中或左右对齐的布局效果。 | 通过barMode可以设置TabBar布局模式，再加上TabsOptions中的barModifier可以设置tabBar的align属性实现页签对齐布局效果。 |
| 场景三 | 实现TabBar左右分布的布局效果。 | 利用Stack组件自定义TabBar，再使用position属性自定义每一个页签的位置。则可实现页签不均匀分布的效果。 |
| 场景四 | 实现每个页签宽度均不相同的布局效果。 | 通过将barMode设置为BarMode.Scrollable可滚动模式，tabBar的宽度即可以根据实际宽度显示。 |
| 场景五 | 实现页签布局根据数量不同，进行动态调整的效果。 | 通过barGridAlign属性可以实现以栅格化方式设置TabBar的可见区域。将barGridAlign属性与页签个数相结合，即可达到动态调整页签布局的效果。 |

*   **场景一：实现不同方向的TabBar布局效果。**
    
    可通过barPosition属性设置页签的位置，其取值为BarPosition.Start或BarPosition.End。默认值为BarPosition.Start，表示页签位于页面顶部；设置为End时，页签则显示在页面底部。
    
    此外，vertical属性用于控制页签的布局方向，默认值为false，表示横向布局（页签位于顶部或底部）；当设为true时，页签变为纵向布局，显示在屏幕左侧或右侧。
    
    综上所述，可以得出下表结论：
    
    | barPosition取值 | vertical取值 | TabBar位置 |
    | --- | --- | --- |
    | BarPosition.Start（默认值） | true | 屏幕左侧 |
    | BarPosition.Start（默认值） | false（默认值） | 屏幕顶部 |
    | BarPosition.End | true | 屏幕右侧 |
    | BarPosition.End | false（默认值） | 屏幕底部 |
    
    用TabBar位于屏幕左侧举例，实现代码如下：
    
    ```
    @Entry
    @Component
    struct TabBarLayoutInDifferentDirections {
      build() {
        Column() {
          Tabs({
            barPosition: BarPosition.Start
          }) {
            TabContent() {
              Column() {
                Text('首页');
              }.width('100%').height('100%')
              .justifyContent(FlexAlign.Center);
            }.tabBar('首页');
    
            TabContent() {
              Column() {
                Text('商城');
              }.width('100%').height('100%')
              .justifyContent(FlexAlign.Center);
            }.tabBar('商城');
    
            TabContent() {
              Column() {
                Text('我的');
              }.width('100%').height('100%')
              .justifyContent(FlexAlign.Center);
            }.tabBar('我的');
          }.vertical(true)
          .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM]);
        };
      }
    }
    ```
    
    实现效果如下：
    
    *   **场景二：实现页签的居中或左右对齐的布局效果。**
        
        通过barMode可以设置TabBar布局模式，再加上TabsOptions中的barModifier可以设置tabBar的align属性实现页签对齐布局效果。
        
        详情可参考示例：页签对齐布局。
        
    *   **场景三：实现TabBar左右分布的布局效果。**
        
        在某些应用场景中，开发者可能需要一个自定义的TabBar布局，其中左边的两个页签居左对齐，右边的两个页签居右对齐，而中间部分则可以放置一个图标或按钮。这种布局方式不仅能够有效利用屏幕空间，还能提供更灵活的导航和功能入口，提升用户体验。
        
        利用Stack组件自定义TabBar，再使用position属性自定义每一个页签的位置。则可实现页签不均匀分布的效果。
        
        实现代码如下：
        
        ```
        @Entry
        @Component
        export struct TabBarAlignLeftAndRight {
          currentIndex: number = 0;
          @State selectedIndex: number = 0;
          fontColor: string = '#000';
          selectedFontColor: string = '#0A59F7';
          tabName: Array<string> = ['首页', '商城', '更多', '我的'];
          private controller: TabsController = new TabsController();
        
          @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 });
            }
            .onClick(() => {
              this.controller.changeIndex(index);
              this.selectedIndex = index;
            })
            .padding(10)
            // 设置TabItem的相对位置
            .position(
              index === 1 ? { left: 80 } :
                index === 2 ? { right: 90 } :
                  index === 3 ? { right: 0 } : {});
          }
        
          build() {
            Stack({ alignContent: Alignment.BottomStart }) {
              Tabs({
                barPosition: BarPosition.End,
                index: this.currentIndex,
                controller: this.controller
              }) {
                TabContent() {
                  Column() {
                    Text('首页');
                  }.width('100%').height('100%')
                  .justifyContent(FlexAlign.Center);
                };
        
                TabContent() {
                  Column() {
                    Text('商城');
                  }.width('100%').height('100%')
                  .justifyContent(FlexAlign.Center);
                };
        
                TabContent() {
                  Column() {
                    Text('更多');
                  }.width('100%').height('100%')
                  .justifyContent(FlexAlign.Center);
                };
        
                TabContent() {
                  Column() {
                    Text('我的');
                  }.width('100%').height('100%')
                  .justifyContent(FlexAlign.Center);
                };
              }
              .barHeight(0)
              .onAnimationStart((index: number, targetIndex: number) => {
                if (index === targetIndex) {
                  return;
                }
                this.selectedIndex = targetIndex;
              });
        
              Stack() {
                // 用Row组件实现TabBar效果
                Row() {
                  ForEach(this.tabName, (item: string, index: number) => {
                    this.tabBuilder(index, item);
                  });
                }
                .justifyContent(FlexAlign.SpaceBetween)
                .alignItems(VerticalAlign.Bottom)
                .width('100%')
                .backgroundColor(Color.White);
        
                // 开发者按需设计中间宫格展示内容，该示例展示图标
                Image($r('app.media.startIcon')) // 资源地址请自行替换
                  .width(25)
                  .height(25)
                  .align(Alignment.Bottom);
              };
            };
          }
        }
        ```
        
        实现效果如下：
        
    *   **场景四：实现每个页签宽度均不相同的布局效果。**
        
        通过将barMode设置为BarMode.Scrollable可滚动模式，tabBar的宽度即可以根据实际宽度显示。
        
        实现代码如下：
        
        ```
        @Entry
        @Component
        struct TabBarLayoutUnevenly {
          build() {
            Column() {
              Tabs() {
                TabContent() {
                  Column() {
                    Text('首页');
                  }.width('100%').height('100%')
                  .justifyContent(FlexAlign.Center);
                }.tabBar('首页');
        
                TabContent() {
                  Column() {
                    Text('一多适配');
                  }.width('100%').height('100%')
                  .justifyContent(FlexAlign.Center);
                }.tabBar('一多适配');
        
                TabContent() {
                  Column() {
                    Text('HarmonyOS应用');
                  }.width('100%').height('100%')
                  .justifyContent(FlexAlign.Center);
                }.tabBar('HarmonyOS应用');
        
                TabContent() {
                  Column() {
                    Text('实况窗');
                  }.width('100%').height('100%')
                  .justifyContent(FlexAlign.Center);
                }.tabBar('实况窗');
              }.barMode(BarMode.Scrollable);
            };
          }
        }
        ```
        
        实现效果如下：
        
        更多实现方式和详情可参考示例：如何给tabBar页签设置不同宽度。
        
    *   **场景五：实现页签布局根据数量不同，进行动态调整的效果。**
        
        通过barGridAlign属性可以实现以栅格化方式设置TabBar的可见区域。将barGridAlign属性与页签个数相结合，即可达到动态调整页签布局的效果。
        
        详情可参考示例：Tabs如何根据TabBar数量更改页签栏排布和设置TabBar栅格化可见区域。
        

#### 常见FAQ

Q：为什么当barHeight与SubTabBarStyle.of同时设置的时候，文字会出现被部分覆盖的错误样式？

A：子页签的内边距默认值：{left:8.0vp,right:8.0vp,top:17.0vp,bottom:18.0vp}。SubTabBarStyle存在默认的内边距，使用时可以主动设置下：tabBar(SubTabBarStyle.of('文本').padding(0))。