【本文正在参加「盲盒」+码有奖征文活动】https://ost.51cto.com/posts/19288
本项目Gitee仓地址:深入浅出eTs学习: 带大家深入浅出学习eTs (gitee.com)
一、需求分析
 相信大家都接过电话吧,咳咳,本节来模拟一个不喜欢接电话的小朋友,在切换界面上加上小红点,具体的通话界面之后可能会做
相信大家都接过电话吧,咳咳,本节来模拟一个不喜欢接电话的小朋友,在切换界面上加上小红点,具体的通话界面之后可能会做
- 实现页面切换
- 不同页面显示不同内容
- 可显示角标(小红点)
二、控件介绍
(1)Tabs
一种可以通过页签进行内容视图切换的容器组件,每个页签对应一个内容视图。
说明:
该组件从API Version 7开始支持。后续版本如有新增内容,则采用上角标单独标记该内容的起始版本。
接口
Tabs(value?: {barPosition?: BarPosition, index?: number, controller?: TabsController})
参数:
| 参数名 | 参数类型 | 必填 | 参数描述 | 
|---|---|---|---|
| barPosition | BarPosition | 否 | 指定页签位置来创建Tabs容器组件。 默认值:BarPosition.Start | 
| index | number | 否 | 指定初次初始页签索引。 默认值:0 | 
| controller | TabsController | 否 | 设置Tabs控制器。 | 
BarPosition枚举说明
| 名称 | 描述 | 
|---|---|
| Start | vertical属性方法设置为true时,页签位于容器左侧;vertical属性方法设置为false时,页签位于容器顶部。 | 
| End | vertical属性方法设置为true时,页签位于容器右侧;vertical属性方法设置为false时,页签位于容器底部。 | 
属性
| 名称 | 参数类型 | 描述 | 
|---|---|---|
| vertical | boolean | 设置为false是为横向Tabs,设置为true时为纵向Tabs。 默认值:false | 
| scrollable | boolean | 设置为true时可以通过滑动页面进行页面切换,为false时不可滑动切换页面。 默认值:true | 
| barMode | BarMode | TabBar布局模式,具体描述见BarMode枚举说明。 默认值:BarMode.Fixed | 
| barWidth | number | Length8+ | TabBar的宽度值。 | 
| barHeight | number | Length8+ | TabBar的高度值。 | 
| animationDuration | number | TabContent滑动动画时长。 默认值:200 | 
BarMode枚举说明
| 名称 | 描述 | 
|---|---|
| Scrollable | TabBar使用实际布局宽度, 超过总长度后可滑动。 | 
| Fixed | 所有TabBar平均分配宽度。 | 

(2)TabContentOpenAtom OpenHarmony
仅在Tabs中使用,对应一个切换页签的内容视图。
说明:
该组件从API Version 7开始支持。后续版本如有新增内容,则采用上角标单独标记该内容的起始版本。
属性
除支持通用属性外,还支持以下属性:
| 名称 | 参数类型 | 描述 | 
|---|---|---|
| tabBar | string | Resource | { icon?: string | Resource, text?: string | Resource } | CustomBuilder8+ | 设置TabBar上显示内容。 CustomBuilder: 构造器,内部可以传入组件(API8版本以上适用)。 > 说明: > 如果icon采用svg格式图源,则要求svg图源删除其自有宽高属性值。如采用带有自有宽高属性的svg图源,icon大小则是svg本身内置的宽高属性值大小。 | 
说明:
- TabContent组件不支持设置通用宽度属性,其宽度默认撑满Tabs父组件。
- TabContent组件不支持设置通用高度属性,其高度由Tabs父组件高度与TabBar组件高度决定。
- TabContent组件不支持触摸热区设置。

// xxx.ets
@Entry
@Component
struct TabContentExample  {
  @State fontColor: string = 'rgba(0, 0, 0, 0.4)'
  @State selectedFontColor: string = 'rgba(10, 30, 255, 1)'
  @State currentIndex: number = 0
  private controller: TabsController = new TabsController()
  @Builder TabBuilder(index: number) {
    Column() {
      Image(this.currentIndex === index ? '/resources/ic_public_contacts_filled_selected.png' : '/resources/ic_public_contacts_filled.png')
        .width(10)
        .height(10)
        .opacity(this.currentIndex === index ? 1 : 0.4)
        .objectFit(ImageFit.Contain)
      Text(`Tab${(index > 2 ? (index - 1) : index) + 1}`)
        .fontColor(this.currentIndex === index ? this.selectedFontColor : this.fontColor)
        .fontSize(10)
        .margin({top: 2})
    }
  }
  @Builder AddBuilder() {
    Column() {
      Image(this.currentIndex === 2 ? '/resources/ic_public_add_norm_filled_selected.png' : '/resources/ic_public_add_norm_filled.png')
        .width(this.currentIndex === 2 ? 26 : 24)
        .height(this.currentIndex === 2 ? 26 : 24)
        .opacity(this.currentIndex === 2 ? 1 : 0.4)
        .objectFit(ImageFit.Contain)
        .animation({duration: 200})
    }
  }
  build() {
    Column() {
      Tabs({ barPosition: BarPosition.End, controller: this.controller }) {
        TabContent() {
          Flex({justifyContent: FlexAlign.Center}) {
            Text('Tab1').fontSize(32)
          }
        }.tabBar(this.TabBuilder(0))
        TabContent() {
          Flex({justifyContent: FlexAlign.Center}) {
            Text('Tab2').fontSize(32)
          }
        }.tabBar(this.TabBuilder(1))
        TabContent() {
          Flex({justifyContent: FlexAlign.Center}) {
            Text('Add').fontSize(32)
          }
        }.tabBar(this.AddBuilder())
        TabContent() {
          Flex({justifyContent: FlexAlign.Center}) {
            Text('Tab3').fontSize(32)
          }
        }.tabBar(this.TabBuilder(3))
        TabContent() {
          Flex({justifyContent: FlexAlign.Center}) {
            Text('Tab4').fontSize(32)
          }
        }.tabBar(this.TabBuilder(4))
      }
      .vertical(false)
      .barWidth(300).barHeight(56)
      .onChange((index: number) => {
        this.currentIndex = index
      })
      .width('90%').backgroundColor('rgba(241, 243, 245, 0.95)')
    }.width('100%').height(200).margin({ top: 5 })
  }
}
(3)BadgeOpenAtom OpenHarmony
可以附加在单个组件上用于信息标记的容器组件。
说明: 该组件从API Version 7开始支持。后续版本如有新增内容,则采用上角标单独标记该内容的起始版本。
Badge({
  value: ' ',
  position: BadgePosition.Left,                // 设置 badge 居左显示
  style: {badgeSize: 10, badgeColor: Color.Red}// 设置 badge 的显示样式
}) {
  Text("Badge")
    .size({width: 100, height: 50})
    .fontSize(20)
    .backgroundColor("#aabbcc")
}
.size({width: 100, height: 50})
Badge({
  value: ' ',
  position: BadgePosition.Right,               // 设置 badge 居右显示
  style: {badgeSize: 10, badgeColor: Color.Red}// 设置 badge 的显示样式
}) {
  Text("Badge")
    .size({width: 100, height: 50})
    .fontSize(20)
    .backgroundColor("#aabbcc")
}
.size({width: 100, height: 50})
Badge({
  value: ' ',
  position: BadgePosition.RightTop,            // 设置 badge 居右上角显示
  style: {badgeSize: 10, badgeColor: Color.Red}// 设置 badge 的显示样式
}) {
  Text("Badge")
    .size({width: 100, height: 50})
    .fontSize(20)
    .backgroundColor("#aabbcc")
}
.size({width: 100, height: 50})

三、UI设计
(1)页面切换
首先需要创建一个TabContent组件,实现页面切换

@Entry @Component struct TabsTest {
  private controller: TabsController = new TabsController();
  @State index: number = 0; // 选项卡下标,默认为第一个
  @Builder tabMessage() {   // 自定义消息标签
    Column() {
      Column() {
        Blank()
        Image(this.index == 0 ? 'pages/icon_message_selected.png' : 'pages/icon_message_normal.png')
          .size({width: 25, height: 25})
        Text('消息')
          .fontSize(16)
          .fontColor(this.index == 0 ? "#2a58d0" : "#6b6b6b")
        Blank()
      }
      .height('100%')
      .width("100%")
      .onClick(() => {
        this.index = 0;
        this.controller.changeIndex(this.index);
      })
    }
  }
  @Builder tabContract() {  // 自定义联系人标签
    Column() {
      Blank()
      Image(this.index == 1 ? 'pages/icon_contract_selected.png' : 'pages/icon_contract_normal.png')
        .size({width: 25, height: 25})
      Text('联系人')
        .fontSize(16)
        .fontColor(this.index == 1 ? "#2a58d0" : "#6b6b6b")
      Blank()
    }
    .height('100%')
    .width("100%")
    .onClick(() => {
      this.index = 1;
      this.controller.changeIndex(this.index);
    })
  }
  @Builder tabDynamic() {   // 自定义动态标签
    Column() {
      Blank()
      Image(this.index == 2 ? 'pages/icon_dynamic_selected.png' : 'pages/icon_dynamic_normal.png')
        .size({width: 25, height: 25})
      Text('动态')
        .fontSize(16)
        .fontColor(this.index == 2 ? "#2a58d0" : "#6b6b6b")
      Blank()
    }
    .height('100%')
    .width("100%")
    .onClick(() => {
      this.index = 2;
      this.controller.changeIndex(this.index);
    })
  }
  build() {
    Column() {
      Tabs({
        barPosition: BarPosition.End, // TabBar排列在下方
        controller: this.controller   // 绑定控制器
      }) {
        TabContent() {
          Column() {
            Text('消息')
              .fontSize(30)
          }
          .width('100%')
          .height('100%')
          .backgroundColor("#aabbcc")
        }
        .tabBar(this.tabMessage)      // 使用自定义TabBar
        TabContent() {
          Column() {
            Text('联系人')
              .fontSize(30)
          }
          .width('100%')
          .height('100%')
          .backgroundColor("#bbccaa")
        }
        .tabBar(this.tabContract)     // 使用自定义TabBar
        TabContent() {
          Column() {
            Text('动态')
              .fontSize(30)
          }
          .width('100%')
          .height('100%')
          .backgroundColor("#ccaabb")
        }
        .tabBar(this.tabDynamic)      // 使用自定义TabBar
      }
      .width('100%')
      .height('100%')
      .barHeight(60)
      .barMode(BarMode.Fixed)         // TabBar均分
      .onChange((index: number) => {  // 页面切换回调
        this.index = index;
      })
    }
    .width('100%')
    .height('100%')
  }
}
(2)图标修改
 在Tab组件中,图标需要选择两种风格,一个是没有点击到的,另一个是被选中的状态,我这里使用本色和红色来演示
在Tab组件中,图标需要选择两种风格,一个是没有点击到的,另一个是被选中的状态,我这里使用本色和红色来演示
 

(3)角标添加
这里加入传统的角标颜色,小红点来实现,当前未接电话有14个

  @Builder tabMessage() {   // 自定义消息标签
    Badge({
      count: 14,                       // 设置 badge 显示的数量
      maxCount: 100,                   // 设置 badge 显示的最大数量
      position: BadgePosition.RightTop,// 设置 badge 显示在右上角
      style: {badgeColor: Color.Red,fontSize:15,badgeSize:30}   // 设置 badge 的显示样式
       }) {
      Column() {
        Column() {
          Blank()
          Image(this.index == 0 ? 'pages/电话2.png' : 'pages/电话.png')
            .size({ width: 25, height: 25 })
          Text('电话')
            .fontSize(16)
            .fontColor(this.index == 0 ? "#2a58d0" : "#6b6b6b")
          Blank()
        }
        .height('100%')
        .width("100%")
        .onClick(() => {
          this.index = 0;
          this.controller.changeIndex(this.index);
        })
      }
    }
  }
四、成功展示

本文作者:程皖Orz
 
 
                     
            
        













 
                    

 
                 
                    