1# Tabs 2 3通过页签进行内容视图切换的容器组件,每个页签对应一个内容视图。 4 5> **说明:** 6> 7> 该组件从API Version 7开始支持。后续版本如有新增内容,则采用上角标单独标记该内容的起始版本。 8> 9> 该组件从API Version 11开始默认支持安全区避让特性(默认值为:expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.BOTTOM])),开发者可以重写该属性覆盖默认行为,API Version 11之前的版本需配合[expandSafeArea](ts-universal-attributes-expand-safe-area.md)属性实现安全区避让。 10 11 12## 子组件 13 14不支持自定义组件作为子组件, 仅可包含子组件[TabContent](ts-container-tabcontent.md), 以及渲染控制类型[if/else](../../../quick-start/arkts-rendering-control-ifelse.md)和[ForEach](../../../quick-start/arkts-rendering-control-foreach.md), 并且if/else和ForEach下也仅支持TabContent, 不支持自定义组件。 15 16> **说明:** 17> 18> Tabs子组件的visibility属性设置为None,或者visibility属性设置为Hidden时,对应子组件不显示,但依然会在视窗内占位。 19> 20> Tabs子组件TabContent显示之后不会销毁,若需要页面懒加载和释放,可以参考[示例12](#示例12页面懒加载和释放)。 21 22 23## 接口 24 25Tabs(value?: {barPosition?: BarPosition, index?: number, controller?: TabsController}) 26 27**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。 28 29**系统能力:** SystemCapability.ArkUI.ArkUI.Full 30 31**参数:** 32 33| 参数名 | 类型 | 必填 | 说明 | 34| ----------- | --------------------------------- | ---- | ---------------------------------------- | 35| barPosition | [BarPosition](#barposition枚举说明)| 否 | 设置Tabs的页签位置。<br/>默认值:BarPosition.Start | 36| index | number | 否 | 设置当前显示页签的索引。<br/>默认值:0<br/>**说明:** <br/>设置为小于0的值时按默认值显示。<br/>可选值为[0, TabContent子节点数量-1]。<br/>直接修改index跳页时,切换动效不生效。 使用TabController的changeIndex时,默认生效切换动效,可以设置animationDuration为0关闭动画。<br />从API version 10开始,该参数支持[$$](../../../quick-start/arkts-two-way-sync.md)双向绑定变量。<br/>Tabs重建、系统资源切换(如系统字体切换、系统深浅色切换)或者组件属性变化时,会跳转到index对应的页面。若需要在上述情况下不跳转,建议使用双向绑定。 | 37| controller | [TabsController](#tabscontroller) | 否 | 设置Tabs控制器。 | 38 39## BarPosition枚举说明 40 41Tabs页签位置枚举。 42 43**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。 44 45**系统能力:** SystemCapability.ArkUI.ArkUI.Full 46 47| 名称 | 说明 | 48| ----- | ------------------------------------------------------------ | 49| Start | vertical属性方法设置为true时,页签位于容器左侧;vertical属性方法设置为false时,页签位于容器顶部。 | 50| End | vertical属性方法设置为true时,页签位于容器右侧;vertical属性方法设置为false时,页签位于容器底部。 | 51 52 53## 属性 54 55除支持[通用属性](ts-universal-attributes-size.md)外,还支持以下属性: 56 57### vertical 58 59vertical(value: boolean) 60 61设置是否为纵向Tab。 62 63**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。 64 65**系统能力:** SystemCapability.ArkUI.ArkUI.Full 66 67**参数:** 68 69| 参数名 | 类型 | 必填 | 说明 | 70| ------ | ------- | ---- | ------------------------------------------------------------ | 71| value | boolean | 是 | 是否为纵向Tab。<br/>默认值:false,横向Tabs,为true时纵向Tabs。<br/>当横向Tabs设置height为auto时,Tabs组件高度自适应子组件高度,即为tabBar高度+divider线宽+TabContent高度+上下padding值+上下border宽度。<br/>当纵向Tabs设置width为auto时,Tabs组件宽度自适应子组件宽度,即为tabBar宽度+divider线宽+TabContent宽度+左右padding值+左右border宽度。<br/>尽量保持每一个页面中的子组件尺寸大小一致,避免滑动页面时出现页面切换动画跳动现象。 | 72 73### scrollable 74 75scrollable(value: boolean) 76 77设置是否可以通过滑动页面进行页面切换。 78 79**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。 80 81**系统能力:** SystemCapability.ArkUI.ArkUI.Full 82 83**参数:** 84 85| 参数名 | 类型 | 必填 | 说明 | 86| ------ | ------- | ---- | ------------------------------------------------------------ | 87| value | boolean | 是 | 是否可以通过滑动页面进行页面切换。<br/>默认值:true,可以通过滑动页面进行页面切换。为false时不可滑动切换页面。 | 88 89### barMode 90 91barMode(value: BarMode, options?: ScrollableBarModeOptions) 92 93设置TabBar布局模式。 94 95**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。 96 97**系统能力:** SystemCapability.ArkUI.ArkUI.Full 98 99**参数:** 100 101| 参数名 | 类型 | 必填 | 说明 | 102| --------------------- | ------------------------------------------------------------ | ---- | ------------------------------------------------------------ | 103| value | [BarMode](#barmode枚举说明) | 是 | 布局模式。<br/>默认值:BarMode.Fixed | 104| options<sup>10+</sup> | [ScrollableBarModeOptions](#scrollablebarmodeoptions10对象说明) | 否 | Scrollable模式下的TabBar的布局样式。<br/>**说明:** <br/>仅Scrollable且水平模式下有效。 | 105 106### barMode<sup>10+</sup> 107 108barMode(value: BarMode.Fixed) 109 110设置TabBar布局模式为BarMode.Fixed。 111 112**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。 113 114**系统能力:** SystemCapability.ArkUI.ArkUI.Full 115 116**参数:** 117 118| 参数名 | 类型 | 必填 | 说明 | 119| -------- | -------------------------------- | ---- | ------------------------------------ | 120| value | [BarMode.Fixed](#barmode枚举说明) | 是 | 所有TabBar会平均分配barWidth宽度(纵向时平均分配barHeight高度)。 | 121 122### barMode<sup>10+</sup> 123 124barMode(value: BarMode.Scrollable, options: ScrollableBarModeOptions) 125 126设置TabBar布局模式为BarMode.Scrollable。 127 128**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。 129 130**系统能力:** SystemCapability.ArkUI.ArkUI.Full 131 132**参数:** 133 134| 参数名 | 类型 | 必填 | 说明 | 135| -------- | --------------------------------- | ---- | ------------------------------------- | 136| value | [BarMode.Scrollable](#barmode枚举说明) | 是 | 所有TabBar都使用实际布局宽度,超过总宽度(横向Tabs的barWidth,纵向Tabs的barHeight)后可滑动。 | 137| options | [ScrollableBarModeOptions](#scrollablebarmodeoptions10对象说明) | 是 | Scrollable模式下的TabBar的布局样式。<br/>**说明:** <br/>仅水平模式下有效。 | 138 139### barWidth 140 141barWidth(value: Length) 142 143设置TabBar的宽度值。设置为小于0或大于Tabs宽度值时,按默认值显示。 144 145**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。 146 147**系统能力:** SystemCapability.ArkUI.ArkUI.Full 148 149**参数:** 150 151| 参数名 | 类型 | 必填 | 说明 | 152| ------ | ----------------------------------------- | ---- | ------------------------------------------------------------ | 153| value | [Length](ts-types.md#length)<sup>8+</sup> | 是 | TabBar的宽度值。<br/>默认值:<br/>未设置[SubTabBarStyle](ts-container-tabcontent.md#subtabbarstyle9)和[BottomTabBarStyle](ts-container-tabcontent.md#bottomtabbarstyle9)的TabBar且vertical属性为false时,默认值为Tabs的宽度。<br/>未设置SubTabBarStyle和BottomTabBarStyle的TabBar且vertical属性为true时,默认值为56vp。<br/>设置SubTabBarStyle样式且vertical属性为false时,默认值为Tabs的宽度。<br/>设置SubTabBarStyle样式且vertical属性为true时,默认值为56vp。<br/>设置BottomTabBarStyle样式且vertical属性为true时,默认值为96vp。<br/>设置BottomTabBarStyle样式且vertical属性为false时,默认值为Tabs的宽度。 | 154 155### barHeight 156 157barHeight(value: Length) 158 159设置TabBar的高度值。设置为'auto'时,TabBar自适应子组件高度,仅在水平模式下有效。设置为小于0或大于Tabs高度值时,按默认值显示。 160 161API Version 14之前的版本,若设置barHeight为固定值后,TabBar无法扩展底部安全区。从API Version 14开始支持配合[safeAreaPadding](./ts-universal-attributes-size.md#safeareapadding14)属性,当safeAreaPadding不设置bottom或者bottom设置为0时,可以实现扩展安全区。 162 163**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。 164 165**系统能力:** SystemCapability.ArkUI.ArkUI.Full 166 167**参数:** 168 169| 参数名 | 类型 | 必填 | 说明 | 170| ------ | ----------------------------------------- | ---- | ------------------------------------------------------------ | 171| value | [Length](ts-types.md#length)<sup>8+</sup> | 是 | TabBar的高度值。<br/>默认值:<br/>未设置带样式的TabBar且vertical属性为false时,默认值为56vp。<br/>未设置带样式的TabBar且vertical属性为true时,默认值为Tabs的高度。<br/>设置[SubTabBarStyle](ts-container-tabcontent.md#subtabbarstyle9)样式且vertical属性为false时,默认值为56vp。<br/>设置SubTabBarStyle样式且vertical属性为true时,默认值为Tabs的高度。<br/>设置[BottomTabBarStyle](ts-container-tabcontent.md#bottomtabbarstyle9)样式且vertical属性为true时,默认值为Tabs的高度。<br/>设置BottomTabBarStyle样式且vertical属性为false时,默认值为56vp, 从API Version 12开始,默认值变更为48vp。 | 172 173### animationDuration 174 175animationDuration(value: number) 176 177设置点击TabBar页签和调用TabsController的changeIndex接口切换TabContent的动画时长。该参数不支持百分比设置。 178 179**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。 180 181**系统能力:** SystemCapability.ArkUI.ArkUI.Full 182 183**参数:** 184 185| 参数名 | 类型 | 必填 | 说明 | 186| ------ | ------ | ---- | ------------------------------------------------------------ | 187| value | number | 是 | 点击TabBar页签和调用TabsController的changeIndex接口切换TabContent的动画时长。<br/>默认值:<br/>API version 10及以前,不设置该属性或设置为null时,默认值为0,即点击TabBar页签和调用TabsController的changeIndex接口切换TabContent无动画。设置为小于0或undefined时,默认值为300。<br/>API version 11及以后,不设置该属性或设置为异常值,且设置TabBar为BottomTabBarStyle样式时,默认值为0。设置TabBar为其他样式时,默认值为300。<br/>单位:ms | 188 189### animationMode<sup>12+</sup> 190 191animationMode(mode: Optional\<AnimationMode\>) 192 193设置点击TabBar页签时切换TabContent的动画形式。 194 195**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 196 197**系统能力:** SystemCapability.ArkUI.ArkUI.Full 198 199**参数:** 200 201| 参数名 | 类型 | 必填 | 说明 | 202| ------ | ------ | ---- | ------------------------------------------------------------ | 203| mode | Optional\<[AnimationMode](#animationmode12枚举说明)\> | 是 | 点击TabBar页签时切换TabContent的动画形式。<br/>默认值:<br/>默认值是AnimationMode.CONTENT_FIRST,表示在点击TabBar切换TabContent时,先加载目标页内容,再开始切换动画。| 204 205### barPosition<sup>9+</sup> 206 207barPosition(value: BarPosition) 208 209设置Tabs的页签位置。 210 211**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。 212 213**系统能力:** SystemCapability.ArkUI.ArkUI.Full 214 215**参数:** 216 217| 参数名 | 类型 | 必填 | 说明 | 218| ----- | ---------------------------------- | ---- | -------------------- | 219| value | [BarPosition](#barposition枚举说明)| 是 | 设置Tabs的页签位置。<br/>默认值:BarPosition.Start | 220 221### divider<sup>10+</sup> 222 223divider(value: DividerStyle | null) 224 225设置区分TabBar和TabContent的分割线样式。 226 227**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。 228 229**系统能力:** SystemCapability.ArkUI.ArkUI.Full 230 231**参数:** 232 233| 参数名 | 类型 | 必填 | 说明 | 234| ------ | --------------------------------------------------------- | ---- | ------------------------------------------------------------ | 235| value | [DividerStyle](#dividerstyle10对象说明) \| null | 是 | 分割线样式,默认不显示分割线。<br/>DividerStyle: 分割线的样式;<br/>null: 不显示分割线。 | 236 237### fadingEdge<sup>10+</sup> 238 239fadingEdge(value: boolean) 240 241设置页签超过容器宽度时是否渐隐消失。建议配合barBackgroundColor属性一起使用,如果barBackgroundColor属性没有定义,会默认显示页签末端为白色的渐隐效果。 242 243**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。 244 245**系统能力:** SystemCapability.ArkUI.ArkUI.Full 246 247**参数:** 248 249| 参数名 | 类型 | 必填 | 说明 | 250| ------ | ------- | ---- | -------------------------------------------------- | 251| value | boolean | 是 | 页签超过容器宽度时是否渐隐消失。<br />默认值:true | 252 253### barOverlap<sup>10+</sup> 254 255barOverlap(value: boolean) 256 257设置TabBar是否背后变模糊并叠加在TabContent之上。 258 259**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。 260 261**系统能力:** SystemCapability.ArkUI.ArkUI.Full 262 263**参数:** 264 265| 参数名 | 类型 | 必填 | 说明 | 266| ------ | ------- | ---- | ------------------------------------------------------------ | 267| value | boolean | 是 | TabBar是否背后变模糊并叠加在TabContent之上。当barOverlap设置为true时,TabBar默认模糊材质的BlurStyle值修改为'BlurStyle.COMPONENT_THICK'。<br />默认值:false | 268 269### barBackgroundColor<sup>10+</sup> 270 271barBackgroundColor(value: ResourceColor) 272 273设置TabBar的背景颜色。 274 275**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。 276 277**系统能力:** SystemCapability.ArkUI.ArkUI.Full 278 279**参数:** 280 281| 参数名 | 类型 | 必填 | 说明 | 282| ------ | ------------------------------------------ | ---- | ------------------------------------ | 283| value | [ResourceColor](ts-types.md#resourcecolor) | 是 | TabBar的背景颜色。<br />默认值:Color.Transparent,透明 | 284 285### barBackgroundBlurStyle<sup>11+</sup> 286 287barBackgroundBlurStyle(value: BlurStyle) 288 289设置TabBar的背景模糊材质。 290 291**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。 292 293**系统能力:** SystemCapability.ArkUI.ArkUI.Full 294 295**参数:** 296 297| 参数名 | 类型 | 必填 | 说明 | 298| ------ | -------------------------------------------- | ---- | ---------------------------------------- | 299| value | [BlurStyle](ts-universal-attributes-background.md#blurstyle9) | 是 | TabBar的背景模糊材质。<br />默认值:BlurStyle.NONE | 300 301### barGridAlign<sup>10+</sup> 302 303barGridAlign(value: BarGridColumnOptions) 304 305以栅格化方式设置TabBar的可见区域。具体参见BarGridColumnOptions对象。仅水平模式下有效,[不适用于XS、XL和XXL设备](../../../ui/arkts-layout-development-grid-layout.md#栅格系统断点)。 306 307**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。 308 309**系统能力:** SystemCapability.ArkUI.ArkUI.Full 310 311**参数:** 312 313| 参数名 | 类型 | 必填 | 说明 | 314| ------ | ------------------------------------------------------- | ---- | ---------------------------------- | 315| value | [BarGridColumnOptions](#bargridcolumnoptions10对象说明) | 是 | 以栅格化方式设置TabBar的可见区域。 | 316 317### edgeEffect<sup>12+</sup> 318 319edgeEffect(edgeEffect: Optional<EdgeEffect>) 320 321设置边缘回弹效果。 322 323**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 324 325**系统能力:** SystemCapability.ArkUI.ArkUI.Full 326 327**参数:** 328 329| 参数名 | 类型 | 必填 | 说明 | 330| ------ | --------------------------------------------- | ---- | -------------------------------------------- | 331| edgeEffect | Optional<[EdgeEffect](ts-appendix-enums.md#edgeeffect)> | 是 | 边缘滑动效果。<br/>默认值:EdgeEffect.Spring | 332 333## DividerStyle<sup>10+</sup>对象说明 334 335分割线样式对象。 336 337**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。 338 339**系统能力:** SystemCapability.ArkUI.ArkUI.Full 340 341| 名称 | 类型 | 必填 | 说明 | 342| ----------- | ---------------------------------------- | ---- | ---------------------------------------- | 343| strokeWidth | [Length](ts-types.md#length) | 是 | 分割线的线宽(不支持百分比设置)。<br/>默认值:0.0<br/>单位:vp | 344| color | [ResourceColor](ts-types.md#resourcecolor) | 否 | 分割线的颜色。<br/>默认值:#33182431 | 345| startMargin | [Length](ts-types.md#length) | 否 | 分割线与侧边栏顶端的距离(不支持百分比设置)。<br/>默认值:0.0<br/>单位:vp | 346| endMargin | [Length](ts-types.md#length) | 否 | 分割线与侧边栏底端的距离(不支持百分比设置)。<br/>默认值:0.0<br/>单位:vp | 347 348## BarGridColumnOptions<sup>10+</sup>对象说明 349 350TabBar栅格化方式设置的对象,包括栅格模式下的column边距和间隔,以及小、中、大屏下,页签占用的columns数量。 351 352**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。 353 354**系统能力:** SystemCapability.ArkUI.ArkUI.Full 355 356| 名称 | 类型 | 必填 | 说明 | 357| ----------- | ---------------------------------------- | ---- | ---------------------------------------- | 358| margin | [Dimension](ts-types.md#dimension10) | 否 | 栅格模式下的column边距(不支持百分比设置)。<br/>默认值:24.0<br/>单位:vp | 359| gutter | [Dimension](ts-types.md#dimension10) | 否 | 栅格模式下的column间隔(不支持百分比设置)。<br/>默认值:24.0<br/>单位:vp | 360| sm | number | 否 | 小屏下,页签占用的columns数量,必须是非负偶数。小屏为大于等于320vp但小于600vp。<br/>默认值为-1,代表页签占用TabBar全部宽度。 | 361| md | number | 否 | 中屏下,页签占用的columns数量,必须是非负偶数。中屏为大于等于600vp但小于800vp。<br/>默认值为-1,代表页签占用TabBar全部宽度。 | 362| lg | number | 否 | 大屏下,页签占用的columns数量,必须是非负偶数。大屏为大于等于840vp但小于1024vp。<br/>默认值为-1,代表页签占用TabBar全部宽度。 | 363 364## ScrollableBarModeOptions<sup>10+</sup>对象说明 365 366Scrollable模式下的TabBar的布局样式对象。 367 368**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。 369 370**系统能力:** SystemCapability.ArkUI.ArkUI.Full 371 372| 名称 | 类型 | 必填 | 说明 | 373| ----------- | ---------------------------------------- | ---- | ---------------------------------------- | 374| margin | [Dimension](ts-types.md#dimension10) | 否 | Scrollable模式下的TabBar的左右边距(不支持百分比设置)。<br/>默认值:0.0<br/>单位:vp | 375| nonScrollableLayoutStyle | [LayoutStyle](#layoutstyle10枚举说明) | 否 | Scrollable模式下不滚动时的页签排布方式。<br/>默认值:LayoutStyle.ALWAYS_CENTER | 376 377## BarMode枚举说明 378 379TabBar布局模式枚举。 380 381**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。 382 383**系统能力:** SystemCapability.ArkUI.ArkUI.Full 384 385| 名称 | 值 | 说明 | 386| ---------- | -- | ---------------------------------------- | 387| Scrollable | 0 | 每一个TabBar均使用实际布局宽度,超过总长度(横向Tabs的barWidth,纵向Tabs的barHeight)后可滑动。 | 388| Fixed | 1 | 所有TabBar平均分配barWidth宽度(纵向时平均分配barHeight高度)。 | 389 390## AnimationMode<sup>12+</sup>枚举说明 391 392点击TabBar页签时切换TabContent的动画形式枚举。 393 394**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 395 396**系统能力:** SystemCapability.ArkUI.ArkUI.Full 397 398| 名称 | 值 | 说明 | 399| ------------- | ---- | ------------------------------------------------------------ | 400| CONTENT_FIRST | 0 | 先加载目标页内容,再开始切换动画 | 401| ACTION_FIRST | 1 | 先开始切换动画,再加载目标页内容;生效需要同时需要满足:Tabs的height、width没有设置成auto | 402| NO_ANIMATION | 2 | 关闭默认动画 | 403 404## LayoutStyle<sup>10+</sup>枚举说明 405 406Scrollable模式下不滚动时的页签排布方式枚举。 407 408**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。 409 410**系统能力:** SystemCapability.ArkUI.ArkUI.Full 411 412| 名称 | 值 | 说明 | 413| ---------- | -- | ---------------------------------------- | 414| ALWAYS_CENTER | 0 | 当页签内容超过TabBar宽度时,TabBar可滚动。<br/>当页签内容不超过TabBar宽度时,TabBar不可滚动,页签紧凑居中。| 415| ALWAYS_AVERAGE_SPLIT | 1 | 当页签内容超过TabBar宽度时,TabBar可滚动。<br/>当页签内容不超过TabBar宽度时,TabBar不可滚动,且所有页签平均分配TabBar宽度。| 416| SPACE_BETWEEN_OR_CENTER | 2 | 当页签内容超过TabBar宽度时,TabBar可滚动。<br/>当页签内容不超过TabBar宽度但超过TabBar宽度一半时,TabBar不可滚动,页签紧凑居中。<br/>当页签内容不超过TabBar宽度一半时,TabBar不可滚动,保证页签居中排列在TabBar宽度一半,且间距相同。| 417 418## 事件 419 420除支持[通用事件](ts-universal-events-click.md)外,还支持以下事件: 421 422### onChange 423 424onChange(event: (index: number) => void) 425 426Tab页签切换后触发的事件。 427 428触发该事件的条件: 429 4301、滑动页面进行页面切换时,组件滑动动画结束后触发。 431 4322、通过[控制器](#tabscontroller)API接口调用。 433 4343、通过[状态变量](../../../quick-start/arkts-state.md)构造的属性值进行修改。 435 4364、通过页签处点击触发。 437 438> **说明:** 439> 440> 使用自定义页签时,在onChange事件中联动可能会导致滑动页面切换后才执行页签联动,引起自定义页签切换效果延迟。建议在[onAnimationStart](#onanimationstart11)中监听并刷新当前索引,以确保动效能够及时触发。具体实现可参考[示例1](#示例1自定义页签切换联动)。 441 442**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。 443 444**系统能力:** SystemCapability.ArkUI.ArkUI.Full 445 446**参数:** 447 448| 参数名 | 类型 | 必填 | 说明 | 449| ------ | ------ | ---- | -------------------------------------- | 450| index | number | 是 | 当前显示的index索引,索引从0开始计算。 | 451 452### onTabBarClick<sup>10+</sup> 453 454onTabBarClick(event: (index: number) => void) 455 456Tab页签点击后触发的事件。 457 458**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。 459 460**系统能力:** SystemCapability.ArkUI.ArkUI.Full 461 462**参数:** 463 464| 参数名 | 类型 | 必填 | 说明 | 465| ------ | ------ | ---- | ------------------------------------ | 466| index | number | 是 | 被点击的index索引,索引从0开始计算。 | 467 468### onAnimationStart<sup>11+</sup> 469 470onAnimationStart(handler: (index: number, targetIndex: number, event: TabsAnimationEvent) => void) 471 472切换动画开始时触发该回调。参数为动画开始前的index值(不是最终结束动画的index值)。当animationDuration为0时动画关闭,不触发该回调。 473 474**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 475 476**系统能力:** SystemCapability.ArkUI.ArkUI.Full 477 478**参数:** 479 480| 参数名 | 类型 | 必填 | 说明 | 481| ----------- | ------------------------------------------------------ | ---- | ------------------------------------------------------------ | 482| index | number | 是 | 当前显示元素的索引。 | 483| targetIndex | number | 是 | 切换动画目标元素的索引。 | 484| event | [TabsAnimationEvent](#tabsanimationevent11对象说明) | 是 | 动画相关信息,包括主轴方向上当前显示元素和目标元素相对Tabs起始位置的位移,以及离手速度。 | 485 486### onAnimationEnd<sup>11+</sup> 487 488onAnimationEnd(handler: (index: number, event: TabsAnimationEvent) => void) 489 490切换动画结束时触发该回调。当Tabs切换动效结束时触发,包括动画过程中手势中断。参数为动画结束后的index值。当animationDuration为0时动画关闭,不触发该回调。 491 492**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 493 494**系统能力:** SystemCapability.ArkUI.ArkUI.Full 495 496**参数:** 497 498| 参数名 | 类型 | 必填 | 说明 | 499| ------ | ------------------------------------------------------ | ---- | ------------------------------------------------------------ | 500| index | number | 是 | 当前显示元素的索引。 | 501| event | [TabsAnimationEvent](#tabsanimationevent11对象说明) | 是 | 动画相关信息,只返回主轴方向上当前显示元素相对于Tabs起始位置的位移。 | 502 503### onGestureSwipe<sup>11+</sup> 504 505onGestureSwipe(handler: (index: number, event: TabsAnimationEvent) => void) 506 507在页面跟手滑动过程中,逐帧触发该回调。 508 509**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 510 511**系统能力:** SystemCapability.ArkUI.ArkUI.Full 512 513**参数:** 514 515| 参数名 | 类型 | 必填 | 说明 | 516| ------ | ------------------------------------------------------ | ---- | ------------------------------------------------------------ | 517| index | number | 是 | 当前显示元素的索引。 | 518| event | [TabsAnimationEvent](#tabsanimationevent11对象说明) | 是 | 动画相关信息,只返回主轴方向上当前显示元素相对于Tabs起始位置的位移。 | 519 520### customContentTransition<sup>11+</sup> 521 522customContentTransition(delegate: (from: number, to: number) => TabContentAnimatedTransition \| undefined) 523 524自定义Tabs页面切换动画。 525 526使用说明: 527 5281、当使用自定义切换动画时,Tabs组件自带的默认切换动画会被禁用,同时,页面也无法跟手滑动。<br>2、当设置为undefined时,表示不使用自定义切换动画,仍然使用组件自带的默认切换动画。<br>3、当前自定义切换动画不支持打断。<br>4、目前自定义切换动画只支持两种场景触发:点击页签和调用TabsController.changeIndex()接口。<br>5、当使用自定义切换动画时,Tabs组件支持的事件中,除了onGestureSwipe,其他事件均支持。<br>6、onChange和onAnimationEnd事件的触发时机需要特殊说明:如果在第一次自定义动画执行过程中,触发了第二次自定义动画,那么在开始第二次自定义动画时,就会触发第一次自定义动画的onChange和onAnimationEnd事件。<br>7、当使用自定义动画时,参与动画的页面布局方式会改为Stack布局。如果开发者未主动设置相关页面的zIndex属性,那么所有页面的zIndex值是一样的,页面的渲染层级会按照在组件树上的顺序(即页面的index值顺序)确定。因此,开发者需要主动修改页面的zIndex属性,来控制页面的渲染层级。 529 530**卡片能力:** 从API version 11开始,该接口支持在ArkTS卡片中使用。 531 532**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 533 534**系统能力:** SystemCapability.ArkUI.ArkUI.Full 535 536**参数:** 537 538| 参数名 | 类型 | 必填 | 说明 | 539| ------ | ------ | ---- | ------------------------------- | 540| from | number | 是 | 动画开始时,当前页面的index值。 | 541| to | number | 是 | 动画开始时,目标页面的index值。 | 542 543**返回值:** 544 545| 类型 | 说明 | 546| ------------------------------------------------------------ | ------------------------ | 547| [TabContentAnimatedTransition](#tabcontentanimatedtransition11) \| undefined | 自定义切换动画相关信息。 | 548 549### onContentWillChange<sup>12+</sup> 550 551onContentWillChange(handler: (currentIndex: number, comingIndex: number) => boolean) 552 553自定义Tabs页面切换拦截事件能力,新页面即将显示时触发该回调。 554 555触发该回调的条件: 556 5571、TabContent支持滑动时,滑动组件切换新页面时触发。 558 5592、通过TabsController.changeIndex接口切换新页面时触发。 560 5613、通过动态修改index属性值切换新页面时触发。 562 5634、通过点击TabBar页签切换新页面时触发。 564 5655、TabBar页签获焦后,通过键盘左右方向键等切换新页面时触发。 566 567**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 568 569**系统能力:** SystemCapability.ArkUI.ArkUI.Full 570 571**参数:** 572 573| 参数名 | 类型 | 必填 | 说明 | 574| ------------ | ------ | ---- | ------------------------------------------ | 575| currentIndex | number | 是 | 当前显示页面的index索引,索引从0开始计算。 | 576| comingIndex | number | 是 | 将要显示的新页面的index索引。 | 577 578**返回值:** 579 580| 类型 | 说明 | 581| ------- | ------------------------------------------------------------ | 582| boolean | 当回调函数handler的返回值为true时,Tabs可以切换到新页面。<br/>当回调函数handler的返回值为false时,Tabs无法切换到新页面,仍然显示原来页面内容。 | 583 584## TabsAnimationEvent<sup>11+</sup>对象说明 585 586Tabs组件动画相关信息集合。 587 588**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 589 590**系统能力:** SystemCapability.ArkUI.ArkUI.Full 591 592| 名称 | 类型 | 只读 | 可选 | 说明 | 593| ------------- | ---------- | ---- | ---- | ------------------------ | 594| currentOffset | number | 否 | 否 | Tabs当前显示元素在主轴方向上,相对于Tabs起始位置的位移。单位VP,默认值为0。| 595| targetOffset | number | 否 | 否 | Tabs动画目标元素在主轴方向上,相对于Tabs起始位置的位移。单位VP,默认值为0。| 596| velocity | number | 否 | 否 | Tabs离手动画开始时的离手速度。单位VP/S,默认值为0。| 597 598## TabContentAnimatedTransition<sup>11+</sup> 599 600Tabs自定义切换动画相关信息。 601 602**卡片能力:** 从API version 11开始,该接口支持在ArkTS卡片中使用。 603 604**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 605 606**系统能力:** SystemCapability.ArkUI.ArkUI.Full 607 608| 名称 | 类型 | 必填 | 说明 | 609| ------------- | ---------------------- | ---- |---------------------- | 610| timeout | number | 否 | Tabs自定义切换动画超时时间。从自定义动画开始切换计时,如果到达该时间后,开发者仍未调用[TabContentTransitionProxy](#tabcontenttransitionproxy11)的finishTransition接口通知Tabs组件自定义动画结束,那么组件就会认为此次自定义动画已结束,直接执行后续操作。单位ms,默认值为1000.| 611| transition | [Callback](./ts-types.md#callback12)\<[TabContentTransitionProxy](#tabcontenttransitionproxy11)> | 是 | 自定义切换动画具体内容。| 612 613## TabContentTransitionProxy<sup>11+</sup> 614 615Tabs自定义切换动画执行过程中,返回给开发者的proxy对象。开发者可通过该对象获取自定义动画的起始和目标页面信息,同时,也可以通过调用该对象的finishTransition接口通知Tabs组件自定义动画已结束。 616 617**卡片能力:** 从API version 11开始,该接口支持在ArkTS卡片中使用。 618 619**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 620 621**系统能力:** SystemCapability.ArkUI.ArkUI.Full 622 623### 属性 624 625| 名称 | 类型 | 只读 | 可选 | 说明 | 626| ----- | ------- | ---- | ---- | --------------------------- | 627| from | number | 否 | 否 | 自定义动画起始页面对应的index值。| 628| to | number | 否 | 否 | 自定义动画目标页面对应的index值。| 629 630### finishTransition 631 632finishTransition(): void 633 634通知Tabs组件,此页面的自定义动画已结束。 635 636**卡片能力:** 从API version 11开始,该接口支持在ArkTS卡片中使用。 637 638**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 639 640**系统能力:** SystemCapability.ArkUI.ArkUI.Full 641 642## TabsController 643 644Tabs组件的控制器,用于控制Tabs组件进行页签切换。不支持一个TabsController控制多个Tabs组件。 645 646**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。 647 648**系统能力:** SystemCapability.ArkUI.ArkUI.Full 649 650### 导入对象 651 652```ts 653let controller: TabsController = new TabsController() 654``` 655 656### constructor 657 658constructor() 659 660TabsController的构造函数。 661 662**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。 663 664**系统能力:** SystemCapability.ArkUI.ArkUI.Full 665 666### changeIndex 667 668changeIndex(value: number): void 669 670控制Tabs切换到指定页签。 671 672**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。 673 674**系统能力:** SystemCapability.ArkUI.ArkUI.Full 675 676**参数:** 677 678| 参数名 | 类型 | 必填 | 说明 | 679| ----- | ------ | ---- | ---------------------------------------- | 680| value | number | 是 | 页签在Tabs里的索引值,索引值从0开始。<br/>**说明:** <br/>设置小于0或大于最大数量的值时,取默认值0。 | 681 682### preloadItems<sup>12+</sup> 683 684preloadItems(indices: Optional\<Array\<number>>): Promise\<void> 685 686控制Tabs预加载指定子节点。调用该接口后会一次性加载所有指定的子节点,因此为了性能考虑,建议分批加载子节点。 687 688**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 689 690**系统能力:** SystemCapability.ArkUI.ArkUI.Full 691 692**参数:** 693 694| 参数名 | 类型 | 必填 | 说明 | 695| ----- | ------ | ---- | ---------------------------------------- | 696| indices | Optional\<Array\<number>> | 是 | 需预加载的子节点的下标数组。<br/>默认值:空数组。 | 697 698**返回值:** 699 700| 类型 | 说明 | 701| ------------------------------------------------------------ | ------------------------ | 702| Promise\<void> | 预加载完成后触发的回调。 | 703 704**错误码:** 705 706以下错误码的详细介绍请参见[通用错误码](../../errorcode-universal.md)错误码。 707 708| 错误码ID | 错误信息 | 709| -------- | -------------------------------------------- | 710| 401 | Parameter invalid. Possible causes: 1. The parameter type is not Array\<number>; 2. The parameter is an empty array; 3. The parameter contains an invalid index. | 711 712### setTabBarTranslate<sup>13+</sup> 713 714setTabBarTranslate(translate: TranslateOptions): void 715 716设置TabBar的平移距离。 717 718> **说明:** 719> 720> 当使用bindTabsToScrollable或bindTabsToNestedScrollable等接口绑定了Tabs组件和可滚动容器组件后,在滑动可滚动容器组件时,会触发所有与其绑定的Tabs组件的TabBar的显示和隐藏动效,调用setTabBarTranslate接口设置的TabBar平移距离会失效。因此不建议同时使用bindTabsToScrollable、bindTabsToNestedScrollable和setTabBarTranslate接口。 721> 722 723**原子化服务API:** 从API version 13开始,该接口支持在原子化服务中使用。 724 725**系统能力:** SystemCapability.ArkUI.ArkUI.Full 726 727**参数:** 728 729| 参数名 | 类型 | 必填 | 说明 | 730| ----- | ------ | ---- | ---------------------------------------- | 731| translate | [TranslateOptions](ts-universal-attributes-transformation.md#translateoptions对象说明) | 是 | 设置TabBar的平移距离。 | 732 733### setTabBarOpacity<sup>13+</sup> 734 735setTabBarOpacity(opacity: number): void 736 737设置TabBar的不透明度。 738 739> **说明:** 740> 741> 当使用bindTabsToScrollable或bindTabsToNestedScrollable等接口绑定了Tabs组件和可滚动容器组件后,在滑动可滚动容器组件时,会触发所有与其绑定的Tabs组件的TabBar的显示和隐藏动效,调用setTabBarOpacity接口设置的TabBar不透明度会失效。因此不建议同时使用bindTabsToScrollable、bindTabsToNestedScrollable和setTabBarOpacity接口。 742> 743 744**原子化服务API:** 从API version 13开始,该接口支持在原子化服务中使用。 745 746**系统能力:** SystemCapability.ArkUI.ArkUI.Full 747 748**参数:** 749 750| 参数名 | 类型 | 必填 | 说明 | 751| ----- | ------ | ---- | ---------------------------------------- | 752| opacity | number | 是 | 设置TabBar的不透明度,取值范围为[0.0, 1.0]。 | 753 754## 示例 755 756### 示例1(自定义页签切换联动) 757 758本示例通过onAnimationStart、onChange实现切换时自定义tabBar和TabContent的联动。 759 760```ts 761// xxx.ets 762@Entry 763@Component 764struct TabsExample { 765 @State fontColor: string = '#182431' 766 @State selectedFontColor: string = '#007DFF' 767 @State currentIndex: number = 0 768 @State selectedIndex: number = 0 769 private controller: TabsController = new TabsController() 770 771 @Builder tabBuilder(index: number, name: string) { 772 Column() { 773 Text(name) 774 .fontColor(this.selectedIndex === index ? this.selectedFontColor : this.fontColor) 775 .fontSize(16) 776 .fontWeight(this.selectedIndex === index ? 500 : 400) 777 .lineHeight(22) 778 .margin({ top: 17, bottom: 7 }) 779 Divider() 780 .strokeWidth(2) 781 .color('#007DFF') 782 .opacity(this.selectedIndex === index ? 1 : 0) 783 }.width('100%') 784 } 785 786 build() { 787 Column() { 788 Tabs({ barPosition: BarPosition.Start, index: this.currentIndex, controller: this.controller }) { 789 TabContent() { 790 Column().width('100%').height('100%').backgroundColor('#00CB87') 791 }.tabBar(this.tabBuilder(0, 'green')) 792 793 TabContent() { 794 Column().width('100%').height('100%').backgroundColor('#007DFF') 795 }.tabBar(this.tabBuilder(1, 'blue')) 796 797 TabContent() { 798 Column().width('100%').height('100%').backgroundColor('#FFBF00') 799 }.tabBar(this.tabBuilder(2, 'yellow')) 800 801 TabContent() { 802 Column().width('100%').height('100%').backgroundColor('#E67C92') 803 }.tabBar(this.tabBuilder(3, 'pink')) 804 } 805 .vertical(false) 806 .barMode(BarMode.Fixed) 807 .barWidth(360) 808 .barHeight(56) 809 .animationDuration(400) 810 .onChange((index: number) => { 811 // currentIndex控制TabContent显示页签 812 this.currentIndex = index 813 this.selectedIndex = index 814 }) 815 .onAnimationStart((index: number, targetIndex: number, event: TabsAnimationEvent) => { 816 if (index === targetIndex) { 817 return 818 } 819 // selectedIndex控制自定义TabBar内Image和Text颜色切换 820 this.selectedIndex = targetIndex 821 }) 822 .width(360) 823 .height(296) 824 .margin({ top: 52 }) 825 .backgroundColor('#F1F3F5') 826 }.width('100%') 827 } 828} 829``` 830 831 832 833### 示例2(分割线基本属性) 834 835本示例通过divider实现了分割线各种属性的展示。 836 837```ts 838// xxx.ets 839@Entry 840@Component 841struct TabsDivider1 { 842 private controller1: TabsController = new TabsController() 843 @State dividerColor: string = 'red' 844 @State strokeWidth: number = 2 845 @State startMargin: number = 0 846 @State endMargin: number = 0 847 @State nullFlag: boolean = false 848 849 build() { 850 Column() { 851 Tabs({ controller: this.controller1 }) { 852 TabContent() { 853 Column().width('100%').height('100%').backgroundColor(Color.Pink) 854 }.tabBar('pink') 855 856 TabContent() { 857 Column().width('100%').height('100%').backgroundColor(Color.Yellow) 858 }.tabBar('yellow') 859 860 TabContent() { 861 Column().width('100%').height('100%').backgroundColor(Color.Blue) 862 }.tabBar('blue') 863 864 TabContent() { 865 Column().width('100%').height('100%').backgroundColor(Color.Green) 866 }.tabBar('green') 867 868 TabContent() { 869 Column().width('100%').height('100%').backgroundColor(Color.Red) 870 }.tabBar('red') 871 } 872 .vertical(true) 873 .scrollable(true) 874 .barMode(BarMode.Fixed) 875 .barWidth(70) 876 .barHeight(200) 877 .animationDuration(400) 878 .onChange((index: number) => { 879 console.info(index.toString()) 880 }) 881 .height('200vp') 882 .margin({ bottom: '12vp' }) 883 .divider(this.nullFlag ? null : { 884 strokeWidth: this.strokeWidth, 885 color: this.dividerColor, 886 startMargin: this.startMargin, 887 endMargin: this.endMargin 888 }) 889 890 Button('常规Divider').width('100%').margin({ bottom: '12vp' }) 891 .onClick(() => { 892 this.nullFlag = false; 893 this.strokeWidth = 2; 894 this.dividerColor = 'red'; 895 this.startMargin = 0; 896 this.endMargin = 0; 897 }) 898 Button('空Divider').width('100%').margin({ bottom: '12vp' }) 899 .onClick(() => { 900 this.nullFlag = true 901 }) 902 Button('颜色变为蓝色').width('100%').margin({ bottom: '12vp' }) 903 .onClick(() => { 904 this.dividerColor = 'blue' 905 }) 906 Button('宽度增加').width('100%').margin({ bottom: '12vp' }) 907 .onClick(() => { 908 this.strokeWidth += 2 909 }) 910 Button('宽度减小').width('100%').margin({ bottom: '12vp' }) 911 .onClick(() => { 912 if (this.strokeWidth > 2) { 913 this.strokeWidth -= 2 914 } 915 }) 916 Button('上边距增加').width('100%').margin({ bottom: '12vp' }) 917 .onClick(() => { 918 this.startMargin += 2 919 }) 920 Button('上边距减少').width('100%').margin({ bottom: '12vp' }) 921 .onClick(() => { 922 if (this.startMargin > 2) { 923 this.startMargin -= 2 924 } 925 }) 926 Button('下边距增加').width('100%').margin({ bottom: '12vp' }) 927 .onClick(() => { 928 this.endMargin += 2 929 }) 930 Button('下边距减少').width('100%').margin({ bottom: '12vp' }) 931 .onClick(() => { 932 if (this.endMargin > 2) { 933 this.endMargin -= 2 934 } 935 }) 936 }.padding({ top: '24vp', left: '24vp', right: '24vp' }) 937 } 938} 939``` 940 941 942 943### 示例3(设置TabBar渐隐) 944 945本示例通过fadingEdge实现了切换子页签渐隐和不渐隐。 946 947```ts 948// xxx.ets 949@Entry 950@Component 951struct TabsOpaque { 952 @State message: string = 'Hello World' 953 private controller: TabsController = new TabsController() 954 private controller1: TabsController = new TabsController() 955 @State selfFadingFade: boolean = true; 956 957 build() { 958 Column() { 959 Button('子页签设置渐隐').width('100%').margin({ bottom: '12vp' }) 960 .onClick((event?: ClickEvent) => { 961 this.selfFadingFade = true; 962 }) 963 Button('子页签设置不渐隐').width('100%').margin({ bottom: '12vp' }) 964 .onClick((event?: ClickEvent) => { 965 this.selfFadingFade = false; 966 }) 967 Tabs({ barPosition: BarPosition.End, controller: this.controller }) { 968 TabContent() { 969 Column().width('100%').height('100%').backgroundColor(Color.Pink) 970 }.tabBar('pink') 971 972 TabContent() { 973 Column().width('100%').height('100%').backgroundColor(Color.Yellow) 974 }.tabBar('yellow') 975 976 TabContent() { 977 Column().width('100%').height('100%').backgroundColor(Color.Blue) 978 }.tabBar('blue') 979 980 TabContent() { 981 Column().width('100%').height('100%').backgroundColor(Color.Green) 982 }.tabBar('green') 983 984 TabContent() { 985 Column().width('100%').height('100%').backgroundColor(Color.Green) 986 }.tabBar('green') 987 988 TabContent() { 989 Column().width('100%').height('100%').backgroundColor(Color.Green) 990 }.tabBar('green') 991 992 TabContent() { 993 Column().width('100%').height('100%').backgroundColor(Color.Green) 994 }.tabBar('green') 995 996 TabContent() { 997 Column().width('100%').height('100%').backgroundColor(Color.Green) 998 }.tabBar('green') 999 } 1000 .vertical(false) 1001 .scrollable(true) 1002 .barMode(BarMode.Scrollable) 1003 .barHeight(80) 1004 .animationDuration(400) 1005 .onChange((index: number) => { 1006 console.info(index.toString()) 1007 }) 1008 .fadingEdge(this.selfFadingFade) 1009 .height('30%') 1010 .width('100%') 1011 1012 Tabs({ barPosition: BarPosition.Start, controller: this.controller1 }) { 1013 TabContent() { 1014 Column().width('100%').height('100%').backgroundColor(Color.Pink) 1015 }.tabBar('pink') 1016 1017 TabContent() { 1018 Column().width('100%').height('100%').backgroundColor(Color.Yellow) 1019 }.tabBar('yellow') 1020 1021 TabContent() { 1022 Column().width('100%').height('100%').backgroundColor(Color.Blue) 1023 }.tabBar('blue') 1024 1025 TabContent() { 1026 Column().width('100%').height('100%').backgroundColor(Color.Green) 1027 }.tabBar('green') 1028 1029 TabContent() { 1030 Column().width('100%').height('100%').backgroundColor(Color.Green) 1031 }.tabBar('green') 1032 1033 TabContent() { 1034 Column().width('100%').height('100%').backgroundColor(Color.Green) 1035 }.tabBar('green') 1036 } 1037 .vertical(true) 1038 .scrollable(true) 1039 .barMode(BarMode.Scrollable) 1040 .barHeight(200) 1041 .barWidth(80) 1042 .animationDuration(400) 1043 .onChange((index: number) => { 1044 console.info(index.toString()) 1045 }) 1046 .fadingEdge(this.selfFadingFade) 1047 .height('30%') 1048 .width('100%') 1049 } 1050 .padding({ top: '24vp', left: '24vp', right: '24vp' }) 1051 } 1052} 1053``` 1054 1055 1056 1057### 示例4(设置TabBar叠加在TabContent内容上) 1058 1059本示例通过barOverlap实现了TabBar是否背后变模糊并叠加在TabContent之上。 1060 1061```ts 1062// xxx.ets 1063@Entry 1064@Component 1065struct barHeightTest { 1066 @State arr: number[] = [0, 1, 2, 3] 1067 @State barOverlap: boolean = true; 1068 build() { 1069 Column() { 1070 Text(`barOverlap ${this.barOverlap}`).fontSize(16) 1071 Button("barOverlap变化").width('100%').margin({ bottom: '12vp' }) 1072 .onClick((event?: ClickEvent) => { 1073 if (this.barOverlap) { 1074 this.barOverlap = false; 1075 } else { 1076 this.barOverlap = true; 1077 } 1078 }) 1079 1080 Tabs({ barPosition: BarPosition.End }) { 1081 TabContent() { 1082 Column() { 1083 List({ space: 10 }) { 1084 ForEach(this.arr, (item: number) => { 1085 ListItem() { 1086 Text("item" + item).width('80%').height(200).fontSize(16).textAlign(TextAlign.Center).backgroundColor('#fff8b81e') 1087 } 1088 }, (item: string) => item) 1089 }.width('100%').height('100%') 1090 .lanes(2).alignListItem(ListItemAlign.Center) 1091 }.width('100%').height('100%') 1092 .backgroundColor(Color.Pink) 1093 } 1094 .tabBar(new BottomTabBarStyle($r('sys.media.ohos_icon_mask_svg'), "测试0")) 1095 } 1096 .scrollable(false) 1097 .height('60%') 1098 .barOverlap(this.barOverlap) 1099 } 1100 .height(500) 1101 .padding({ top: '24vp', left: '24vp', right: '24vp' }) 1102 } 1103} 1104``` 1105 1106 1107 1108### 示例5(设置TabBar栅格化可见区域) 1109 1110本示例通过barGridAlign实现了以栅格化方式设置TabBar的可见区域。 1111 1112```ts 1113// xxx.ets 1114@Entry 1115@Component 1116struct TabsExample5 { 1117 private controller: TabsController = new TabsController() 1118 @State gridMargin: number = 10 1119 @State gridGutter: number = 10 1120 @State sm: number = -2 1121 @State clickedContent: string = ""; 1122 1123 build() { 1124 Column() { 1125 Row() { 1126 Button("gridMargin+10 " + this.gridMargin) 1127 .width('47%') 1128 .height(50) 1129 .margin({ top: 5 }) 1130 .onClick((event?: ClickEvent) => { 1131 this.gridMargin += 10 1132 }) 1133 .margin({ right: '6%', bottom: '12vp' }) 1134 Button("gridMargin-10 " + this.gridMargin) 1135 .width('47%') 1136 .height(50) 1137 .margin({ top: 5 }) 1138 .onClick((event?: ClickEvent) => { 1139 this.gridMargin -= 10 1140 }) 1141 .margin({ bottom: '12vp' }) 1142 } 1143 1144 Row() { 1145 Button("gridGutter+10 " + this.gridGutter) 1146 .width('47%') 1147 .height(50) 1148 .margin({ top: 5 }) 1149 .onClick((event?: ClickEvent) => { 1150 this.gridGutter += 10 1151 }) 1152 .margin({ right: '6%', bottom: '12vp' }) 1153 Button("gridGutter-10 " + this.gridGutter) 1154 .width('47%') 1155 .height(50) 1156 .margin({ top: 5 }) 1157 .onClick((event?: ClickEvent) => { 1158 this.gridGutter -= 10 1159 }) 1160 .margin({ bottom: '12vp' }) 1161 } 1162 1163 Row() { 1164 Button("sm+2 " + this.sm) 1165 .width('47%') 1166 .height(50) 1167 .margin({ top: 5 }) 1168 .onClick((event?: ClickEvent) => { 1169 this.sm += 2 1170 }) 1171 .margin({ right: '6%' }) 1172 Button("sm-2 " + this.sm).width('47%').height(50).margin({ top: 5 }) 1173 .onClick((event?: ClickEvent) => { 1174 this.sm -= 2 1175 }) 1176 } 1177 1178 Text("点击内容:" + this.clickedContent).width('100%').height(200).margin({ top: 5 }) 1179 1180 1181 Tabs({ barPosition: BarPosition.End, controller: this.controller }) { 1182 TabContent() { 1183 Column().width('100%').height('100%').backgroundColor(Color.Pink) 1184 }.tabBar(BottomTabBarStyle.of($r("sys.media.ohos_app_icon"), "1")) 1185 1186 TabContent() { 1187 Column().width('100%').height('100%').backgroundColor(Color.Green) 1188 }.tabBar(BottomTabBarStyle.of($r("sys.media.ohos_app_icon"), "2")) 1189 1190 TabContent() { 1191 Column().width('100%').height('100%').backgroundColor(Color.Blue) 1192 }.tabBar(BottomTabBarStyle.of($r("sys.media.ohos_app_icon"), "3")) 1193 } 1194 .width('350vp') 1195 .animationDuration(300) 1196 .height('60%') 1197 .barGridAlign({ sm: this.sm, margin: this.gridMargin, gutter: this.gridGutter }) 1198 .backgroundColor(0xf1f3f5) 1199 .onTabBarClick((index: number) => { 1200 this.clickedContent += "now index " + index + " is clicked\n"; 1201 }) 1202 } 1203 .width('100%') 1204 .height(500) 1205 .margin({ top: 5 }) 1206 .padding('10vp') 1207 } 1208} 1209``` 1210 1211 1212 1213### 示例6(设置Scrollable模式下的TabBar的布局样式) 1214 1215本示例实现了barMode的ScrollableBarModeOptions参数,该参数仅在Scrollable模式下有效。 1216 1217```ts 1218// xxx.ets 1219@Entry 1220@Component 1221struct TabsExample6 { 1222 private controller: TabsController = new TabsController() 1223 @State scrollMargin: number = 0 1224 @State layoutStyle: LayoutStyle = LayoutStyle.ALWAYS_CENTER 1225 @State text: string = "文本" 1226 1227 build() { 1228 Column() { 1229 Row() { 1230 Button("scrollMargin+10 " + this.scrollMargin) 1231 .width('47%') 1232 .height(50) 1233 .margin({ top: 5 }) 1234 .onClick((event?: ClickEvent) => { 1235 this.scrollMargin += 10 1236 }) 1237 .margin({ right: '6%', bottom: '12vp' }) 1238 Button("scrollMargin-10 " + this.scrollMargin) 1239 .width('47%') 1240 .height(50) 1241 .margin({ top: 5 }) 1242 .onClick((event?: ClickEvent) => { 1243 this.scrollMargin -= 10 1244 }) 1245 .margin({ bottom: '12vp' }) 1246 } 1247 1248 Row() { 1249 Button("文本增加 ") 1250 .width('47%') 1251 .height(50) 1252 .margin({ top: 5 }) 1253 .onClick((event?: ClickEvent) => { 1254 this.text += '文本增加' 1255 }) 1256 .margin({ right: '6%', bottom: '12vp' }) 1257 Button("文本重置") 1258 .width('47%') 1259 .height(50) 1260 .margin({ top: 5 }) 1261 .onClick((event?: ClickEvent) => { 1262 this.text = "文本" 1263 }) 1264 .margin({ bottom: '12vp' }) 1265 } 1266 1267 Row() { 1268 Button("layoutStyle.ALWAYS_CENTER") 1269 .width('100%') 1270 .height(50) 1271 .margin({ top: 5 }) 1272 .fontSize(15) 1273 .onClick((event?: ClickEvent) => { 1274 this.layoutStyle = LayoutStyle.ALWAYS_CENTER; 1275 }) 1276 .margin({ bottom: '12vp' }) 1277 } 1278 1279 Row() { 1280 Button("layoutStyle.ALWAYS_AVERAGE_SPLIT") 1281 .width('100%') 1282 .height(50) 1283 .margin({ top: 5 }) 1284 .fontSize(15) 1285 .onClick((event?: ClickEvent) => { 1286 this.layoutStyle = LayoutStyle.ALWAYS_AVERAGE_SPLIT; 1287 }) 1288 .margin({ bottom: '12vp' }) 1289 } 1290 1291 Row() { 1292 Button("layoutStyle.SPACE_BETWEEN_OR_CENTER") 1293 .width('100%') 1294 .height(50) 1295 .margin({ top: 5 }) 1296 .fontSize(15) 1297 .onClick((event?: ClickEvent) => { 1298 this.layoutStyle = LayoutStyle.SPACE_BETWEEN_OR_CENTER; 1299 }) 1300 .margin({ bottom: '12vp' }) 1301 } 1302 1303 Tabs({ barPosition: BarPosition.End, controller: this.controller }) { 1304 TabContent() { 1305 Column().width('100%').height('100%').backgroundColor(Color.Pink) 1306 }.tabBar(SubTabBarStyle.of(this.text)) 1307 1308 TabContent() { 1309 Column().width('100%').height('100%').backgroundColor(Color.Green) 1310 }.tabBar(SubTabBarStyle.of(this.text)) 1311 1312 TabContent() { 1313 Column().width('100%').height('100%').backgroundColor(Color.Blue) 1314 }.tabBar(SubTabBarStyle.of(this.text)) 1315 } 1316 .animationDuration(300) 1317 .height('60%') 1318 .backgroundColor(0xf1f3f5) 1319 .barMode(BarMode.Scrollable, { margin: this.scrollMargin, nonScrollableLayoutStyle: this.layoutStyle }) 1320 } 1321 .width('100%') 1322 .height(500) 1323 .margin({ top: 5 }) 1324 .padding('24vp') 1325 } 1326} 1327``` 1328 1329 1330 1331### 示例7(自定义Tabs页面切换动画) 1332 1333本示例通过customContentTransition实现了自定义Tabs页面的切换动画。 1334 1335```ts 1336// xxx.ets 1337interface itemType { 1338 text: string, 1339 backgroundColor: Color 1340} 1341 1342@Entry 1343@Component 1344struct TabsCustomAnimationExample { 1345 @State data: itemType[] = [ 1346 { 1347 text: 'Red', 1348 backgroundColor: Color.Red 1349 }, 1350 { 1351 text: 'Yellow', 1352 backgroundColor: Color.Yellow 1353 }, 1354 { 1355 text: 'Blue', 1356 backgroundColor: Color.Blue 1357 }] 1358 @State opacityList: number[] = [] 1359 @State scaleList: number[] = [] 1360 1361 private durationList: number[] = [] 1362 private timeoutList: number[] = [] 1363 private customContentTransition: (from: number, to: number) => TabContentAnimatedTransition = (from: number, to: number) => { 1364 let tabContentAnimatedTransition = { 1365 timeout: this.timeoutList[from], 1366 transition: (proxy: TabContentTransitionProxy) => { 1367 this.scaleList[from] = 1.0 1368 this.scaleList[to] = 0.5 1369 this.opacityList[from] = 1.0 1370 this.opacityList[to] = 0.5 1371 animateTo({ 1372 duration: this.durationList[from], 1373 onFinish: () => { 1374 proxy.finishTransition() 1375 } 1376 }, () => { 1377 this.scaleList[from] = 0.5 1378 this.scaleList[to] = 1.0 1379 this.opacityList[from] = 0.5 1380 this.opacityList[to] = 1.0 1381 }) 1382 } 1383 } as TabContentAnimatedTransition 1384 return tabContentAnimatedTransition 1385 } 1386 1387 aboutToAppear(): void { 1388 let duration = 1000 1389 let timeout = 1000 1390 for (let i = 1; i <= this.data.length; i++) { 1391 this.opacityList.push(1.0) 1392 this.scaleList.push(1.0) 1393 this.durationList.push(duration * i) 1394 this.timeoutList.push(timeout * i) 1395 } 1396 } 1397 1398 build() { 1399 Column() { 1400 Tabs() { 1401 ForEach(this.data, (item: itemType, index: number) => { 1402 TabContent() {} 1403 .tabBar(item.text) 1404 .backgroundColor(item.backgroundColor) 1405 // 自定义动画变化透明度、缩放页面等 1406 .opacity(this.opacityList[index]) 1407 .scale({ x: this.scaleList[index], y: this.scaleList[index] }) 1408 }) 1409 } 1410 .backgroundColor(0xf1f3f5) 1411 .width('100%') 1412 .height(500) 1413 .customContentTransition(this.customContentTransition) 1414 } 1415 } 1416} 1417``` 1418 1419 1420 1421### 示例8(页面切换拦截) 1422 1423本示例通过onContentWillChange实现了自定义页面手势滑动切换拦截。 1424 1425```ts 1426//xxx.ets 1427@Entry 1428@Component 1429struct TabsExample { 1430 @State selectedIndex: number = 2 1431 @State currentIndex: number = 2 1432 private controller: TabsController = new TabsController() 1433 @Builder tabBuilder(title: string,targetIndex: number) { 1434 Column(){ 1435 Text(title).fontColor(this.selectedIndex === targetIndex ? '#1698CE' : '#6B6B6B') 1436 }.width('100%') 1437 .height(50) 1438 .justifyContent(FlexAlign.Center) 1439 } 1440 build() { 1441 Column() { 1442 Tabs({ barPosition: BarPosition.End, index: this.currentIndex, controller: this.controller }) { 1443 TabContent() { 1444 Column(){ 1445 Text('首页的内容') 1446 }.width('100%').height('100%').backgroundColor('#00CB87').justifyContent(FlexAlign.Center) 1447 }.tabBar(this.tabBuilder('首页',0)) 1448 1449 TabContent() { 1450 Column(){ 1451 Text('发现的内容') 1452 }.width('100%').height('100%').backgroundColor('#007DFF').justifyContent(FlexAlign.Center) 1453 }.tabBar(this.tabBuilder('发现',1)) 1454 1455 TabContent() { 1456 Column(){ 1457 Text('推荐的内容') 1458 }.width('100%').height('100%').backgroundColor('#FFBF00').justifyContent(FlexAlign.Center) 1459 }.tabBar(this.tabBuilder('推荐',2)) 1460 1461 TabContent() { 1462 Column(){ 1463 Text('我的内容') 1464 }.width('100%').height('100%').backgroundColor('#E67C92').justifyContent(FlexAlign.Center) 1465 }.tabBar(this.tabBuilder('我的',3)) 1466 } 1467 .vertical(false) 1468 .barMode(BarMode.Fixed) 1469 .barWidth(360) 1470 .barHeight(60) 1471 .animationDuration(0) 1472 .onChange((index: number) => { 1473 this.currentIndex = index 1474 this.selectedIndex = index 1475 }) 1476 .width(360) 1477 .height(600) 1478 .backgroundColor('#F1F3F5') 1479 .scrollable(true) 1480 .onContentWillChange((currentIndex, comingIndex) => { 1481 if (comingIndex == 2) { 1482 return false 1483 } 1484 return true 1485 }) 1486 1487 Button('动态修改index').width('50%').margin({ top: 20 }) 1488 .onClick(()=>{ 1489 this.currentIndex = (this.currentIndex + 1) % 4 1490 }) 1491 1492 Button('changeIndex').width('50%').margin({ top: 20 }) 1493 .onClick(()=>{ 1494 this.currentIndex = (this.currentIndex + 1) % 4 1495 this.controller.changeIndex(this.currentIndex) 1496 }) 1497 }.width('100%') 1498 } 1499} 1500``` 1501 1502 1503 1504### 示例9(自定义TabBar切换动画) 1505 1506本示例通过onChange、onAnimationStart、onAnimationEnd、onGestureSwipe等接口实现了自定义TabBar的切换动画。 1507 1508<!--code_no_check--> 1509 1510```ts 1511// EntryAbility.ets 1512import { Configuration, UIAbility } from '@kit.AbilityKit' 1513import { i18n } from '@kit.LocalizationKit' 1514import { CommonUtil } from '../common/CommonUtil' 1515 1516export default class EntryAbility extends UIAbility { 1517 onConfigurationUpdate(newConfig: Configuration): void { 1518 // 监听系统配置变化 1519 if (newConfig.language) { 1520 CommonUtil.setIsRTL(i18n.isRTL(newConfig.language)) 1521 } 1522 } 1523} 1524``` 1525 1526<!--code_no_check--> 1527 1528```ts 1529// CommonUtil.ets 1530import { i18n, intl } from '@kit.LocalizationKit' 1531 1532export class CommonUtil { 1533 private static isRTL: boolean = i18n.isRTL((new intl.Locale()).language) 1534 1535 public static setIsRTL(isRTL: boolean): void { 1536 CommonUtil.isRTL = isRTL 1537 } 1538 1539 public static getIsRTL(): boolean { 1540 return CommonUtil.isRTL 1541 } 1542} 1543``` 1544 1545<!--code_no_check--> 1546 1547```ts 1548// xxx.ets 1549import { LengthMetrics } from '@kit.ArkUI' 1550import { CommonUtil } from '../common/CommonUtil' 1551 1552@Entry 1553@Component 1554struct TabsExample { 1555 @State colorArray: [string, string][] = 1556 [['green', '#00CB87'], ['blue', '#007DFF'], ['yellow', '#FFBF00'], ['pink', '#E67C92']] 1557 @State currentIndex: number = 0 1558 @State animationDuration: number = 300 1559 @State indicatorLeftMargin: number = 0 1560 @State indicatorWidth: number = 0 1561 private tabsWidth: number = 0 1562 private textInfos: [number, number][] = [] 1563 private isStartAnimateTo: boolean = false 1564 1565 aboutToAppear():void { 1566 for (let i = 0; i < this.colorArray.length; i++) { 1567 this.textInfos.push([0, 0]); 1568 } 1569 } 1570 1571 @Builder 1572 tabBuilder(index: number, name: string) { 1573 Column() { 1574 Text(name) 1575 .fontSize(16) 1576 .fontColor(this.currentIndex === index ? '#007DFF' : '#182431') 1577 .fontWeight(this.currentIndex === index ? 500 : 400) 1578 .id(index.toString()) 1579 .onAreaChange((oldValue: Area, newValue: Area) => { 1580 this.textInfos[index] = [newValue.globalPosition.x as number, newValue.width as number] 1581 if (!this.isStartAnimateTo && this.currentIndex === index && this.tabsWidth > 0) { 1582 this.setIndicatorAttr(this.textInfos[this.currentIndex][0], this.textInfos[this.currentIndex][1]) 1583 } 1584 }) 1585 }.width('100%') 1586 } 1587 1588 build() { 1589 Stack({ alignContent: Alignment.TopStart }) { 1590 Tabs({ barPosition: BarPosition.Start }) { 1591 ForEach(this.colorArray, (item: [string, string], index:number) => { 1592 TabContent() { 1593 Column().width('100%').height('100%').backgroundColor(item[1]) 1594 }.tabBar(this.tabBuilder(index, item[0])) 1595 }) 1596 } 1597 .onAreaChange((oldValue: Area, newValue: Area)=> { 1598 this.tabsWidth = newValue.width as number 1599 if (!this.isStartAnimateTo) { 1600 this.setIndicatorAttr(this.textInfos[this.currentIndex][0], this.textInfos[this.currentIndex][1]) 1601 } 1602 }) 1603 .barWidth('100%') 1604 .barHeight(56) 1605 .width('100%') 1606 .height(296) 1607 .backgroundColor('#F1F3F5') 1608 .animationDuration(this.animationDuration) 1609 .onChange((index: number) => { 1610 this.currentIndex = index // 监听索引index的变化,实现页签内容的切换。 1611 }) 1612 .onAnimationStart((index: number, targetIndex: number, event: TabsAnimationEvent) => { 1613 // 切换动画开始时触发该回调。下划线跟着页面一起滑动,同时宽度渐变。 1614 this.currentIndex = targetIndex 1615 this.startAnimateTo(this.animationDuration, this.textInfos[targetIndex][0], this.textInfos[targetIndex][1]) 1616 }) 1617 .onAnimationEnd((index: number, event: TabsAnimationEvent) => { 1618 // 切换动画结束时触发该回调。下划线动画停止。 1619 let currentIndicatorInfo = this.getCurrentIndicatorInfo(index, event) 1620 this.startAnimateTo(0, currentIndicatorInfo.left, currentIndicatorInfo.width) 1621 }) 1622 .onGestureSwipe((index: number, event: TabsAnimationEvent) => { 1623 // 在页面跟手滑动过程中,逐帧触发该回调。 1624 let currentIndicatorInfo = this.getCurrentIndicatorInfo(index, event) 1625 this.currentIndex = currentIndicatorInfo.index 1626 this.setIndicatorAttr(currentIndicatorInfo.left, currentIndicatorInfo.width) 1627 }) 1628 1629 Column() 1630 .height(2) 1631 .width(this.indicatorWidth) 1632 .margin({ start: LengthMetrics.vp(this.indicatorLeftMargin), top: LengthMetrics.vp(48) }) 1633 .backgroundColor('#007DFF') 1634 }.width('100%') 1635 } 1636 1637 private getCurrentIndicatorInfo(index: number, event: TabsAnimationEvent): Record<string, number> { 1638 let nextIndex = index 1639 if (index > 0 && (CommonUtil.getIsRTL() ? event.currentOffset < 0 : event.currentOffset > 0)) { 1640 nextIndex-- 1641 } else if (index < this.textInfos.length - 1 && 1642 (CommonUtil.getIsRTL() ? event.currentOffset > 0 : event.currentOffset < 0)) { 1643 nextIndex++ 1644 } 1645 let indexInfo = this.textInfos[index] 1646 let nextIndexInfo = this.textInfos[nextIndex] 1647 let swipeRatio = Math.abs(event.currentOffset / this.tabsWidth) 1648 let currentIndex = swipeRatio > 0.5 ? nextIndex : index // 页面滑动超过一半,tabBar切换到下一页。 1649 let currentLeft = indexInfo[0] + (nextIndexInfo[0] - indexInfo[0]) * swipeRatio 1650 let currentWidth = indexInfo[1] + (nextIndexInfo[1] - indexInfo[1]) * swipeRatio 1651 return { 'index': currentIndex, 'left': currentLeft, 'width': currentWidth } 1652 } 1653 1654 private startAnimateTo(duration: number, leftMargin: number, width: number) { 1655 this.isStartAnimateTo = true 1656 animateTo({ 1657 duration: duration, // 动画时长 1658 curve: Curve.Linear, // 动画曲线 1659 iterations: 1, // 播放次数 1660 playMode: PlayMode.Normal, // 动画模式 1661 onFinish: () => { 1662 this.isStartAnimateTo = false 1663 console.info('play end') 1664 } 1665 }, () => { 1666 this.setIndicatorAttr(leftMargin, width) 1667 }) 1668 } 1669 1670 private setIndicatorAttr(leftMargin: number, width: number) { 1671 this.indicatorWidth = width 1672 if (CommonUtil.getIsRTL()) { 1673 this.indicatorLeftMargin = this.tabsWidth - leftMargin - width 1674 } else { 1675 this.indicatorLeftMargin = leftMargin 1676 } 1677 } 1678} 1679``` 1680 1681 1682 1683### 示例10(预加载子节点) 1684 1685本示例通过preloadItems接口实现了预加载指定子节点。 1686 1687```ts 1688// xxx.ets 1689import { BusinessError } from '@kit.BasicServicesKit' 1690 1691@Entry 1692@Component 1693struct TabsPreloadItems { 1694 @State currentIndex: number = 1 1695 private tabsController: TabsController = new TabsController() 1696 1697 build() { 1698 Column() { 1699 Tabs({ index: this.currentIndex, controller: this.tabsController }) { 1700 TabContent() { 1701 MyComponent({ color: '#00CB87' }) 1702 }.tabBar(SubTabBarStyle.of('green')) 1703 1704 TabContent() { 1705 MyComponent({ color: '#007DFF' }) 1706 }.tabBar(SubTabBarStyle.of('blue')) 1707 1708 TabContent() { 1709 MyComponent({ color: '#FFBF00' }) 1710 }.tabBar(SubTabBarStyle.of('yellow')) 1711 1712 TabContent() { 1713 MyComponent({ color: '#E67C92' }) 1714 }.tabBar(SubTabBarStyle.of('pink')) 1715 } 1716 .width(360) 1717 .height(296) 1718 .backgroundColor('#F1F3F5') 1719 .onChange((index: number) => { 1720 this.currentIndex = index 1721 }) 1722 1723 Button('preload items: [0, 2, 3]') 1724 .margin(5) 1725 .onClick(() => { 1726 // 预加载第0、2、3个子节点,提高滑动或点击切换至这些节点时的性能 1727 this.tabsController.preloadItems([0, 2, 3]) 1728 .then(() => { 1729 console.info('preloadItems success.') 1730 }) 1731 .catch((error: BusinessError) => { 1732 console.error('preloadItems failed, error code: ' + error.code + ', error message: ' + error.message) 1733 }) 1734 }) 1735 } 1736 } 1737} 1738 1739@Component 1740struct MyComponent { 1741 private color: string = "" 1742 1743 aboutToAppear(): void { 1744 console.info('aboutToAppear backgroundColor:' + this.color) 1745 } 1746 1747 aboutToDisappear(): void { 1748 console.info('aboutToDisappear backgroundColor:' + this.color) 1749 } 1750 1751 build() { 1752 Column() 1753 .width('100%') 1754 .height('100%') 1755 .backgroundColor(this.color) 1756 } 1757} 1758``` 1759 1760### 示例11(设置TabBar平移距离和不透明度) 1761 1762本示例通过setTabBarTranslate、setTabBarOpacity等接口设置了TabBar的平移距离和不透明度。 1763 1764```ts 1765// xxx.ets 1766@Entry 1767@Component 1768struct TabsExample { 1769 private controller: TabsController = new TabsController() 1770 1771 build() { 1772 Column() { 1773 Button('设置TabBar的平移距离').margin({ top: 20 }) 1774 .onClick(() => { 1775 this.controller.setTabBarTranslate({ x: -20, y: -20 }) 1776 }) 1777 1778 Button('设置TabBar的透明度').margin({ top: 20 }) 1779 .onClick(() => { 1780 this.controller.setTabBarOpacity(0.5) 1781 }) 1782 1783 Tabs({ barPosition: BarPosition.End, controller: this.controller }) { 1784 TabContent() { 1785 Column().width('100%').height('100%').backgroundColor('#00CB87') 1786 }.tabBar(BottomTabBarStyle.of($r('app.media.startIcon'), 'green')) 1787 1788 TabContent() { 1789 Column().width('100%').height('100%').backgroundColor('#007DFF') 1790 }.tabBar(BottomTabBarStyle.of($r('app.media.startIcon'), 'blue')) 1791 1792 TabContent() { 1793 Column().width('100%').height('100%').backgroundColor('#FFBF00') 1794 }.tabBar(BottomTabBarStyle.of($r('app.media.startIcon'), 'yellow')) 1795 1796 TabContent() { 1797 Column().width('100%').height('100%').backgroundColor('#E67C92') 1798 }.tabBar(BottomTabBarStyle.of($r('app.media.startIcon'), 'pink')) 1799 } 1800 .width(360) 1801 .height(296) 1802 .margin({ top: 20 }) 1803 .barBackgroundColor('#F1F3F5') 1804 } 1805 .width('100%') 1806 } 1807} 1808``` 1809 1810 1811 1812### 示例12(页面懒加载和释放) 1813 1814本示例通过使用自定义TabBar与Swiper配合LazyForEach实现页面懒加载和释放。 1815 1816```ts 1817// xxx.ets 1818class MyDataSource implements IDataSource { 1819 private list: number[] = [] 1820 1821 constructor(list: number[]) { 1822 this.list = list 1823 } 1824 1825 totalCount(): number { 1826 return this.list.length 1827 } 1828 1829 getData(index: number): number { 1830 return this.list[index] 1831 } 1832 1833 registerDataChangeListener(listener: DataChangeListener): void { 1834 } 1835 1836 unregisterDataChangeListener() { 1837 } 1838} 1839 1840@Entry 1841@Component 1842struct TabsSwiperExample { 1843 @State fontColor: string = '#182431' 1844 @State selectedFontColor: string = '#007DFF' 1845 @State currentIndex: number = 0 1846 private list: number[] = [] 1847 private tabsController: TabsController = new TabsController() 1848 private swiperController: SwiperController = new SwiperController() 1849 private swiperData: MyDataSource = new MyDataSource([]) 1850 1851 aboutToAppear(): void { 1852 for (let i = 0; i <= 9; i++) { 1853 this.list.push(i); 1854 } 1855 this.swiperData = new MyDataSource(this.list) 1856 } 1857 1858 @Builder tabBuilder(index: number, name: string) { 1859 Column() { 1860 Text(name) 1861 .fontColor(this.currentIndex === index ? this.selectedFontColor : this.fontColor) 1862 .fontSize(16) 1863 .fontWeight(this.currentIndex === index ? 500 : 400) 1864 .lineHeight(22) 1865 .margin({ top: 17, bottom: 7 }) 1866 Divider() 1867 .strokeWidth(2) 1868 .color('#007DFF') 1869 .opacity(this.currentIndex === index ? 1 : 0) 1870 }.width('20%') 1871 } 1872 1873 build() { 1874 Column() { 1875 Tabs({ barPosition: BarPosition.Start, controller: this.tabsController }) { 1876 ForEach(this.list, (item: number) => { 1877 TabContent().tabBar(this.tabBuilder(item, '页签 ' + this.list[item])) 1878 }) 1879 } 1880 .onTabBarClick((index: number) => { 1881 this.currentIndex = index 1882 this.swiperController.changeIndex(index, true) 1883 }) 1884 .barMode(BarMode.Scrollable) 1885 .backgroundColor('#F1F3F5') 1886 .height(56) 1887 .width('100%') 1888 1889 Swiper(this.swiperController) { 1890 LazyForEach(this.swiperData, (item: string) => { 1891 Text(item.toString()) 1892 .onAppear(()=>{ 1893 console.info('onAppear ' + item.toString()) 1894 }) 1895 .onDisAppear(()=>{ 1896 console.info('onDisAppear ' + item.toString()) 1897 }) 1898 .width('100%') 1899 .height('100%') 1900 .backgroundColor(0xAFEEEE) 1901 .textAlign(TextAlign.Center) 1902 .fontSize(30) 1903 }, (item: string) => item) 1904 } 1905 .loop(false) 1906 .onChange((index: number) => { 1907 this.currentIndex = index 1908 }) 1909 .onAnimationStart((index: number, targetIndex: number, extraInfo: SwiperAnimationEvent) => { 1910 this.currentIndex = targetIndex 1911 this.tabsController.changeIndex(targetIndex) 1912 }) 1913 } 1914 } 1915} 1916``` 1917 1918