1# Tabs 2 3The **Tabs** component is a container component that allows users to switch between content views through tabs. Each tab page corresponds to a content view. 4 5> **NOTE** 6> 7> This component is supported since API version 7. Updates will be marked with a superscript to indicate their earliest API version. 8> 9> Since API version 11, this component supports the safe area attribute by default, with the default attribute value being **expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.BOTTOM]))**. You can override this attribute to change the default behavior. In earlier versions, you need to use the [expandSafeArea](ts-universal-attributes-expand-safe-area.md) attribute to implement the safe area feature. 10 11 12## Child Components 13 14Custom components cannot be used as child components. Only the [TabContent](ts-container-tabcontent.md) child component is allowed, with support for [if/else](../../../quick-start/arkts-rendering-control-ifelse.md) and [ForEach](../../../quick-start/arkts-rendering-control-foreach.md) rendering control. In addition, the **if/else** and **ForEach** statements support **TabContent** components only, but not custom components. 15 16> **NOTE** 17> 18> If the child component has the **visibility** attribute set to **None** or **Hidden**, it is hidden but still takes up space in the layout. 19> 20> The **TabContent** child component is not destroyed once it is displayed. If you need to implement lazy loading and resource release of pages, see [Example 12](#example-12-implementing-lazy-loading-and-resource-release-of-pages). 21 22 23## APIs 24 25Tabs(value?: {barPosition?: BarPosition, index?: number, controller?: TabsController}) 26 27**Atomic service API**: This API can be used in atomic services since API version 14. 28 29**System capability**: SystemCapability.ArkUI.ArkUI.Full 30 31**Parameters** 32 33| Name | Type | Mandatory | Description | 34| ----------- | --------------------------------- | ---- | ---------------------------------------- | 35| barPosition | [BarPosition](#barposition)| No | Position of the **Tabs** component.<br>Default value: **BarPosition.Start** | 36| index | number | No | Index of the currently displayed tab.<br>Default value: **0**<br>**NOTE**<br>A value less than 0 evaluates to the default value.<br>The value ranges from 0 to the number of **TabContent** nodes minus 1.<br>When the tab is switched by changing the index, the tab switching animation does not take effect. When **changeIndex** of **TabController** is used for tab switching, the tab switching animation is enabled by default. You can disable the animation by setting **animationDuration** to **0**.<br>Since API version 10, this parameter supports two-way binding through [$$](../../../quick-start/arkts-two-way-sync.md).| 37| controller | [TabsController](#tabscontroller) | No | Tab controller. | 38 39## BarPosition 40 41Enumerates the positions of the **Tabs** component. 42 43**Atomic service API**: This API can be used in atomic services since API version 11. 44 45**System capability**: SystemCapability.ArkUI.ArkUI.Full 46 47| Name | Description | 48| ----- | ------------------------------------------------------------ | 49| Start | If the **vertical** attribute is set to **true**, the tab is on the left of the container. If the **vertical** attribute is set to **false**, the tab is on the top of the container.| 50| End | If the **vertical** attribute is set to **true**, the tab is on the right of the container. If the **vertical** attribute is set to **false**, the tab is at the bottom of the container.| 51 52 53## Attributes 54 55In addition to the [universal attributes](ts-universal-attributes-size.md), the following attributes are supported. 56 57### vertical 58 59vertical(value: boolean) 60 61Sets whether to use vertical tabs. 62 63**Atomic service API**: This API can be used in atomic services since API version 11. 64 65**System capability**: SystemCapability.ArkUI.ArkUI.Full 66 67**Parameters** 68 69| Name| Type | Mandatory| Description | 70| ------ | ------- | ---- | ------------------------------------------------------------ | 71| value | boolean | Yes | Whether to use vertical tabs.<br>The value **true** means to use vertical tabs, and **false** means to use horizontal tabs.<br>Default value: **false**<br>If set to have a height of **auto**, horizontal tabs auto-adapt the height to child components, which is calculated as follows: Tab bar height + Divider width + Tab content height + Top and bottom paddings + Top and bottom border widths.<br>If set to have a width of **auto**, vertical tabs auto-adapt the width to child components, which is calculated as follows: Tab bar width + Divider width + Tab content width + Left and right paddings + Left and right border widths.<br>To avoid animation jitter when switching between tabs, maintain a consistent size for child components on each tab.| 72 73### scrollable 74 75scrollable(value: boolean) 76 77Sets whether the tabs are scrollable. 78 79**Atomic service API**: This API can be used in atomic services since API version 11. 80 81**System capability**: SystemCapability.ArkUI.ArkUI.Full 82 83**Parameters** 84 85| Name| Type | Mandatory| Description | 86| ------ | ------- | ---- | ------------------------------------------------------------ | 87| value | boolean | Yes | Whether the tabs are scrollable.<br>**true** (default): The tabs are scrollable.<br> **false**: The tabs are not scrollable.| 88 89### barMode 90 91barMode(value: BarMode, options?: ScrollableBarModeOptions) 92 93Sets the tab bar layout mode. 94 95**Atomic service API**: This API can be used in atomic services since API version 11. 96 97**System capability**: SystemCapability.ArkUI.ArkUI.Full 98 99**Parameters** 100 101| Name | Type | Mandatory| Description | 102| --------------------- | ------------------------------------------------------------ | ---- | ------------------------------------------------------------ | 103| value | [BarMode](#barmode) | Yes | Layout mode.<br>Default value: **BarMode.Fixed** | 104| options<sup>10+</sup> | [ScrollableBarModeOptions](#scrollablebarmodeoptions10)| No | Layout style of the tab bar in scrollable mode.<br>**NOTE**<br>This parameter is effective only when the tab bar is in horizontal scrollable mode.| 105 106### barMode<sup>10+</sup> 107 108barMode(value: BarMode.Fixed) 109 110Sets the tab bar layout mode to **BarMode.Fixed**. 111 112**Atomic service API**: This API can be used in atomic services since API version 11. 113 114**System capability**: SystemCapability.ArkUI.ArkUI.Full 115 116**Parameters** 117 118| Name | Type | Mandatory| Description | 119| -------- | -------------------------------- | ---- | ------------------------------------ | 120| value | [BarMode.Fixed](#barmode)| Yes | The width of each tab is determined by equally dividing the number of tabs by the bar width (or bar height in the vertical layout). | 121 122### barMode<sup>10+</sup> 123 124barMode(value: BarMode.Scrollable, options: ScrollableBarModeOptions) 125 126Sets the tab bar layout mode to **BarMode.Scrollable**. 127 128**Atomic service API**: This API can be used in atomic services since API version 11. 129 130**System capability**: SystemCapability.ArkUI.ArkUI.Full 131 132**Parameters** 133 134| Name | Type | Mandatory| Description | 135| -------- | --------------------------------- | ---- | ------------------------------------- | 136| value | [BarMode.Scrollable](#barmode)| Yes | The width of each tab is determined by the actual layout. The tabs are scrollable in the following case: In horizontal layout, the total width exceeds the tab bar width; in vertical layout, the total height exceeds the tab bar height. | 137| options | [ScrollableBarModeOptions](#scrollablebarmodeoptions10)| Yes | Layout style of the tab bar in scrollable mode.<br>**NOTE**<br>This parameter is effective only when the tab bar is in scrollable mode. | 138 139### barWidth 140 141barWidth(value: Length) 142 143Sets the width of the tab bar. If the set value is less than 0 or greater than the width of the **Tabs** component, the default value is used. 144 145**Atomic service API**: This API can be used in atomic services since API version 11. 146 147**System capability**: SystemCapability.ArkUI.ArkUI.Full 148 149**Parameters** 150 151| Name| Type | Mandatory| Description | 152| ------ | ----------------------------------------- | ---- | ------------------------------------------------------------ | 153| value | [Length](ts-types.md#length)<sup>8+</sup> | Yes | Width of the tab bar.<br>Default value:<br>If neither [SubTabBarStyle](ts-container-tabcontent.md#subtabbarstyle9) nor [BottomTabBarStyle](ts-container-tabcontent.md#bottomtabbarstyle9) is set, and the **vertical** attribute is **false**, the default value is the width of the **Tabs** component.<br>If neither **SubTabBarStyle** nor **BottomTabBarStyle** is set, and the **vertical** attribute is **true**, the default value is 56 vp.<br>If **SubTabBarStyle** is set, and the **vertical** attribute is **false**, the default value is the width of the **Tabs** component.<br>If **SubTabBarStyle** is set, and the **vertical** attribute is **true**, the default value is 56 vp.<br>If **BottomTabBarStyle** is set, and the **vertical** attribute is **true**, the default value is 96 vp.<br>If **BottomTabBarStyle** is set, and the **vertical** attribute is **false**, the default value is the width of the **Tabs** component.| 154 155### barHeight 156 157barHeight(value: Length) 158 159Sets the height of the tab bar. If this attribute is set to **'auto'**, which takes effect only in horizontal mode, the tab bar adapts to the height of its child components. If the set value is less than 0 or greater than the height of the **Tabs** component, the default value is used. 160 161In versions earlier than API version 14, setting **barHeight** to a fixed value restricts the tab bar from extending beyond the bottom safe area. Since API version 14, the [safeAreaPadding](./ts-universal-attributes-size.md#safeareapadding14) attribute is supported. When **safeAreaPadding** is set to 0 or is not explicitly set, the tab bar is allowed to extend beyond the bottom safe area. 162 163**Atomic service API**: This API can be used in atomic services since API version 11. 164 165**System capability**: SystemCapability.ArkUI.ArkUI.Full 166 167**Parameters** 168 169| Name| Type | Mandatory| Description | 170| ------ | ----------------------------------------- | ---- | ------------------------------------------------------------ | 171| value | [Length](ts-types.md#length)<sup>8+</sup> | Yes | Height of the tab bar.<br>Default value:<br>If the tab bar has the **vertical** attribute set to **false** and does not have a style specified, the default value is 56 vp.<br>If the tab bar has the **vertical** attribute set to **true** and does not have a style specified, the default value is the height of the **Tabs** component.<br>If [SubTabBarStyle](ts-container-tabcontent.md#subtabbarstyle9) is set, and the **vertical** attribute is **false**, the default value is 56 vp.<br>If **SubTabBarStyle** is set, and the **vertical** attribute is **true**, the default value is the height of the **Tabs** component.<br>If [BottomTabBarStyle](ts-container-tabcontent.md#bottomtabbarstyle9) is set, and the **vertical** attribute is **true**, the default value is the height of the **Tabs** component.<br>If **BottomTabBarStyle** is set, and the **vertical** attribute is **false**, the default value is 56 vp in versions earlier than API version 12 and 48 vp since API version 12.| 172 173### animationDuration 174 175animationDuration(value: number) 176 177Sets the length of time required to complete the tab switching animation, which is initiated by clicking a specific tab or by calling the **changeIndex** API of **TabsController**. This parameter cannot be set in percentage. 178 179**Atomic service API**: This API can be used in atomic services since API version 11. 180 181**System capability**: SystemCapability.ArkUI.ArkUI.Full 182 183**Parameters** 184 185| Name| Type | Mandatory| Description | 186| ------ | ------ | ---- | ------------------------------------------------------------ | 187| value | number | Yes | Length of time required to complete the tab switching animation, which is initiated by clicking a specific tab or by calling the **changeIndex** API of **TabsController**.<br>The default value varies.<br>API version 10 and earlier versions: If this parameter is set to **null** or is not set, the default value **0** is used, which means that no tab switching animation is displayed when a specific tab is clicked or the **changeIndex** API of **TabsController** is called. If this parameter is set to **undefined** or a value less than 0, the default value **300** is used.<br>API version 11 and later versions: If this parameter is set to an invalid value or is not set, the default value is **0** when the tab bar is set to **BottomTabBarStyle** and **300** when the tab bar is set to any other style.<br>Unit: ms| 188 189### animationMode<sup>12+</sup> 190 191animationMode(mode: Optional\<AnimationMode\>) 192 193Sets the animation mode for switching between tabs. 194 195**Atomic service API**: This API can be used in atomic services since API version 12. 196 197**System capability**: SystemCapability.ArkUI.ArkUI.Full 198 199**Parameters** 200 201| Name| Type | Mandatory| Description | 202| ------ | ------ | ---- | ------------------------------------------------------------ | 203| mode | Optional\<[AnimationMode](#animationmode12)\>| Yes | Animation mode for switching between tabs.<br>Default value:<br>**AnimationMode.CONTENT_FIRST**, indicating that the content of the target page is loaded before the switching animation starts in the process of switching between tabs.| 204 205### barPosition<sup>9+</sup> 206 207barPosition(value: BarPosition) 208 209Position of the **Tabs** component. 210 211**Atomic service API**: This API can be used in atomic services since API version 11. 212 213**System capability**: SystemCapability.ArkUI.ArkUI.Full 214 215**Parameters** 216 217| Name| Type | Mandatory| Description | 218| ----- | ---------------------------------- | ---- | -------------------- | 219| value | [BarPosition](#barposition)| Yes | Position of the **Tabs** component.<br>Default value: **BarPosition.Start** | 220 221### divider<sup>10+</sup> 222 223divider(value: DividerStyle | null) 224 225Sets the divider style for the **TabBar** and **TabContent** components. 226 227**Atomic service API**: This API can be used in atomic services since API version 11. 228 229**System capability**: SystemCapability.ArkUI.ArkUI.Full 230 231**Parameters** 232 233| Name| Type | Mandatory| Description | 234| ------ | --------------------------------------------------------- | ---- | ------------------------------------------------------------ | 235| value | [DividerStyle](#dividerstyle10) \| null | Yes | Divider style. By default, the divider is not displayed.<br>**DividerStyle**: divider style.<br>**null**: The divider is not displayed.| 236 237### fadingEdge<sup>10+</sup> 238 239fadingEdge(value: boolean) 240 241Sets whether the tab fades out when it exceeds the container width. It is recommended that this attribute be used together with the **barBackgroundColor** attribute. If the **barBackgroundColor** attribute is not defined, the tab fades out in white when it exceeds the container width by default. 242 243**Atomic service API**: This API can be used in atomic services since API version 11. 244 245**System capability**: SystemCapability.ArkUI.ArkUI.Full 246 247**Parameters** 248 249| Name| Type | Mandatory| Description | 250| ------ | ------- | ---- | -------------------------------------------------- | 251| value | boolean | Yes | Whether the tab fades out when it exceeds the container width.<br>Default value: **true**| 252 253### barOverlap<sup>10+</sup> 254 255barOverlap(value: boolean) 256 257Sets whether the tab bar is superimposed on the **TabContent** component after having its background blurred. 258 259**Atomic service API**: This API can be used in atomic services since API version 11. 260 261**System capability**: SystemCapability.ArkUI.ArkUI.Full 262 263**Parameters** 264 265| Name| Type | Mandatory| Description | 266| ------ | ------- | ---- | ------------------------------------------------------------ | 267| value | boolean | Yes | Whether the tab bar is superimposed on the **TabContent** component after having its background blurred. When **barOverlap** is set to **true**, the default value of **BlurStyle** for the **TabBar** is automatically changed to **'BlurStyle.COMPONENT_THICK'**.<br>Default value: **false**| 268 269### barBackgroundColor<sup>10+</sup> 270 271barBackgroundColor(value: ResourceColor) 272 273Background color of the tab bar. 274 275**Atomic service API**: This API can be used in atomic services since API version 11. 276 277**System capability**: SystemCapability.ArkUI.ArkUI.Full 278 279**Parameters** 280 281| Name| Type | Mandatory| Description | 282| ------ | ------------------------------------------ | ---- | ------------------------------------ | 283| value | [ResourceColor](ts-types.md#resourcecolor) | Yes | Background color of the tab bar.<br>Default value: **Color.Transparent**| 284 285### barBackgroundBlurStyle<sup>11+</sup> 286 287barBackgroundBlurStyle(value: BlurStyle) 288 289Sets the background blur style of the tab bar. 290 291**Atomic service API**: This API can be used in atomic services since API version 11. 292 293**System capability**: SystemCapability.ArkUI.ArkUI.Full 294 295**Parameters** 296 297| Name| Type | Mandatory| Description | 298| ------ | -------------------------------------------- | ---- | ---------------------------------------- | 299| value | [BlurStyle](ts-universal-attributes-background.md#blurstyle9) | Yes | Background blur style of the tab bar.<br>Default value: **BlurStyle.NONE**| 300 301### barGridAlign<sup>10+</sup> 302 303barGridAlign(value: BarGridColumnOptions) 304 305Sets the visible area of the tab bar in grid mode. For details, see **BarGridColumnOptions**. This attribute is effective only in horizontal mode. It is not applicable to [XS, XL, and XXL devices](../../../ui/arkts-layout-development-grid-layout.md#grid-breakpoints). 306 307**Atomic service API**: This API can be used in atomic services since API version 11. 308 309**System capability**: SystemCapability.ArkUI.ArkUI.Full 310 311**Parameters** 312 313| Name| Type | Mandatory| Description | 314| ------ | ------------------------------------------------------- | ---- | ---------------------------------- | 315| value | [BarGridColumnOptions](#bargridcolumnoptions10) | Yes | Visible area of the tab bar in grid mode.| 316 317### edgeEffect<sup>12+</sup> 318 319edgeEffect(edgeEffect: Optional<EdgeEffect>) 320 321Sets the edge effect used when the boundary of the scrolling area is reached. 322 323**Atomic service API**: This API can be used in atomic services since API version 12. 324 325**System capability**: SystemCapability.ArkUI.ArkUI.Full 326 327**Parameters** 328 329| Name| Type | Mandatory| Description | 330| ------ | --------------------------------------------- | ---- | -------------------------------------------- | 331| edgeEffect | Optional<[EdgeEffect](ts-appendix-enums.md#edgeeffect)> | Yes | Effect used when the boundary of the scrolling area is reached.<br>Default value: **EdgeEffect.Spring**| 332 333## DividerStyle<sup>10+</sup> 334 335Describes the divider style. 336 337**Atomic service API**: This API can be used in atomic services since API version 11. 338 339**System capability**: SystemCapability.ArkUI.ArkUI.Full 340 341| Name | Type | Mandatory | Description | 342| ----------- | ---------------------------------------- | ---- | ---------------------------------------- | 343| strokeWidth | [Length](ts-types.md#length) | Yes | Width of the divider. It cannot be set in percentage.<br>Default value: **0.0**<br>Unit: vp | 344| color | [ResourceColor](ts-types.md#resourcecolor) | No | Color of the divider.<br>Default value: **#33182431** | 345| startMargin | [Length](ts-types.md#length) | No | Distance between the divider and the top of the sidebar. It cannot be set in percentage.<br>Default value: **0.0**<br>Unit: vp| 346| endMargin | [Length](ts-types.md#length) | No | Distance between the divider and the bottom of the sidebar. It cannot be set in percentage.<br>Default value: **0.0**<br>Unit: vp| 347 348## BarGridColumnOptions<sup>10+</sup> 349 350Implements a **BarGridColumnOptions** object for setting the visible area of the tab bar in grid mode, including the column margin and gutter, as well as the number of columns occupied by tabs under small, medium, and large screen sizes. 351 352**Atomic service API**: This API can be used in atomic services since API version 11. 353 354**System capability**: SystemCapability.ArkUI.ArkUI.Full 355 356| Name | Type | Mandatory | Description | 357| ----------- | ---------------------------------------- | ---- | ---------------------------------------- | 358| margin | [Dimension](ts-types.md#dimension10) | No | Column margin in grid mode. It cannot be set in percentage.<br>Default value: **24.0**<br>Unit: vp | 359| gutter | [Dimension](ts-types.md#dimension10) | No | Column gutter (that is, gap between columns) in grid mode. It cannot be set in percentage.<br>Default value: **24.0**<br>Unit: vp | 360| sm | number | No | Number of columns occupied by a tab on a screen whose width is greater than or equal to 320 vp but less than 600 vp.<br>The value must be a non-negative even number. The default value is **-1**, indicating that the tab takes up the entire width of the tab bar.| 361| md | number | No | Number of columns occupied by a tab on a screen whose width is greater than or equal to 600 vp but less than 800 vp.<br>The value must be a non-negative even number. The default value is **-1**, indicating that the tab takes up the entire width of the tab bar.| 362| lg | number | No | Number of columns occupied by a tab on a screen whose width is greater than or equal to 840 vp but less than 1024 vp.<br>The value must be a non-negative even number. The default value is **-1**, indicating that the tab takes up the entire width of the tab bar.| 363 364## ScrollableBarModeOptions<sup>10+</sup> 365 366Implements a **ScrollableBarModeOptions** object. 367 368**Atomic service API**: This API can be used in atomic services since API version 11. 369 370**System capability**: SystemCapability.ArkUI.ArkUI.Full 371 372| Name | Type | Mandatory | Description | 373| ----------- | ---------------------------------------- | ---- | ---------------------------------------- | 374| margin | [Dimension](ts-types.md#dimension10) | No | Left and right margin of the tab bar in scrollable mode. It cannot be set in percentage.<br>Default value: **0.0**<br>Unit: vp | 375| nonScrollableLayoutStyle | [LayoutStyle](#layoutstyle10) | No | Tab layout mode of the tab bar when not scrolling in scrollable mode.<br>Default value: **LayoutStyle.ALWAYS_CENTER** | 376 377## BarMode 378 379Enumerates layout modes of the tab bar. 380 381**Atomic service API**: This API can be used in atomic services since API version 11. 382 383**System capability**: SystemCapability.ArkUI.ArkUI.Full 384 385| Name | Value| Description | 386| ---------- | -- | ---------------------------------------- | 387| Scrollable | 0 | The width of each tab is determined by the actual layout. The tabs are scrollable in the following case: In horizontal layout, the total width exceeds the tab bar width; in vertical layout, the total height exceeds the tab bar height.| 388| Fixed | 1 | The width of each tab is determined by equally dividing the number of tabs by the bar width (or bar height in the vertical layout).| 389 390## AnimationMode<sup>12+</sup> 391 392Enumerates the animation modes for switching between tabs. 393 394**Atomic service API**: This API can be used in atomic services since API version 12. 395 396**System capability**: SystemCapability.ArkUI.ArkUI.Full 397 398| Name | Value | Description | 399| ------------- | ---- | ------------------------------------------------------------ | 400| CONTENT_FIRST | 0 | Load the content of the target page before starting the switching animation. | 401| ACTION_FIRST | 1 | Start the switching animation before loading the content of the target page. For the settings to take effect, the height and width of tabs must be set to **auto**.| 402| NO_ANIMATION | 2 | The switching animation is disabled. | 403 404## LayoutStyle<sup>10+</sup> 405 406Enumerates the tab layout styles of the tab bar when not scrolling in scrollable mode. 407 408**Atomic service API**: This API can be used in atomic services since API version 11. 409 410**System capability**: SystemCapability.ArkUI.ArkUI.Full 411 412| Name | Value| Description | 413| ---------- | -- | ---------------------------------------- | 414| ALWAYS_CENTER | 0 | If the tab content exceeds the tab bar width, the tabs are scrollable.<br>If not, the tabs are compactly centered on the tab bar and not scrollable.| 415| ALWAYS_AVERAGE_SPLIT | 1 | If the tab content exceeds the tab bar width, the tabs are scrollable.<br>If not, the tabs are not scrollable, and the width of the tab bar is evenly distributed among all tabs.| 416| SPACE_BETWEEN_OR_CENTER | 2 | If the tab content exceeds the tab bar width, the tabs are scrollable.<br>If the tab content exceeds half the width of the tab bar but is still within the tab bar width, the tabs are compactly centered and not scrollable.<br>If the tab content does not exceed half the width of the tab bar, the tabs are centered within half the width of the tab bar with even spacing between them and are not scrollable.| 417 418## Events 419 420In addition to the [universal events](ts-universal-events-click.md), the following events are supported. 421 422### onChange 423 424onChange(event: (index: number) => void) 425 426Triggered when a tab is switched. 427 428This event is triggered when any of the following conditions is met: 429 4301. The swiping animation is completed, followed by tab switching. 431 4322. The [Controller](#tabscontroller) API is called. 433 4343. The attribute value is updated using a [state variable](../../../quick-start/arkts-state.md). 435 4364. A tab is clicked. 437 438> **NOTE** 439> 440> When a custom tab is used, the synchronization between tabs and swipe gestures in the **onChange** event may be performed only after tab switching occurs. This can lead to a delay in switching to the custom tab. To address this issue, listen for the current tab index in [onAnimationStart](#onanimationstart11) and update the tab index accordingly. For details about the implementation, see [Example 1](#example-1-setting-up-custom-tab-switching-synchronization). 441 442**Atomic service API**: This API can be used in atomic services since API version 11. 443 444**System capability**: SystemCapability.ArkUI.ArkUI.Full 445 446**Parameters** 447 448| Name| Type | Mandatory| Description | 449| ------ | ------ | ---- | ------------------------------------ | 450| index | number | Yes | Index of the clicked tab. The index starts from 0.| 451 452### onTabBarClick<sup>10+</sup> 453 454onTabBarClick(event: (index: number) => void) 455 456Triggered when a tab is clicked. 457 458**Atomic service API**: This API can be used in atomic services since API version 11. 459 460**System capability**: SystemCapability.ArkUI.ArkUI.Full 461 462**Parameters** 463 464| Name| Type | Mandatory| Description | 465| ------ | ------ | ---- | ------------------------------------ | 466| event | [Callback](./ts-types.md#callback12)\<number> | Yes | Index of the clicked tab. The index starts from 0.| 467 468### onAnimationStart<sup>11+</sup> 469 470onAnimationStart(handler: OnTabsAnimationStartCallback) 471 472Triggered when the tab switching animation starts. This callback is not triggered when **animationDuration** is set to **0**, which effectively disables the animation. 473 474**Atomic service API**: This API can be used in atomic services since API version 12. 475 476**System capability**: SystemCapability.ArkUI.ArkUI.Full 477 478**Parameters** 479 480| Name | Type | Mandatory| Description | 481| ----------- | ------------------------------------------------------ | ---- | ------------------------------------------------------------ | 482| index | number | Yes | Index of the currently displayed element. | 483| targetIndex | number | Yes | Index of the target element to switch to. | 484| event | [TabsAnimationEvent](#tabsanimationevent11) | Yes | Extra information of the animation, including the offset of the currently displayed element and target element relative to the start position of the **Tabs** along the main axis, and the hands-off velocity.| 485 486### onAnimationEnd<sup>11+</sup> 487 488onAnimationEnd(handler: (index: number, event: TabsAnimationEvent) => void) 489 490Triggered when the tab switching animation ends. This event is triggered when the tab switching animation ends, whether it is caused by gesture interruption or not. This callback is not triggered when **animationDuration** is set to **0**, which effectively disables the animation. 491 492**Atomic service API**: This API can be used in atomic services since API version 12. 493 494**System capability**: SystemCapability.ArkUI.ArkUI.Full 495 496**Parameters** 497 498| Name| Type | Mandatory| Description | 499| ------ | ------------------------------------------------------ | ---- | ------------------------------------------------------------ | 500| index | number | Yes | Index of the currently displayed element. | 501| event | [TabsAnimationEvent](#tabsanimationevent11) | Yes | Extra information of the animation, which is the offset of the currently displayed element relative to the start position of the **Tabs** along the main axis.| 502 503### onGestureSwipe<sup>11+</sup> 504 505onGestureSwipe(handler: (index: number, event: TabsAnimationEvent) => void) 506 507Triggered on a frame-by-frame basis when the tab is switched by a swipe. 508 509**Atomic service API**: This API can be used in atomic services since API version 12. 510 511**System capability**: SystemCapability.ArkUI.ArkUI.Full 512 513**Parameters** 514 515| Name| Type | Mandatory| Description | 516| ------ | ------------------------------------------------------ | ---- | ------------------------------------------------------------ | 517| index | number | Yes | Index of the currently displayed element. | 518| event | [TabsAnimationEvent](#tabsanimationevent11) | Yes | Extra information of the animation, which is the offset of the currently displayed element relative to the start position of the **Tabs** along the main axis.| 519 520### customContentTransition<sup>11+</sup> 521 522customContentTransition(delegate: (from: number, to: number) => TabContentAnimatedTransition \| undefined) 523 524Sets the custom tab switching animation. 525 526Instructions: 527 5281. When the custom tab switching animation is used, the default switching animation of the **Tabs** component is disabled, and tabs cannot be switched through swiping.<br>2. The value **undefined** means not to use the custom tab switching animation, in which case the default switching animation is used.<br>3. The custom tab switching animation cannot be interrupted.<br>4. Currently, the custom tab switching animation can be triggered only by clicking a tab or by calling the **TabsController.changeIndex()** API.<br>5. When the custom tab switching animation is used, the **Tabs** component supports all events except **onGestureSwipe**.<br>6. Notes about the **onChange** and **onAnimationEnd** events: If the second custom animation is triggered during the execution of the first custom animation, the **onChange** and **onAnimationEnd** events of the first custom animation will be triggered when the second custom animation starts.<br>7. When the custom animation is used, the stack layout is used for pages involved in the animation. If the **zIndex** attribute is not set for related pages, the **zIndex** values of all pages are the same. In this case, the pages are rendered in the order in which they are added to the component tree (that is, the sequence of page indexes). In light of this, to control the rendering levels of pages, set the **zIndex** attribute of the pages. 529 530**Widget capability**: This API can be used in ArkTS widgets since API version 11. 531 532**Atomic service API**: This API can be used in atomic services since API version 12. 533 534**System capability**: SystemCapability.ArkUI.ArkUI.Full 535 536**Parameters** 537 538| Name| Type | Mandatory| Description | 539| ------ | ------ | ---- | ------------------------------- | 540| from | number | Yes | Index of the currently displayed tab before the animation starts.| 541| to | number | Yes | Index of the target tab before the animation starts.| 542 543**Return value** 544 545| Type | Description | 546| ------------------------------------------------------------ | ------------------------ | 547| [TabContentAnimatedTransition](#tabcontentanimatedtransition11) \| undefined | Information about the custom tab switching animation.| 548 549### onContentWillChange<sup>12+</sup> 550 551onContentWillChange(handler: (currentIndex: number, comingIndex: number) => boolean) 552 553Triggered when a new page is about to be displayed. 554 555Specifically, this event is triggered in the following cases: 556 5571. When the user swipes on the **TabContent** component (provided that it supports swiping) to switch to a new page. 558 5592. When **TabsController.changeIndex** is called to switch to a new page. 560 5613. When the **index** attribute is changed to switch to a new page. 562 5634. When the user clicks a tab on the tab bar to switch to a new page. 564 5655. When the user presses the left or right arrow key on the keyboard to switch to a new page while the tab bar is focused. 566 567**Atomic service API**: This API can be used in atomic services since API version 12. 568 569**System capability**: SystemCapability.ArkUI.ArkUI.Full 570 571**Parameters** 572 573| Name | Type | Mandatory| Description | 574| ------------ | ------ | ---- | ------------------------------------------ | 575| currentIndex | number | Yes | Index of the active tab. The index starts from 0.| 576| comingIndex | number | Yes | Index of the new tab to be displayed. | 577 578**Return value** 579 580| Type | Description | 581| ------- | ------------------------------------------------------------ | 582| boolean | The value **true** means that the tab can switch to the new page.<br>The value **false** means that the tab cannot switch to the new page and will remain on the current page.| 583 584## TabsAnimationEvent<sup>11+</sup> 585 586Describes the animation information of the **Tabs** component. 587 588**Atomic service API**: This API can be used in atomic services since API version 12. 589 590**System capability**: SystemCapability.ArkUI.ArkUI.Full 591 592| Name | Type | Read Only| Optional| Description | 593| ------------- | ---------- | ---- | ---- | ------------------------ | 594| currentOffset | number | No| No| Offset of the currently displayed element relative to the start position of the **Tabs** component along the main axis.<br> Unit: vp<br>Default value: **0**| 595| targetOffset | number | No| No| Offset of the target element relative to the start position of the **Tabs** component along the main axis.<br> Unit: vp<br>Default value: **0**| 596| velocity | number | No| No| Hands-off velocity at the beginning of the animation. Unit: VP/S<br>Default value: **0**| 597 598## TabContentAnimatedTransition<sup>11+</sup> 599 600Provides the information about the custom tab page switching animation. 601 602**Widget capability**: This API can be used in ArkTS widgets since API version 11. 603 604**Atomic service API**: This API can be used in atomic services since API version 12. 605 606**System capability**: SystemCapability.ArkUI.ArkUI.Full 607 608| Name | Type | Mandatory | Description | 609| ------------- | ---------------------- | ---- |---------------------- | 610| timeout | number | No| Timeout for the custom switching animation. The timer starts when the switching begins. If this timeframe passes without you calling the **finishTransition** API in [TabContentTransitionProxy](#tabcontenttransitionproxy11), the component will assume that the custom animation has ended and will proceed directly with subsequent operations. Unit: ms<br>Default value: **1000**| 611| transition | [Callback](./ts-types.md#callback12)\<[TabContentTransitionProxy](#tabcontenttransitionproxy11)> | Yes| Content of the custom switching animation.| 612 613## TabContentTransitionProxy<sup>11+</sup> 614 615Implements the proxy object returned during the execution of the custom switching animation of the **Tabs** component. You can use this object to obtain the start and target pages for the custom tab switching animation. In addition, you can call the **finishTransition** API of this object to notify the **Tabs** component of the ending of the custom animation. 616 617**Widget capability**: This API can be used in ArkTS widgets since API version 11. 618 619**Atomic service API**: This API can be used in atomic services since API version 12. 620 621**System capability**: SystemCapability.ArkUI.ArkUI.Full 622 623### Attributes 624 625| Name | Type | Read Only| Optional| Description | 626| ----- | ------- | ---- | ---- | --------------------------- | 627| from | number | No| No| Index of the starting page of the custom animation.| 628| to | number | No| No| Index of the target tab to switch to.| 629 630### finishTransition 631 632finishTransition(): void 633 634Notifies the **Tabs** component that the custom animation has finished playing. 635 636**Widget capability**: This API can be used in ArkTS widgets since API version 11. 637 638**Atomic service API**: This API can be used in atomic services since API version 12. 639 640**System capability**: SystemCapability.ArkUI.ArkUI.Full 641 642## TabsController 643 644Defines a tab controller, which is used to control switching of tabs. One **TabsController** cannot control multiple **Tabs** components. 645 646**Atomic service API**: This API can be used in atomic services since API version 11. 647 648**System capability**: SystemCapability.ArkUI.ArkUI.Full 649 650### Objects to Import 651 652```ts 653let controller: TabsController = new TabsController() 654``` 655 656### constructor 657 658constructor() 659 660A constructor used to create a **TabsController** object. 661 662**Atomic service API**: This API can be used in atomic services since API version 11. 663 664**System capability**: SystemCapability.ArkUI.ArkUI.Full 665 666### changeIndex 667 668changeIndex(value: number): void 669 670Switches to the specified tab. 671 672**Atomic service API**: This API can be used in atomic services since API version 11. 673 674**System capability**: SystemCapability.ArkUI.ArkUI.Full 675 676**Parameters** 677 678| Name | Type | Mandatory | Description | 679| ----- | ------ | ---- | ---------------------------------------- | 680| value | number | Yes | Index of the tab. The value starts from 0.<br>**NOTE**<br>If this parameter is set to a value less than 0 or greater than the maximum number, the default value **0** is used.| 681 682### preloadItems<sup>12+</sup> 683 684preloadItems(indices: Optional\<Array\<number>>): Promise\<void> 685 686Preloads child nodes. After this API is called, all specified child nodes will be loaded at once. Therefore, for performance considerations, it is recommended that you load child nodes in batches. 687 688**Atomic service API**: This API can be used in atomic services since API version 12. 689 690**System capability**: SystemCapability.ArkUI.ArkUI.Full 691 692**Parameters** 693 694| Name | Type | Mandatory | Description | 695| ----- | ------ | ---- | ---------------------------------------- | 696| indices | Optional\<Array\<number>> | Yes| Array of indexes of the child nodes to preload.<br>The default value is an empty array.| 697 698**Return value** 699 700| Type | Description | 701| ------------------------------------------------------------ | ------------------------ | 702| Promise\<void> | Promise used to return the value.| 703 704**Error codes** 705 706For details about the error codes, see [Universal Error Codes](../../errorcode-universal.md). 707 708| ID | Error Message | 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 716Sets the translation distance of the tab bar. 717 718> **NOTE** 719> 720> When **bindTabsToScrollable** or **bindTabsToNestedScrollable** is used to bind the **Tabs** component with a scrollable container, scrolling the container will trigger the display and hide animations of the tab bar for all **Tabs** components bound to it. In this case, calling the **setTabBarTranslate** API has no effect. Therefore, avoid using **bindTabsToScrollable**, **bindTabsToNestedScrollable**, and **setTabBarTranslate** simultaneously. 721> 722 723**Atomic service API**: This API can be used in atomic services since API version 13. 724 725**System capability**: SystemCapability.ArkUI.ArkUI.Full 726 727**Parameters** 728 729| Name | Type | Mandatory | Description | 730| ----- | ------ | ---- | ---------------------------------------- | 731| translate | [TranslateOptions](ts-universal-attributes-transformation.md#translateoptions) | Yes| Translation distance of the tab bar.| 732 733### setTabBarOpacity<sup>13+</sup> 734 735setTabBarOpacity(opacity: number): void 736 737Sets the opacity of the tab bar. 738 739> **NOTE** 740> 741> When **bindTabsToScrollable** or **bindTabsToNestedScrollable** is used to bind the **Tabs** component with a scrollable container, scrolling the container will trigger the display and hide animations of the tab bar for all **Tabs** components bound to it. In this case, calling the **setTabBarOpacity** API has no effect. Therefore, avoid using **bindTabsToScrollable**, **bindTabsToNestedScrollable**, and **setTabBarOpacity** simultaneously. 742> 743 744**Atomic service API**: This API can be used in atomic services since API version 13. 745 746**System capability**: SystemCapability.ArkUI.ArkUI.Full 747 748**Parameters** 749 750| Name | Type | Mandatory | Description | 751| ----- | ------ | ---- | ---------------------------------------- | 752| opacity | number | Yes| Opacity of the tab bar. The value range is [0.0, 1.0].| 753 754## Example 755 756### Example 1: Setting Up Custom Tab Switching Synchronization 757 758This example demonstrates how to use **onAnimationStart** and **onChange** to synchronize tabs with swiping gestures during tab switching. 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 controls which tab is displayed. 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 controls the color switching for the image and text in the custom tab bar. 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### Example 2: Setting the Basic Attributes of the Divider 834 835This example uses **divider** to present dividers in different styles. 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('Regular 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('Empty Divider').width('100%').margin({ bottom: '12vp' }) 899 .onClick(() => { 900 this.nullFlag = true 901 }) 902 Button('Change to Blue').width('100%').margin({ bottom: '12vp'}) 903 .onClick(() => { 904 this.dividerColor = 'blue' 905 }) 906 Button('Increase Width').width('100%').margin({ bottom: '12vp' }) 907 .onClick(() => { 908 this.strokeWidth += 2 909 }) 910 Button('Decrease Width').width('100%').margin({ bottom: '12vp'}) 911 .onClick(() => { 912 if (this.strokeWidth > 2) { 913 this.strokeWidth -= 2 914 } 915 }) 916 Button('Increase Top Margin').width('100%').margin({ bottom: '12vp' }) 917 .onClick(() => { 918 this.startMargin += 2 919 }) 920 Button('Decrease Top Margin').width('100%').margin({ bottom: '12vp' }) 921 .onClick(() => { 922 if (this.startMargin > 2) { 923 this.startMargin -= 2 924 } 925 }) 926 Button('Increase Bottom Margin').width('100%').margin({ bottom:'12vp'}) 927 .onClick(() => { 928 this.endMargin += 2 929 }) 930 Button('Decrease Bottom Margin').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### Example 3: Setting Tab Bar Fading 944 945This example uses **fadingEdge** to specify whether to fade out tabs. 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('Set Tab to Fade').width('100%').margin({ bottom: '12vp' }) 960 .onClick((event?: ClickEvent) => { 961 this.selfFadingFade = true; 962 }) 963 Button('Set Tab Not to Fade').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### Example 4: Setting the Tab Bar to Be Superimposed on the TabContent Component 1058 1059This example uses **barOverlap** to specify whether the tab bar is superimposed on the **TabContent** component after having its background blurred. 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("Change 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'), "test 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### Example 5: Setting the Grid-Aligned Visible Area for the TabBar 1109 1110This example uses **barGridAlign** to set the visible area of the tab bar in grid mode. 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("Clicked content: " + 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 += "index " + index + " was clicked\n"; 1201 }) 1202 } 1203 .width('100%') 1204 .height(500) 1205 .margin({ top: 5 }) 1206 .padding('10vp') 1207 } 1208} 1209``` 1210 1211 1212 1213### Example 6: Setting the Layout Style for a Scrollable TabBar 1214 1215This example implements the **ScrollableBarModeOptions** parameter of **barMode**. This parameter is effective only in **Scrollable** mode. 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 = "Text" 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("Add Text") 1250 .width('47%') 1251 .height(50) 1252 .margin({ top: 5 }) 1253 .onClick((event?: ClickEvent) => { 1254 this.text += 'Add Text' 1255 }) 1256 .margin({ right: '6%', bottom: '12vp' }) 1257 Button("Reset Text") 1258 .width('47%') 1259 .height(50) 1260 .margin({ top: 5 }) 1261 .onClick((event?: ClickEvent) => { 1262 this.text = "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### Example 7: Implementing a Custom Tab Switching Animation 1332 1333This example uses **customContentTransition** to implement a custom tab switching animation. 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 // Customize the opacity and scale animation. 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### Example 8: Intercepting Page Switching 1422 1423This example uses **onContentWillChange** to intercept and customize the gesture-based swiping actions for page switching. 1424 1425```ts 1426//xxx.ets 1427@Entry 1428@Component 1429struct TabsExample { 1430 @State currentIndex: number = 2 1431 private controller: TabsController = new TabsController() 1432 @Builder tabBuilder(title: string,targetIndex: number) { 1433 Column(){ 1434 Text(title).fontColor(this.currentIndex === targetIndex ? '#1698CE' : '#6B6B6B') 1435 }.width('100%') 1436 .height(50) 1437 .justifyContent(FlexAlign.Center) 1438 } 1439 build() { 1440 Column() { 1441 Tabs({ barPosition: BarPosition.End, controller: this.controller, index: this.currentIndex }) { 1442 TabContent() { 1443 Column(){ 1444 Text('Content of the Home tab') 1445 }.width('100%').height('100%').backgroundColor('#00CB87').justifyContent(FlexAlign.Center) 1446 }.tabBar(this.tabBuilder('Home',0)) 1447 1448 TabContent() { 1449 Column(){ 1450 Text('Content of the Discover tab') 1451 }.width('100%').height('100%').backgroundColor('#007DFF').justifyContent(FlexAlign.Center) 1452 }.tabBar(this.tabBuilder('Discover',1)) 1453 1454 TabContent() { 1455 Column(){ 1456 Text('Content of the Recommended tab') 1457 }.width('100%').height('100%').backgroundColor('#FFBF00').justifyContent(FlexAlign.Center) 1458 }.tabBar(this.tabBuilder('Recommended',2)) 1459 1460 TabContent() { 1461 Column(){ 1462 Text('Content of the Me tab') 1463 }.width('100%').height('100%').backgroundColor('#E67C92').justifyContent(FlexAlign.Center) 1464 }.tabBar(this.tabBuilder('Me',3)) 1465 } 1466 .vertical(false) 1467 .barMode(BarMode.Fixed) 1468 .barWidth(360) 1469 .barHeight(60) 1470 .animationDuration(0) 1471 .onChange((index: number) => { 1472 this.currentIndex = index 1473 }) 1474 .width(360) 1475 .height(600) 1476 .backgroundColor('#F1F3F5') 1477 .scrollable(true) 1478 .onContentWillChange((currentIndex, comingIndex) => { 1479 if (comingIndex == 2) { 1480 return false 1481 } 1482 return true 1483 }) 1484 1485 Button('Change Index').width('50%').margin({ top: 20 }) 1486 .onClick(()=>{ 1487 this.currentIndex = (this.currentIndex + 1) % 4 1488 }) 1489 1490 Button('changeIndex').width('50%').margin({ top: 20 }) 1491 .onClick(()=>{ 1492 this.currentIndex = (this.currentIndex + 1) % 4 1493 this.controller.changeIndex(this.currentIndex) 1494 }) 1495 }.width('100%') 1496 } 1497} 1498``` 1499 1500 1501 1502### Example 9: Customizing the Tab Bar Switching Animation 1503 1504This example uses **onChange**, **onAnimationStart**, **onAnimationEnd**, and **onGestureSwipe** APIs to customize the tab bar switching animation. 1505 1506<!--code_no_check--> 1507 1508```ts 1509// EntryAbility.ets 1510import { Configuration, UIAbility } from '@kit.AbilityKit' 1511import { i18n } from '@kit.LocalizationKit' 1512import { CommonUtil } from '../common/CommonUtil' 1513 1514export default class EntryAbility extends UIAbility { 1515 onConfigurationUpdate(newConfig: Configuration): void { 1516 // Listen for system configuration changes. 1517 if (newConfig.language) { 1518 CommonUtil.setIsRTL(i18n.isRTL(newConfig.language)) 1519 } 1520 } 1521} 1522``` 1523 1524<!--code_no_check--> 1525 1526```ts 1527// CommonUtil.ets 1528import { i18n, intl } from '@kit.LocalizationKit' 1529 1530export class CommonUtil { 1531 private static isRTL: boolean = i18n.isRTL((new intl.Locale()).language) 1532 1533 public static setIsRTL(isRTL: boolean): void { 1534 CommonUtil.isRTL = isRTL 1535 } 1536 1537 public static getIsRTL(): boolean { 1538 return CommonUtil.isRTL 1539 } 1540} 1541``` 1542 1543<!--code_no_check--> 1544 1545```ts 1546// xxx.ets 1547import { LengthMetrics } from '@kit.ArkUI' 1548import { CommonUtil } from '../common/CommonUtil' 1549 1550@Entry 1551@Component 1552struct TabsExample { 1553 @State colorArray: [string, string][] = 1554 [['green', '#00CB87'], ['blue', '#007DFF'], ['yellow', '#FFBF00'], ['pink', '#E67C92']] 1555 @State currentIndex: number = 0 1556 @State animationDuration: number = 300 1557 @State indicatorLeftMargin: number = 0 1558 @State indicatorWidth: number = 0 1559 private tabsWidth: number = 0 1560 private textInfos: [number, number][] = [] 1561 private isStartAnimateTo: boolean = false 1562 1563 aboutToAppear():void { 1564 for (let i = 0; i < this.colorArray.length; i++) { 1565 this.textInfos.push([0, 0]); 1566 } 1567 } 1568 1569 @Builder 1570 tabBuilder(index: number, name: string) { 1571 Column() { 1572 Text(name) 1573 .fontSize(16) 1574 .fontColor(this.currentIndex === index ? '#007DFF' : '#182431') 1575 .fontWeight(this.currentIndex === index ? 500 : 400) 1576 .id(index.toString()) 1577 .onAreaChange((oldValue: Area, newValue: Area) => { 1578 this.textInfos[index] = [newValue.globalPosition.x as number, newValue.width as number] 1579 if (!this.isStartAnimateTo && this.currentIndex === index && this.tabsWidth > 0) { 1580 this.setIndicatorAttr(this.textInfos[this.currentIndex][0], this.textInfos[this.currentIndex][1]) 1581 } 1582 }) 1583 }.width('100%') 1584 } 1585 1586 build() { 1587 Stack({ alignContent: Alignment.TopStart }) { 1588 Tabs({ barPosition: BarPosition.Start }) { 1589 ForEach(this.colorArray, (item: [string, string], index:number) => { 1590 TabContent() { 1591 Column().width('100%').height('100%').backgroundColor(item[1]) 1592 }.tabBar(this.tabBuilder(index, item[0])) 1593 }) 1594 } 1595 .onAreaChange((oldValue: Area, newValue: Area)=> { 1596 this.tabsWidth = newValue.width as number 1597 if (!this.isStartAnimateTo) { 1598 this.setIndicatorAttr(this.textInfos[this.currentIndex][0], this.textInfos[this.currentIndex][1]) 1599 } 1600 }) 1601 .barWidth('100%') 1602 .barHeight(56) 1603 .width('100%') 1604 .height(296) 1605 .backgroundColor('#F1F3F5') 1606 .animationDuration(this.animationDuration) 1607 .onChange((index: number) => { 1608 this.currentIndex = index // Listen for index changes to switch between tab pages. 1609 }) 1610 .onAnimationStart((index: number, targetIndex: number, event: TabsAnimationEvent) => { 1611 // Triggered when the tab switching animation starts. The underline moves with the active tab, along with a width gradient. 1612 this.currentIndex = targetIndex 1613 this.startAnimateTo(this.animationDuration, this.textInfos[targetIndex][0], this.textInfos[targetIndex][1]) 1614 }) 1615 .onAnimationEnd((index: number, event: TabsAnimationEvent) => { 1616 // Triggered when the tab switching animation ends. The underline animation stops. 1617 let currentIndicatorInfo = this.getCurrentIndicatorInfo(index, event) 1618 this.startAnimateTo(0, currentIndicatorInfo.left, currentIndicatorInfo.width) 1619 }) 1620 .onGestureSwipe((index: number, event: TabsAnimationEvent) => { 1621 // Triggered on a frame-by-frame basis when the tab is switched by a swipe. 1622 let currentIndicatorInfo = this.getCurrentIndicatorInfo(index, event) 1623 this.currentIndex = currentIndicatorInfo.index 1624 this.setIndicatorAttr(currentIndicatorInfo.left, currentIndicatorInfo.width) 1625 }) 1626 1627 Column() 1628 .height(2) 1629 .width(this.indicatorWidth) 1630 .margin({ start: LengthMetrics.vp(this.indicatorLeftMargin), top: LengthMetrics.vp(48) }) 1631 .backgroundColor('#007DFF') 1632 }.width('100%') 1633 } 1634 1635 private getCurrentIndicatorInfo(index: number, event: TabsAnimationEvent): Record<string, number> { 1636 let nextIndex = index 1637 if (index > 0 && (CommonUtil.getIsRTL() ? event.currentOffset < 0 : event.currentOffset > 0)) { 1638 nextIndex-- 1639 } else if (index < this.textInfos.length - 1 && 1640 (CommonUtil.getIsRTL() ? event.currentOffset > 0 : event.currentOffset < 0)) { 1641 nextIndex++ 1642 } 1643 let indexInfo = this.textInfos[index] 1644 let nextIndexInfo = this.textInfos[nextIndex] 1645 let swipeRatio = Math.abs(event.currentOffset / this.tabsWidth) 1646 let currentIndex = swipeRatio > 0.5 ? nextIndex : index // If the swipe distance exceeds half the page width, the tab bar switches to the next page. 1647 let currentLeft = indexInfo[0] + (nextIndexInfo[0] - indexInfo[0]) * swipeRatio 1648 let currentWidth = indexInfo[1] + (nextIndexInfo[1] - indexInfo[1]) * swipeRatio 1649 return { 'index': currentIndex, 'left': currentLeft, 'width': currentWidth } 1650 } 1651 1652 private startAnimateTo(duration: number, leftMargin: number, width: number) { 1653 this.isStartAnimateTo = true 1654 animateTo({ 1655 duration: duration, // Animation duration. 1656 curve: Curve.Linear, // Animation curve. 1657 iterations: 1, // Number of playback times. 1658 playMode: PlayMode.Normal, // Animation playback mode. 1659 onFinish: () => { 1660 this.isStartAnimateTo = false 1661 console.info('play end') 1662 } 1663 }, () => { 1664 this.setIndicatorAttr(leftMargin, width) 1665 }) 1666 } 1667 1668 private setIndicatorAttr(leftMargin: number, width: number) { 1669 this.indicatorWidth = width 1670 if (CommonUtil.getIsRTL()) { 1671 this.indicatorLeftMargin = this.tabsWidth - leftMargin - width 1672 } else { 1673 this.indicatorLeftMargin = leftMargin 1674 } 1675 } 1676} 1677``` 1678 1679 1680 1681### Example 10: Preloading Child Nodes 1682 1683In this example, the **preloadItems** API is used to preload specified child nodes. 1684 1685```ts 1686// xxx.ets 1687import { BusinessError } from '@kit.BasicServicesKit' 1688 1689@Entry 1690@Component 1691struct TabsPreloadItems { 1692 @State currentIndex: number = 1 1693 private tabsController: TabsController = new TabsController() 1694 1695 build() { 1696 Column() { 1697 Tabs({ index: this.currentIndex, controller: this.tabsController }) { 1698 TabContent() { 1699 MyComponent({ color: '#00CB87' }) 1700 }.tabBar(SubTabBarStyle.of('green')) 1701 1702 TabContent() { 1703 MyComponent({ color: '#007DFF' }) 1704 }.tabBar(SubTabBarStyle.of('blue')) 1705 1706 TabContent() { 1707 MyComponent({ color: '#FFBF00' }) 1708 }.tabBar(SubTabBarStyle.of('yellow')) 1709 1710 TabContent() { 1711 MyComponent({ color: '#E67C92' }) 1712 }.tabBar(SubTabBarStyle.of('pink')) 1713 } 1714 .width(360) 1715 .height(296) 1716 .backgroundColor('#F1F3F5') 1717 .onChange((index: number) => { 1718 this.currentIndex = index 1719 }) 1720 1721 Button('preload items: [0, 2, 3]') 1722 .margin(5) 1723 .onClick(() => { 1724 // Preload child nodes 0, 2, and 3 to improve the performance when users swipe or click to switch to these nodes. 1725 this.tabsController.preloadItems([0, 2, 3]) 1726 .then(() => { 1727 console.info('preloadItems success.') 1728 }) 1729 .catch((error: BusinessError) => { 1730 console.error('preloadItems failed, error code: ' + error.code + ', error message: ' + error.message) 1731 }) 1732 }) 1733 } 1734 } 1735} 1736 1737@Component 1738struct MyComponent { 1739 private color: string = "" 1740 1741 aboutToAppear(): void { 1742 console.info('aboutToAppear backgroundColor:' + this.color) 1743 } 1744 1745 aboutToDisappear(): void { 1746 console.info('aboutToDisappear backgroundColor:' + this.color) 1747 } 1748 1749 build() { 1750 Column() 1751 .width('100%') 1752 .height('100%') 1753 .backgroundColor(this.color) 1754 } 1755} 1756``` 1757 1758### Example 11: Setting Tab Bar Translation and Opacity 1759 1760This example demonstrates how to set the translation distance and opacity of the tab bar using the **setTabBarTranslate** and **setTabBarOpacity** APIs. 1761 1762```ts 1763// xxx.ets 1764@Entry 1765@Component 1766struct TabsExample { 1767 private controller: TabsController = new TabsController() 1768 1769 build() { 1770 Column() { 1771 Button('Set TabBar Translation Distance').margin({ top: 20 }) 1772 .onClick(() => { 1773 this.controller.setTabBarTranslate({ x: -20, y: -20 }) 1774 }) 1775 1776 Button('Set TabBar Opacity').margin({ top: 20 }) 1777 .onClick(() => { 1778 this.controller.setTabBarOpacity(0.5) 1779 }) 1780 1781 Tabs({ barPosition: BarPosition.End, controller: this.controller }) { 1782 TabContent() { 1783 Column().width('100%').height('100%').backgroundColor('#00CB87') 1784 }.tabBar(BottomTabBarStyle.of($r('app.media.startIcon'), 'green')) 1785 1786 TabContent() { 1787 Column().width('100%').height('100%').backgroundColor('#007DFF') 1788 }.tabBar(BottomTabBarStyle.of($r('app.media.startIcon'), 'blue')) 1789 1790 TabContent() { 1791 Column().width('100%').height('100%').backgroundColor('#FFBF00') 1792 }.tabBar(BottomTabBarStyle.of($r('app.media.startIcon'), 'yellow')) 1793 1794 TabContent() { 1795 Column().width('100%').height('100%').backgroundColor('#E67C92') 1796 }.tabBar(BottomTabBarStyle.of($r('app.media.startIcon'), 'pink')) 1797 } 1798 .width(360) 1799 .height(296) 1800 .margin({ top: 20 }) 1801 .barBackgroundColor('#F1F3F5') 1802 } 1803 .width('100%') 1804 } 1805} 1806``` 1807 1808 1809 1810### Example 12: Implementing Lazy Loading and Resource Release of Pages 1811 1812This example demonstrates how to use a custom tab bar with the **Swiper** component and **LazyForEach** to implement lazy loading and resource release of pages. 1813 1814```ts 1815// xxx.ets 1816class MyDataSource implements IDataSource { 1817 private list: number[] = [] 1818 1819 constructor(list: number[]) { 1820 this.list = list 1821 } 1822 1823 totalCount(): number { 1824 return this.list.length 1825 } 1826 1827 getData(index: number): number { 1828 return this.list[index] 1829 } 1830 1831 registerDataChangeListener(listener: DataChangeListener): void { 1832 } 1833 1834 unregisterDataChangeListener() { 1835 } 1836} 1837 1838@Entry 1839@Component 1840struct TabsSwiperExample { 1841 @State fontColor: string = '#182431' 1842 @State selectedFontColor: string = '#007DFF' 1843 @State currentIndex: number = 0 1844 private list: number[] = [] 1845 private tabsController: TabsController = new TabsController() 1846 private swiperController: SwiperController = new SwiperController() 1847 private swiperData: MyDataSource = new MyDataSource([]) 1848 1849 aboutToAppear(): void { 1850 for (let i = 0; i <= 9; i++) { 1851 this.list.push(i); 1852 } 1853 this.swiperData = new MyDataSource(this.list) 1854 } 1855 1856 @Builder tabBuilder(index: number, name: string) { 1857 Column() { 1858 Text(name) 1859 .fontColor(this.currentIndex === index ? this.selectedFontColor : this.fontColor) 1860 .fontSize(16) 1861 .fontWeight(this.currentIndex === index ? 500 : 400) 1862 .lineHeight(22) 1863 .margin({ top: 17, bottom: 7 }) 1864 Divider() 1865 .strokeWidth(2) 1866 .color('#007DFF') 1867 .opacity(this.currentIndex === index ? 1 : 0) 1868 }.width('20%') 1869 } 1870 1871 build() { 1872 Column() { 1873 Tabs({ barPosition: BarPosition.Start, controller: this.tabsController }) { 1874 ForEach(this.list, (item: number) => { 1875 TabContent().tabBar(this.tabBuilder(item, 'Tab ' + this.list[item])) 1876 }) 1877 } 1878 .onTabBarClick((index: number) => { 1879 this.currentIndex = index 1880 this.swiperController.changeIndex(index, true) 1881 }) 1882 .barMode(BarMode.Scrollable) 1883 .backgroundColor('#F1F3F5') 1884 .height(56) 1885 .width('100%') 1886 1887 Swiper(this.swiperController) { 1888 LazyForEach(this.swiperData, (item: string) => { 1889 Text(item.toString()) 1890 .onAppear(()=>{ 1891 console.info('onAppear ' + item.toString()) 1892 }) 1893 .onDisAppear(()=>{ 1894 console.info('onDisAppear ' + item.toString()) 1895 }) 1896 .width('100%') 1897 .height('100%') 1898 .backgroundColor(0xAFEEEE) 1899 .textAlign(TextAlign.Center) 1900 .fontSize(30) 1901 }, (item: string) => item) 1902 } 1903 .loop(false) 1904 .onChange((index: number) => { 1905 this.currentIndex = index 1906 }) 1907 .onAnimationStart((index: number, targetIndex: number, extraInfo: SwiperAnimationEvent) => { 1908 this.currentIndex = targetIndex 1909 this.tabsController.changeIndex(targetIndex) 1910 }) 1911 } 1912 } 1913} 1914``` 1915 1916 1917