自定义Tabs组件————Bond! 超级无敌具详细,你就看吧烙铁!!! 原创

bond_heloworld
发布于 2024-12-26 09:58
浏览
2收藏

1. 概述

组件名称: Tabs
描述:
Tabs组件用于在不同的页面或内容块之间进行切换,通常用于底部导航栏或顶部标签栏。它提供了一种直观且用户友好的方式来管理多个相关页面或内容,使得应用界面更加清晰和易于操作。
主要功能:
页面切换: 允许用户在多个相关页面或内容块之间进行快速切换。
自定义动画效果: 支持自定义内容切换时的动画效果,如透明度变化、缩放等,提供更加丰富的视觉体验。
事件监听: 提供事件监听机制(如页面显示、动画开始等),帮助开发者在特定事件发生时执行自定义逻辑。
可配置性: 支持多种配置选项,如标签栏的位置、模式、大小、颜色等,使开发者可以根据应用需求进行灵活定制。

通过使用Tabs组件,开发者可以轻松实现复杂的应用导航结构,并提供一致且易于使用的用户界面。
页面切换。
自定义动画效果。
事件监听(如页面显示、动画开始等)。

2. 使用场景

Tabs组件在多种应用场景中都非常有用,特别是在需要在多个相关页面或内容块之间进行快速切换的情况下。以下是一些典型的使用场景:

2.1 社交媒体应用

底部导航栏: 用于在“首页”、“发现”、“消息”和“个人中心”之间切换。
示例: 用户可以在底部导航栏中点击不同的图标来查看不同的内容。

2.2 电商平台

顶部标签栏: 用于在“首页”、“分类”、“购物车”和“我的”之间切换。
示例: 用户可以在顶部标签栏中选择不同的分类来浏览商品。

2.3 新闻客户端

底部导航栏: 用于在“头条”、“热点”、“本地”和“我的”之间切换。
示例: 用户可以在底部导航栏中点击不同的标签来查看不同类别的新闻。

2.4 个人管理应用

底部导航栏: 用于在“计划”、“任务”、“日程”和“个人资料”之间切换。
示例: 用户可以在底部导航栏中选择不同的标签来管理个人计划和任务。

2.5 多功能工具应用

顶部标签栏: 用于在“工具A”、“工具B”、“工具C”和“设置”之间切换。
示例: 用户可以在顶部导航栏中选择不同的工具来执行特定的功能。

2.6 音乐播放器

底部导航栏: 用于在“播放列表”、“搜索”、“发现”和“我的音乐”之间切换。

2.7 地图应用

顶部标签栏: 用于在“地图”、“导航”、“搜索”和“设置”之间切换。
通过使用Tabs组件,开发者可以为用户提供一种直观且易于操作的导航方式,使得应用界面更加清晰和易于管理。这些场景展示了Tabs组件在不同类型的移动应用中的广泛应用。

3. API说明

3.1 属性:

barPosition: 标签栏的位置,支持BarPosition.Start、BarPosition.End、BarPosition.Top、BarPosition.Bottom。
index: 当前选中的标签页索引。
controller: 控制标签页的控制器。
vertical: 是否垂直排列标签页,默认为false。
barMode: 标签栏的模式,支持BarMode.Fixed、BarMode.Scrollable。
barWidth: 标签栏的宽度。
barHeight: 标签栏的高度。
animationMode: 动画模式,支持AnimationMode.NoAnimation、AnimationMode.BasicAnimation等。
animationDuration: 动画的持续时间。
barBackgroundColor: 标签栏的背景颜色。

3.2 事件:

onChange: 当标签页切换时触发。
onAnimationStart: 当标签页动画开始时触发。

3.3 方法:

TabContent: 用于定义每个标签页的内容。
tabBar: 用于定义标签栏的样式。
customContentTransition: 自定义内容切换的动画效果。

3.4 接口

interface itemType {
  text: string,
  backgroundColor: Color
}

3.5 类

class TabsController {
  setCurrentIndex(index: number): void;
}

3.6 动画配置

TabContentAnimatedTransition

interface TabContentAnimatedTransition {
  timeout: number,
  transition: (proxy: TabContentTransitionProxy) => void
}

TabContentTransitionProxy

interface TabContentTransitionProxy {
  finishTransition(): void;
}

3.7 示例

@Entry
@Component
struct TabsExample {
  @State currentIndex: number = 0
  @State selectedIndex: number = 0
  private controller: TabsController = new TabsController()

  @Builder tabBuilder(index: number, name: string, selectIcon: Resource, border_radius_left: number, border_radius_right: number) {
    Column() {
      Column() {
        Image(selectIcon)
          .width(28)
          .height(26)
          .fillColor(this.selectedIndex === index ? $r('app.color.font_color') : '#9C9C9C')
        Text(name)
          .fontSize(16)
          .fontColor(this.currentIndex === index ? $r('app.color.font_color') : "#9C9C9C")
      }.width('100%').height('80%')
    }.width('100%').height('80%')
    .backgroundColor($r('app.color.select_menu_background'))
    .borderRadius({topLeft: border_radius_left, topRight: border_radius_right, bottomLeft: border_radius_left, bottomRight: border_radius_right})
    .margin({left: border_radius_left, right: border_radius_right, bottom: '10%'})
  }

  @State opacityList: number[] = []
  @State scaleList: number[] = []

  private durationList: number[] = []
  private timeoutList: number[] = []

  private customContentTransition: (from: number, to: number) => TabContentAnimatedTransition = (from: number, to: number) => {
    let tabContentAnimatedTransition = {
      timeout: this.timeoutList[from],
      transition: (proxy: TabContentTransitionProxy) => {
        this.scaleList[from] = 1.0
        this.scaleList[to] = 0.5
        this.opacityList[from] = 1.0
        this.opacityList[to] = 0.5
        animateTo({
          duration: this.durationList[from],
          onFinish: () => {
            proxy.finishTransition()
          }
        }, () => {
          this.scaleList[from] = 0.5
          this.scaleList[to] = 1.0
          this.opacityList[from] = 0.5
          this.opacityList[to] = 1.0
        })
      }
    } as TabContentAnimatedTransition
    return tabContentAnimatedTransition
  }

  aboutToAppear(): void {
    let duration = 250
    let timeout = 250
    for (let i = 1; i <= this.data.length; i++) {
      this.opacityList.push(1.0)
      this.scaleList.push(1.0)
      this.durationList.push(duration)
      this.timeoutList.push(timeout)
    }
  }

  build() {
    Row() {
      Column() {
        Tabs({ barPosition: BarPosition.End, index: this.currentIndex, controller: this.controller }) {
          TabContent() {
            Index().width('100%').height('100%')
          }.tabBar(this.tabBuilder(0, '探索', $r('app.media.ball'), 25, 0))
          .opacity(this.opacityList[0])
          .scale({ x: this.scaleList[0], y: this.scaleList[0] })

          TabContent() {
            Plan().width('100%').height('100%')
          }.tabBar(this.tabBuilder(1, '规划', $r('app.media.signSvg'), 0, 0))
          .opacity(this.opacityList[1])
          .scale({ x: this.scaleList[1], y: this.scaleList[1] })

          TabContent() {
            MyPage().width('100%').height('100%')
          }.tabBar(this.tabBuilder(2, '我的', $r('app.media.chat'), 0, 25))
          .opacity(this.opacityList[2])
          .scale({ x: this.scaleList[2], y: this.scaleList[2] })
        }
        .customContentTransition(this.customContentTransition)
        .animationMode(AnimationMode.NO_ANIMATION)
        .barBackgroundColor($r('app.color.background_color'))
        .vertical(false)
        .barMode(BarMode.Fixed)
        .barWidth("88%")
        .barHeight(56)
        .animationDuration(200)
        .onChange((index: number) => {
          this.currentIndex = index
          this.selectedIndex = index
        })
        .onAnimationStart((index: number, targetIndex: number, event: TabsAnimationEvent) => {
          if (index === targetIndex) {
            return
          }
          this.selectedIndex = targetIndex
        })
        .height('100%')
        .width('100%')
      }
      .width('100%')
      .height('100%')
      .borderRadius(10)
    }
    .backgroundColor($r('app.color.background_color'))
    .justifyContent(FlexAlign.Center)
    .width('100%')
    .height('100%')
  }
}

4. 详细说明

提供一些简单的示例代码,展示如何使用Tabs组件。
可以包括一个基础的使用示例,以及一个自定义动画效果的示例。

4.1 属性详细说明

barPosition: 标签栏的位置,可选值为BarPosition.Start、BarPosition.End、BarPosition.Top、BarPosition.Bottom。
示例:

Tabs({ barPosition: BarPosition.End })

index: 当前选中的标签页索引,初始值为0。
示例:

Tabs({ index: this.currentIndex })

controller: 控制标签页的控制器,可以通过该控制器进行程序化控制。

示例:

Tabs({ controller: this.controller })

vertical: 是否垂直排列标签页,默认为false,即水平排列。
示例:

Tabs({ vertical: false })

barMode: 标签栏的模式,可选值为BarMode.Fixed、BarMode.Scrollable。

示例:

Tabs({ barMode: BarMode.Fixed })

barWidth: 标签栏的宽度,默认为"100%"。

示例:

Tabs({ barWidth: "88%" })

barHeight: 标签栏的高度,默认为48。
示例:

Tabs({ barHeight: 56 })

barHeight: 标签栏的高度,默认为48。
示例:

Tabs({ barHeight: 56 })

animationMode: 动画模式,可选值为AnimationMode.NO_ANIMATION、AnimationMode.BASIC_ANIMATION。

示例:

Tabs({ animationMode: AnimationMode.NO_ANIMATION })

animationDuration: 动画的持续时间(单位:毫秒),默认为200。
示例:

Tabs({ animationDuration: 200 })

barBackgroundColor: 标签栏的背景颜色,默认为#FFFFFF。

示例:

Tabs({ barBackgroundColor: $r('app.color.background_color') })

4.2 事件详细说明

onChange: 当标签页切换时触发。

参数: index(新选中的标签页索引)。
示例:

.onChange((index: number) => {
  this.currentIndex = index;
  this.selectedIndex = index;
})

onAnimationStart: 当标签页动画开始时触发。

参数: index(当前标签页索引),targetIndex(目标标签页索引),event(动画事件对象)。

示例:

.onAnimationStart((index: number, targetIndex: number, event: TabsAnimationEvent) => {
  if (index === targetIndex) {
    return;
  }
  this.selectedIndex = targetIndex;
})

4.3 方法详细说明

TabContent: 定义每个标签页的内容。
示例

TabContent() {
  Index().width('100%').height('100%')
}

tabBar: 定义标签栏的样式,包括图片、文字、颜色等。
参数:
index(标签页索引)。
name(标签页名称)。
selectIcon(选中时的图标资源)。
border_radius_left(左上角和左下角的圆角半径)。
border_radius_right(右上角和右下角的圆角半径)。
示例:

.tabBar(this.tabBuilder(0, '探索', $r('app.media.ball'), 25, 0))

customContentTransition: 自定义内容切换时的动画效果。

参数:
from(当前标签页索引)。
to(目标标签页索引)。
返回值: TabContentAnimatedTransition对象。
示例:

private customContentTransition: (from: number, to: number) => TabContentAnimatedTransition = (from: number, to: number) => {
  let tabContentAnimatedTransition = {
    timeout: this.timeoutList[from],
    transition: (proxy: TabContentTransitionProxy) => {
      this.scaleList[from] = 1.0
      this.scaleList[to] = 0.5
      this.opacityList[from] = 1.0
      this.opacityList[to] = 0.5
      animateTo({
        duration: this.durationList[from],
        onFinish: () => {
          proxy.finishTransition()
        }
      }, () => {
        this.scaleList[from] = 0.5
        this.scaleList[to] = 1.0
        this.opacityList[from] = 0.5
        this.opacityList[to] = 1.0
      })
    }
  } as TabContentAnimatedTransition
  return tabContentAnimatedTransition
}

4.4 接口详细说明

interface itemType {
  text: string,
  backgroundColor: Color
}

4.5 类详细说明

class TabsController {
  setCurrentIndex(index: number): void;
}

4.6 动画配置详细说明

TabContentAnimatedTransition

interface TabContentAnimatedTransition {
  timeout: number,
  transition: (proxy: TabContentTransitionProxy) => void
}

TabContentTransitionProxy

interface TabContentTransitionProxy {
  finishTransition(): void;
}

5. 自定义配置

Tabs组件提供了丰富的自定义选项,使开发者可以根据应用需求调整标签栏的样式和动画效果。以下是具体的自定义配置说明:

5.1 自定义标签栏样式

通过tabBar方法,可以自定义每个标签项的样式,包括图标、文字和颜色。

5.1.1 tabBar 方法

tabBar方法用于定义标签栏的样式。该方法接受多个参数,用于配置标签项的外观。
TabBar样式:
可以通过tabBar方法自定义每个标签页的样式,包括图片、文字、颜色等。
动画效果:
如何通过customContentTransition方法实现自定义的动画效果,包括透明度变化、缩放等。

5.1.2 示例

@Builder tabBuilder(index: number, name: string, selectIcon: Resource, border_radius_left: number, border_radius_right: number) {
  Column() {
    Column() {
      Image(selectIcon)
        .width(28)
        .height(26)
        .fillColor(this.selectedIndex === index ? $r('app.color.font_color') : '#9C9C9C')
      Text(name)
        .fontSize(16)
        .fontColor(this.currentIndex === index ? $r('app.color.font_color') : "#9C9C9C")
    }.width('100%').height('80%')
  }.width('100%').height('80%')
  .backgroundColor($r('app.color.select_menu_background'))
  .borderRadius({topLeft: border_radius_left, topRight: border_radius_right, bottomLeft: border_radius_left, bottomRight: border_radius_right})
  .margin({left: border_radius_left, right: border_radius_right, bottom: '10%'})
}

5.1.3 使用示例

Tabs({ barPosition: BarPosition.End, index: this.currentIndex, controller: this.controller }) {
  TabContent() {
    Index().width('100%').height('100%')
  }.tabBar(this.tabBuilder(0, '探索', $r('app.media.ball'), 25, 0))

  TabContent() {
    Plan().width('100%').height('100%')
  }.tabBar(this.tabBuilder(1, '规划', $r('app.media.signSvg'), 0, 0))

  TabContent() {
    MyPage().width('100%').height('100%')
  }.tabBar(this.tabBuilder(2, '我的', $r('app.media.chat'), 0, 25))
}

5.2 自定义动画效果

通过customContentTransition方法,可以自定义标签页切换时的动画效果,如透明度变化、缩放等。

5.2.1 customContentTransition 方法

参数名 类型 描述
from number 当前标签页索引。
to number 目标标签页索引。

5.2.2 返回值

customContentTransition方法返回一个TabContentAnimatedTransition对象,该对象包含以下字段:

参数名 类型 描述
timeout number 动画开始前的延迟时间(单位:毫秒)。
transition (proxy: TabContentTransitionProxy) => void 动画过渡方法,用于定义动画逻辑。

5.2.3 示例

private customContentTransition: (from: number, to: number) => TabContentAnimatedTransition = (from: number, to: number) => {
  let tabContentAnimatedTransition = {
    timeout: this.timeoutList[from],
    transition: (proxy: TabContentTransitionProxy) => {
      this.scaleList[from] = 1.0
      this.scaleList[to] = 0.5
      this.opacityList[from] = 1.0
      this.opacityList[to] = 0.5
      animateTo({
        duration: this.durationList[from],
        onFinish: () => {
          proxy.finishTransition()
        }
      }, () => {
        this.scaleList[from] = 0.5
        this.scaleList[to] = 1.0
        this.opacityList[from] = 0.5
        this.opacityList[to] = 1.0
      })
    }
  } as TabContentAnimatedTransition
  return tabContentAnimatedTransition
}

5.2.4 使用示例

Tabs({ barPosition: BarPosition.End, index: this.currentIndex, controller: this.controller })
  .customContentTransition(this.customContentTransition)
  .animationMode(AnimationMode.NO_ANIMATION)
  .animationDuration(200)

5.3 自定义动画模式和持续时间

除了自定义动画效果,还可以通过animationMode和animationDuration属性来调整动画的模式和持续时间。

5.3.1 animationMode 属性

animationMode属性用于设置动画模式,可选值为AnimationMode.NO_ANIMATION和AnimationMode.BASIC_ANIMATION。

| animationMode | AnimationMode| AnimationMode.NO_ANIMATION | 动画模式,可选值为AnimationMode.NO_ANIMATION、AnimationMode.BASIC_ANIMATION。 |

5.3.2 animationDuration 属性

animationDuration属性用于设置动画的持续时间(单位:毫秒)。
| animationDuration | number | 200 | 动画的持续时间(单位:毫秒)。 |

5.3.3 示例

Tabs({ barPosition: BarPosition.End, index: this.currentIndex, controller: this.controller })
  .customContentTransition(this.customContentTransition)
  .animationMode(AnimationMode.BASIC_ANIMATION)
  .animationDuration(300)

5.4 自定义标签栏颜色和背景

通过barBackgroundColor、fontColor和selectedFontColor属性,可以自定义标签栏的背景颜色、未选中标签的文字颜色和选中标签的文字颜色。

5.4.1 barBackgroundColor 属性

| barBackgroundColor| string | #FFFFFF | 标签栏的背景颜色。 |

5.4.2 fontColor 属性

fontColor属性用于设置未选中标签的文字颜色。
| fontColor | string | #182431 | 未选中标签的文字颜色。 |

5.4.3 selectedFontColor 属性

selectedFontColor属性用于设置选中标签的文字颜色。
| selectedFontColor | string | #007DFF | 选中标签的文字颜色。 |

5.4.4 示例

Tabs({ barPosition: BarPosition.End, index: this.currentIndex, controller: this.controller })
  .barBackgroundColor($r('app.color.background_color'))
  .fontColor('#182431')
  .selectedFontColor('#007DFF')

5.5 自定义标签栏布局

通过barWidth、barHeight、vertical和barMode属性,可以自定义标签栏的布局和样式。

5.5.1 barWidth 属性

barWidth属性用于设置标签栏的宽度。
| barWidth | string | “100%” | 标签栏的宽度。 |

5.5.2 barHeight 属性

| barHeight | number | 48 | 标签栏的高度。 |

5.5.3 vertical 属性
vertical属性用于设置标签栏是否垂直排列。
| vertical | boolean | false | 是否垂直排列标签页,默认为false,即水平排列。 |

5.5.4 barMode 属性

| barMode | BarMode | BarMode.Fixed | 标签栏的模式,可选值为BarMode.Fixed、BarMode.Scrollable。 |

5.5.5 示例

Tabs({ barPosition: BarPosition.End, index: this.currentIndex, controller: this.controller })
  .barWidth("88%")
  .barHeight(56)
  .vertical(false)
  .barMode(BarMode.Fixed)

5.6 事件监听

Tabs组件提供了onChange和onAnimationStart事件,方便开发者在标签页切换或动画开始时执行自定义逻辑。
onChange事件在标签页切换时触发。
onAnimationStart事件在标签页动画开始时触发。

5.6.3 示例

Tabs({ barPosition: BarPosition.End, index: this.currentIndex, controller: this.controller })
  .onChange((index: number) => {
    this.currentIndex = index;
    this.selectedIndex = index;
  })
  .onAnimationStart((index: number, targetIndex: number, event: TabsAnimationEvent) => {
    if (index === targetIndex) {
      return;
    }
    this.selectedIndex = targetIndex;
  })

6. 注意事项

使用Tabs组件时,需要注意以下事项,以确保组件正常工作并提供良好的用户体验。

6.1 索引范围

描述: index和selectedIndex属性的值应该在有效范围内(即0到标签页数量-1)。
示例: 如果有3个标签页,index和selectedIndex的值应该在0到2之间。

6.2 资源加载

描述: 确保所有图标资源(selectIcon)已经正确加载,否则将导致显示问题。
示例: 使用$r(‘app.media.ball’)确保资源路径正确。

6.3 动画性能

描述: 自定义动画效果可能会影响性能,特别是在动画逻辑较为复杂的情况下。
建议: 尽量保持动画逻辑简单,并测试不同设备上的性能表现。

6.4 进行触发事件处理

描述: 正确处理onChange和onAnimationStart事件,避免不必要的逻辑执行。
示例:

  .onChange((index: number) => {
    this.currentIndex = index;
    this.selectedIndex = index;
  })
  .onAnimationStart((index: number, targetIndex: number, event: TabsAnimationEvent) => {
    if (index === targetIndex) {
      return;
    }
    this.selectedIndex = targetIndex;
  })
  

6.5 样式一致性

描述: 确保所有标签页的样式(如字体颜色、背景颜色等)保持一致,以提供统一的视觉效果。
示例:

  .onChange((index: number) => {
    this.currentIndex = index;
    this.selectedIndex = index;
  })
  .onAnimationStart((index: number, targetIndex: number, event: TabsAnimationEvent) => {
    if (index === targetIndex) {
      return;
    }
    this.selectedIndex = targetIndex;
  })
  

7. 参考资料

官方文档Tabs组件

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
已于2024-12-26 10:00:15修改
收藏 2
回复
举报
1条回复
按时间正序
/
按时间倒序
在敲键盘的小鱼干很饥饿
在敲键盘的小鱼干很饥饿

这也太详细了吧


回复
2024-12-26 10:33:10
回复
    相关推荐