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 takes up space in the layout. 19 20 21## APIs 22 23Tabs(value?: {barPosition?: BarPosition, index?: number, controller?: TabsController}) 24 25**Atomic service API**: This API can be used in atomic services since API version 11. 26 27**System capability**: SystemCapability.ArkUI.ArkUI.Full 28 29**Parameters** 30 31| Name | Type | Mandatory | Description | 32| ----------- | --------------------------------- | ---- | ---------------------------------------- | 33| barPosition | [BarPosition](#barposition)| No | Position of the **Tabs** component.<br>Default value: **BarPosition.Start** | 34| index | number | No | Index of the currently displayed tab.<br>Default value: **0**<br>**NOTE**<br><br>A value less than 0 evaluates to the default value.<br>The value ranges from 0 to the number of **TabContent** subnodes 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).| 35| controller | [TabsController](#tabscontroller) | No | Tab controller. | 36 37## BarPosition 38 39Enumerates the positions of the **Tabs** component. 40 41**Atomic service API**: This API can be used in atomic services since API version 11. 42 43**System capability**: SystemCapability.ArkUI.ArkUI.Full 44 45| Name | Description | 46| ----- | ---------------------------------------- | 47| 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.| 48| 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.| 49 50 51## Attributes 52 53In addition to the [universal attributes](ts-universal-attributes-size.md), the following attributes are supported. 54 55### vertical 56 57vertical(value: boolean) 58 59Sets whether to use vertical tabs. 60 61**Atomic service API**: This API can be used in atomic services since API version 11. 62 63**System capability**: SystemCapability.ArkUI.ArkUI.Full 64 65**Parameters** 66 67| Name| Type | Mandatory| Description | 68| ------ | ------- | ---- | ------------------------------------------------------------ | 69| 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.| 70 71### scrollable 72 73scrollable(value: boolean) 74 75Sets whether the tabs are scrollable. 76 77**Atomic service API**: This API can be used in atomic services since API version 11. 78 79**System capability**: SystemCapability.ArkUI.ArkUI.Full 80 81**Parameters** 82 83| Name| Type | Mandatory| Description | 84| ------ | ------- | ---- | ------------------------------------------------------------ | 85| value | boolean | Yes | Whether the tabs are scrollable.<br>**true** (default): The tabs are scrollable.<br> **false**: The tabs are not scrollable.| 86 87### barMode 88 89barMode(value: BarMode, options?: ScrollableBarModeOptions) 90 91Sets the tab bar layout mode. 92 93**Atomic service API**: This API can be used in atomic services since API version 11. 94 95**System capability**: SystemCapability.ArkUI.ArkUI.Full 96 97**Parameters** 98 99| Name | Type | Mandatory| Description | 100| --------------------- | ------------------------------------------------------------ | ---- | ------------------------------------------------------------ | 101| value | [BarMode](#barmode) | Yes | Layout mode.<br>Default value: **BarMode.Fixed** | 102| options<sup>10+</sup> | [ScrollableBarModeOptions](#scrollablebarmodeoptions10)| No | Layout style.<br>**NOTE**<br>This value is effective only when the tab bar is in scrollable mode.| 103 104### barMode<sup>10+</sup> 105 106barMode(value: BarMode.Fixed) 107 108Sets the tab bar layout mode to **BarMode.Fixed**. 109 110**Atomic service API**: This API can be used in atomic services since API version 11. 111 112**System capability**: SystemCapability.ArkUI.ArkUI.Full 113 114**Parameters** 115 116| Name | Type | Mandatory| Description | 117| -------- | -------------------------------- | ---- | ------------------------------------ | 118| 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). | 119 120### barMode<sup>10+</sup> 121 122barMode(value: BarMode.Scrollable, options: ScrollableBarModeOptions) 123 124Sets the tab bar layout mode to **BarMode.Scrollable**. 125 126**Atomic service API**: This API can be used in atomic services since API version 11. 127 128**System capability**: SystemCapability.ArkUI.ArkUI.Full 129 130**Parameters** 131 132| Name | Type | Mandatory| Description | 133| -------- | --------------------------------- | ---- | ------------------------------------- | 134| 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 horizontal layout, the total height exceeds the tab bar height. | 135| options | [ScrollableBarModeOptions](#scrollablebarmodeoptions10)| Yes | Layout style.| 136 137### barWidth 138 139barWidth(value: Length) 140 141Sets the width of the tab bar. A value less than 0 or greater than the width of the **Tabs** component evaluates to the default value. 142 143**Atomic service API**: This API can be used in atomic services since API version 11. 144 145**System capability**: SystemCapability.ArkUI.ArkUI.Full 146 147**Parameters** 148 149| Name| Type | Mandatory| Description | 150| ------ | ----------------------------------------- | ---- | ------------------------------------------------------------ | 151| 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.| 152 153### barHeight 154 155barHeight(value: Length) 156 157Sets the height of the tab bar. A value less than 0 or greater than the height of the **Tabs** component evaluates to the default value. 158 159**Atomic service API**: This API can be used in atomic services since API version 11. 160 161**System capability**: SystemCapability.ArkUI.ArkUI.Full 162 163**Parameters** 164 165| Name| Type | Mandatory| Description | 166| ------ | ----------------------------------------- | ---- | ------------------------------------------------------------ | 167| 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 52 vp since API version 12.| 168 169### animationDuration 170 171animationDuration(value: number) 172 173Sets 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. 174 175**Atomic service API**: This API can be used in atomic services since API version 11. 176 177**System capability**: SystemCapability.ArkUI.ArkUI.Full 178 179**Parameters** 180 181| Name| Type | Mandatory| Description | 182| ------ | ------ | ---- | ------------------------------------------------------------ | 183| 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 **0** is used when the tab bar is set to **BottomTabBarStyle**; the default value **300** is used when the tab bar is set to any other style.<br>Unit: ms| 184 185### animationMode<sup>12+</sup> 186 187animationMode(mode: Optional\<AnimationMode\>) 188 189Sets the animation mode for switching between tabs. 190 191**Atomic service API**: This API can be used in atomic services since API version 12. 192 193**System capability**: SystemCapability.ArkUI.ArkUI.Full 194 195**Parameters** 196 197| Name| Type | Mandatory| Description | 198| ------ | ------ | ---- | ------------------------------------------------------------ | 199| mode | Optional\<[AnimationMode](#animationmode12)\>| Yes | Animation mode for switching between tabs.<br>The default value varies.<br>The default value is **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.| 200 201### barPosition<sup>9+</sup> 202 203barPosition(value: BarPosition) 204 205Position of the **Tabs** component. 206 207**Atomic service API**: This API can be used in atomic services since API version 11. 208 209**System capability**: SystemCapability.ArkUI.ArkUI.Full 210 211**Parameters** 212 213| Name| Type | Mandatory| Description | 214| ----- | ---------------------------------- | ---- | -------------------- | 215| value | [BarPosition](#barposition)| Yes | Position of the **Tabs** component.<br>Default value: **BarPosition.Start** | 216 217### divider<sup>10+</sup> 218 219divider(value: DividerStyle | null) 220 221Sets the divider style for the **TabBar** and **TabContent** components. 222 223**Atomic service API**: This API can be used in atomic services since API version 11. 224 225**System capability**: SystemCapability.ArkUI.ArkUI.Full 226 227**Parameters** 228 229| Name| Type | Mandatory| Description | 230| ------ | --------------------------------------------------------- | ---- | ------------------------------------------------------------ | 231| 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.| 232 233### fadingEdge<sup>10+</sup> 234 235fadingEdge(value: boolean) 236 237Sets 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. 238 239**Atomic service API**: This API can be used in atomic services since API version 11. 240 241**System capability**: SystemCapability.ArkUI.ArkUI.Full 242 243**Parameters** 244 245| Name| Type | Mandatory| Description | 246| ------ | ------- | ---- | -------------------------------------------------- | 247| value | boolean | Yes | Whether the tab fades out when it exceeds the container width.<br>Default value: **true**| 248 249### barOverlap<sup>10+</sup> 250 251barOverlap(value: boolean) 252 253Sets whether the tab bar is superimposed on the **TabContent** component after having its background blurred. 254 255**Atomic service API**: This API can be used in atomic services since API version 11. 256 257**System capability**: SystemCapability.ArkUI.ArkUI.Full 258 259**Parameters** 260 261| Name| Type | Mandatory| Description | 262| ------ | ------- | ---- | ------------------------------------------------------------ | 263| value | boolean | Yes | Whether the tab bar is superimposed on the **TabContent** component after having its background blurred.<br>Default value: **false**| 264 265### barBackgroundColor<sup>10+</sup> 266 267barBackgroundColor(value: ResourceColor) 268 269Background color of the tab bar. 270 271**Atomic service API**: This API can be used in atomic services since API version 11. 272 273**System capability**: SystemCapability.ArkUI.ArkUI.Full 274 275**Parameters** 276 277| Name| Type | Mandatory| Description | 278| ------ | ------------------------------------------ | ---- | ------------------------------------ | 279| value | [ResourceColor](ts-types.md#resourcecolor) | Yes | Background color of the tab bar.<br>Default value: **Color.Transparent**| 280 281### barBackgroundBlurStyle<sup>11+</sup> 282 283barBackgroundBlurStyle(value: BlurStyle) 284 285Sets the background blur style of the tab bar. 286 287**Atomic service API**: This API can be used in atomic services since API version 11. 288 289**System capability**: SystemCapability.ArkUI.ArkUI.Full 290 291**Parameters** 292 293| Name| Type | Mandatory| Description | 294| ------ | -------------------------------------------- | ---- | ---------------------------------------- | 295| value | [BlurStyle](ts-universal-attributes-background.md#blurstyle9) | Yes | Background blur style of the tab bar.<br>Default value: **BlurStyle.NONE**| 296 297### barGridAlign<sup>10+</sup> 298 299barGridAlign(value: BarGridColumnOptions) 300 301Sets 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). 302 303**Atomic service API**: This API can be used in atomic services since API version 11. 304 305**System capability**: SystemCapability.ArkUI.ArkUI.Full 306 307**Parameters** 308 309| Name| Type | Mandatory| Description | 310| ------ | ------------------------------------------------------- | ---- | ---------------------------------- | 311| value | [BarGridColumnOptions](#bargridcolumnoptions10) | Yes | Visible area of the tab bar in grid mode.| 312 313### edgeEffect<sup>12+</sup> 314 315edgeEffect(edgeEffect: Optional<EdgeEffect>) 316 317Sets the edge effect used when the boundary of the scrolling area is reached. 318 319**Atomic service API**: This API can be used in atomic services since API version 12. 320 321**System capability**: SystemCapability.ArkUI.ArkUI.Full 322 323**Parameters** 324 325| Name| Type | Mandatory| Description | 326| ------ | --------------------------------------------- | ---- | -------------------------------------------- | 327| 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**| 328 329## DividerStyle<sup>10+</sup> 330 331Describes the divider style. 332 333**Atomic service API**: This API can be used in atomic services since API version 11. 334 335**System capability**: SystemCapability.ArkUI.ArkUI.Full 336 337| Name | Type | Mandatory | Description | 338| ----------- | ---------------------------------------- | ---- | ---------------------------------------- | 339| 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 | 340| color | [ResourceColor](ts-types.md#resourcecolor) | No | Color of the divider.<br>Default value: **#33182431** | 341| 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| 342| 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| 343 344## BarGridColumnOptions<sup>10+</sup> 345 346Implements 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. 347 348**Atomic service API**: This API can be used in atomic services since API version 11. 349 350**System capability**: SystemCapability.ArkUI.ArkUI.Full 351 352| Name | Type | Mandatory | Description | 353| ----------- | ---------------------------------------- | ---- | ---------------------------------------- | 354| 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 | 355| 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 | 356| 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.| 357| 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.| 358| 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.| 359 360## ScrollableBarModeOptions<sup>10+</sup> 361 362Implements a **ScrollableBarModeOptions** object. 363 364**Atomic service API**: This API can be used in atomic services since API version 11. 365 366**System capability**: SystemCapability.ArkUI.ArkUI.Full 367 368| Name | Type | Mandatory | Description | 369| ----------- | ---------------------------------------- | ---- | ---------------------------------------- | 370| 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 | 371| nonScrollableLayoutStyle | [LayoutStyle](#layoutstyle10) | No | Tab layout mode of the tab bar when not scrolling in scrollable mode.<br>Default value: **LayoutStyle.ALWAYS_CENTER** | 372 373## BarMode 374 375Enumerates layout modes of the tab bar. 376 377**Atomic service API**: This API can be used in atomic services since API version 11. 378 379**System capability**: SystemCapability.ArkUI.ArkUI.Full 380 381| Name | Value| Description | 382| ---------- | -- | ---------------------------------------- | 383| 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 horizontal layout, the total height exceeds the tab bar height.| 384| 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).| 385 386## AnimationMode<sup>12+</sup> 387 388Enumerates the animation modes for switching between tabs. 389 390**Atomic service API**: This API can be used in atomic services since API version 12. 391 392**System capability**: SystemCapability.ArkUI.ArkUI.Full 393 394| Name | Value | Description | 395| ------------- | -- | ---------------------------------------- | 396| CONTENT_FIRST | 0 | Load the content of the target page before starting the switching animation.| 397| 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**.| 398| NO_ANIMATION | 2 | The switching animation is disabled.| 399 400## LayoutStyle<sup>10+</sup> 401 402Enumerates the tab layout styles of the tab bar when not scrolling in scrollable mode. 403 404**Atomic service API**: This API can be used in atomic services since API version 11. 405 406**System capability**: SystemCapability.ArkUI.ArkUI.Full 407 408| Name | Value| Description | 409| ---------- | -- | ---------------------------------------- | 410| ALWAYS_CENTER | 0 | When the tab content exceeds the tab bar width, the tabs are scrollable.<br>Otherwise, the tabs are compactly centered and not scrollable.| 411| ALWAYS_AVERAGE_SPLIT | 1 | When the tab content exceeds the tab bar width, the tabs are scrollable.<br>Otherwise, the tabs are not scrollable, and the tab bar width is distributed evenly between all tabs.<br>This option is valid only in horizontal mode, and is equivalent to **LayoutStyle.ALWAYS_CENTER** otherwise.| 412| SPACE_BETWEEN_OR_CENTER | 2 | When the tab content exceeds the tab bar width, the tabs are scrollable.<br>When the tab content exceeds half of the tab bar width but still within the tab bar width, the tabs are compactly centered and not scrollable.<br>When the tab content does not exceed half of the tab bar width, the tabs are centered within half of the tab bar width, with even spacing between, and not scrollable.| 413 414## Events 415 416In addition to the [universal events](ts-universal-events-click.md), the following events are supported. 417 418### onChange 419 420onChange(event: (index: number) => void) 421 422Triggered when a tab is switched. 423 424This event is triggered when any of the following conditions is met: 425 4261. The **TabContent** component supports sliding, and the user slides on the tab bar. 427 4282. The [Controller](#tabscontroller) API is called. 429 4303. The attribute value is updated using a [state variable](../../../quick-start/arkts-state.md). 431 4324. A tab is clicked. 433 434**Atomic service API**: This API can be used in atomic services since API version 11. 435 436**System capability**: SystemCapability.ArkUI.ArkUI.Full 437 438**Parameters** 439 440| Name| Type | Mandatory| Description | 441| ------ | ------ | ---- | -------------------------------------- | 442| index | number | Yes | Index of the active tab. The index starts from 0.| 443 444### onTabBarClick<sup>10+</sup> 445 446onTabBarClick(event: (index: number) => void) 447 448Triggered when a tab is clicked. 449 450**Atomic service API**: This API can be used in atomic services since API version 11. 451 452**System capability**: SystemCapability.ArkUI.ArkUI.Full 453 454**Parameters** 455 456| Name| Type | Mandatory| Description | 457| ------ | ------ | ---- | ------------------------------------ | 458| index | number | Yes | Index of the clicked tab. The index starts from 0.| 459 460### onAnimationStart<sup>11+</sup> 461 462onAnimationStart(handler: (index: number, targetIndex: number, event: TabsAnimationEvent) => void) 463 464Triggered when the tab switching animation starts. The **index** parameter indicates the index before the animation starts (not the one after). 465 466**Atomic service API**: This API can be used in atomic services since API version 12. 467 468**System capability**: SystemCapability.ArkUI.ArkUI.Full 469 470**Parameters** 471 472| Name | Type | Mandatory| Description | 473| ----------- | ------------------------------------------------------ | ---- | ------------------------------------------------------------ | 474| index | number | Yes | Index of the currently displayed element. | 475| targetIndex | number | Yes | Index of the target element to switch to. | 476| 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.| 477 478### onAnimationEnd<sup>11+</sup> 479 480onAnimationEnd(handler: (index: number, event: TabsAnimationEvent) => void) 481 482Triggered 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. The **index** parameter indicates the index after the animation ends. 483 484**Atomic service API**: This API can be used in atomic services since API version 12. 485 486**System capability**: SystemCapability.ArkUI.ArkUI.Full 487 488**Parameters** 489 490| Name| Type | Mandatory| Description | 491| ------ | ------------------------------------------------------ | ---- | ------------------------------------------------------------ | 492| index | number | Yes | Index of the currently displayed element. | 493| 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.| 494 495### onGestureSwipe<sup>11+</sup> 496 497onGestureSwipe(handler: (index: number, event: TabsAnimationEvent) => void) 498 499Triggered on a frame-by-frame basis when the tab is switched by a swipe. 500 501**Atomic service API**: This API can be used in atomic services since API version 12. 502 503**System capability**: SystemCapability.ArkUI.ArkUI.Full 504 505**Parameters** 506 507| Name| Type | Mandatory| Description | 508| ------ | ------------------------------------------------------ | ---- | ------------------------------------------------------------ | 509| index | number | Yes | Index of the currently displayed element. | 510| 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.| 511 512### customContentTransition<sup>11+</sup> 513 514customContentTransition(delegate: (from: number, to: number) => TabContentAnimatedTransition \| undefined) 515 516Sets the custom tab switching animation. 517 518Instructions: 519 5201. 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. 521 522**Widget capability**: This API can be used in ArkTS widgets since API version 11. 523 524**Atomic service API**: This API can be used in atomic services since API version 12. 525 526**System capability**: SystemCapability.ArkUI.ArkUI.Full 527 528**Parameters** 529 530| Name| Type | Mandatory| Description | 531| ------ | ------ | ---- | ------------------------------- | 532| from | number | Yes | Index of the currently displayed tab before the animation starts.| 533| to | number | Yes | Index of the target tab before the animation starts.| 534 535**Return value** 536 537| Type | Description | 538| ------------------------------------------------------------ | ------------------------ | 539| [TabContentAnimatedTransition](#tabcontentanimatedtransition11) \| undefined | Information about the custom tab switching animation.| 540 541### onContentWillChange<sup>12+</sup> 542 543onContentWillChange(handler: (currentIndex: number, comingIndex: number) => boolean) 544 545Triggered when a new page is about to be displayed. 546 547Specifically, this event is triggered in the following cases: 548 5491. When the user swipes on the **TabContent** component (provided that it supports swiping) to switch to a new page. 550 5512. When **TabsController.changeIndex** is called to switch to a new page. 552 5533. When the **index** attribute is changed to switch to a new page. 554 5554. When the user clicks a tab on the tab bar to switch to a new page. 556 5575. 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. 558 559**Atomic service API**: This API can be used in atomic services since API version 12. 560 561**System capability**: SystemCapability.ArkUI.ArkUI.Full 562 563**Parameters** 564 565| Name | Type | Mandatory| Description | 566| ------------ | ------ | ---- | ------------------------------------------ | 567| currentIndex | number | Yes | Index of the active tab. The index starts from 0.| 568| comingIndex | number | Yes | Index of the new tab to be displayed. | 569 570**Return value** 571 572| Type | Description | 573| ------- | ------------------------------------------------------------ | 574| 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.| 575 576## TabsAnimationEvent<sup>11+</sup> 577 578Describes the animation information of the **Tabs** component. 579 580**Atomic service API**: This API can be used in atomic services since API version 12. 581 582**System capability**: SystemCapability.ArkUI.ArkUI.Full 583 584| Name | Type | Read Only| Optional| Description | 585| ------------- | ---------- | ---- | ---- | ------------------------ | 586| 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**| 587| 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**| 588| velocity | number | No| No| Hands-off velocity at the beginning of the animation. Unit: VP/S<br>Default value: **0**| 589 590## TabContentAnimatedTransition<sup>11+</sup> 591 592Provides the information about the custom tab page switching animation. 593 594**Widget capability**: This API can be used in ArkTS widgets since API version 11. 595 596**Atomic service API**: This API can be used in atomic services since API version 12. 597 598**System capability**: SystemCapability.ArkUI.ArkUI.Full 599 600| Name | Type | Mandatory | Description | 601| ------------- | ---------------------- | ---- |---------------------- | 602| 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**| 603| transition | (proxy: [TabContentTransitionProxy](#tabcontenttransitionproxy11)) => void | Yes| Content of the custom switching animation.| 604 605## TabContentTransitionProxy<sup>11+</sup> 606 607Implements 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. 608 609**Widget capability**: This API can be used in ArkTS widgets since API version 11. 610 611**Atomic service API**: This API can be used in atomic services since API version 12. 612 613**System capability**: SystemCapability.ArkUI.ArkUI.Full 614 615### Attributes 616 617| Name | Type | Read Only| Optional| Description | 618| ----- | ------- | ---- | ---- | --------------------------- | 619| from | number | No| No| Index of the starting page of the custom animation.| 620| to | number | No| No| Index of the target tab to switch to.| 621 622### finishTransition 623 624finishTransition(): void 625 626Notifies the **Tabs** component that the custom animation has finished playing. 627 628**Widget capability**: This API can be used in ArkTS widgets since API version 11. 629 630**Atomic service API**: This API can be used in atomic services since API version 12. 631 632**System capability**: SystemCapability.ArkUI.ArkUI.Full 633 634## TabsController 635 636Defines a tab controller, which is used to control switching of tabs. One **TabsController** cannot control multiple **Tabs** components. 637 638**Atomic service API**: This API can be used in atomic services since API version 11. 639 640**System capability**: SystemCapability.ArkUI.ArkUI.Full 641 642### Objects to Import 643 644```ts 645let controller: TabsController = new TabsController() 646``` 647 648### constructor 649 650constructor() 651 652A constructor used to create a **TabsController** object. 653 654**Atomic service API**: This API can be used in atomic services since API version 11. 655 656**System capability**: SystemCapability.ArkUI.ArkUI.Full 657 658### changeIndex 659 660changeIndex(value: number): void 661 662Switches to the specified tab. 663 664**Atomic service API**: This API can be used in atomic services since API version 11. 665 666**System capability**: SystemCapability.ArkUI.ArkUI.Full 667 668**Parameters** 669 670| Name | Type | Mandatory | Description | 671| ----- | ------ | ---- | ---------------------------------------- | 672| 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.| 673 674### preloadItems<sup>12+</sup> 675 676preloadItems(indices: Optional\<Array\<number>>): Promise\<void> 677 678Preloads 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. 679 680**Atomic service API**: This API can be used in atomic services since API version 12. 681 682**System capability**: SystemCapability.ArkUI.ArkUI.Full 683 684**Parameters** 685 686| Name | Type | Mandatory | Description | 687| ----- | ------ | ---- | ---------------------------------------- | 688| indices | Optional\<Array\<number>> | Yes| Array of indexes of the child nodes to preload.<br>The default value is an empty array.| 689 690**Return value** 691 692| Type | Description | 693| ------------------------------------------------------------ | ------------------------ | 694| Promise\<void> | Promise used to return the value.| 695 696**Error codes** 697 698For details about the error codes, see [Universal Error Codes](../../errorcode-universal.md). 699 700| ID | Error Message | 701| -------- | -------------------------------------------- | 702| 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. | 703 704## Example 705 706### Example 1 707 708This example uses **onChange** to implement the linkage between **tabBar** and **TabContent**. 709 710```ts 711// xxx.ets 712@Entry 713@Component 714struct TabsExample { 715 @State fontColor: string = '#182431' 716 @State selectedFontColor: string = '#007DFF' 717 @State currentIndex: number = 0 718 private controller: TabsController = new TabsController() 719 720 @Builder tabBuilder(index: number, name: string) { 721 Column() { 722 Text(name) 723 .fontColor(this.currentIndex === index ? this.selectedFontColor : this.fontColor) 724 .fontSize(16) 725 .fontWeight(this.currentIndex === index ? 500 : 400) 726 .lineHeight(22) 727 .margin({ top: 17, bottom: 7 }) 728 Divider() 729 .strokeWidth(2) 730 .color('#007DFF') 731 .opacity(this.currentIndex === index ? 1 : 0) 732 }.width('100%') 733 } 734 735 build() { 736 Column() { 737 Tabs({ barPosition: BarPosition.Start, index: this.currentIndex, controller: this.controller }) { 738 TabContent() { 739 Column().width('100%').height('100%').backgroundColor('#00CB87') 740 }.tabBar(this.tabBuilder(0, 'green')) 741 742 TabContent() { 743 Column().width('100%').height('100%').backgroundColor('#007DFF') 744 }.tabBar(this.tabBuilder(1, 'blue')) 745 746 TabContent() { 747 Column().width('100%').height('100%').backgroundColor('#FFBF00') 748 }.tabBar(this.tabBuilder(2, 'yellow')) 749 750 TabContent() { 751 Column().width('100%').height('100%').backgroundColor('#E67C92') 752 }.tabBar(this.tabBuilder(3, 'pink')) 753 } 754 .vertical(false) 755 .barMode(BarMode.Fixed) 756 .barWidth(360) 757 .barHeight(56) 758 .animationDuration(400) 759 .onChange((index: number) => { 760 this.currentIndex = index 761 }) 762 .width(360) 763 .height(296) 764 .margin({ top: 52 }) 765 .backgroundColor('#F1F3F5') 766 }.width('100%') 767 } 768} 769``` 770 771 772 773### Example 2 774 775This example uses **divider** to present dividers in different styles. 776 777```ts 778// xxx.ets 779@Entry 780@Component 781struct TabsDivider1 { 782 private controller1: TabsController = new TabsController() 783 @State dividerColor: string = 'red' 784 @State strokeWidth: number = 2 785 @State startMargin: number = 0 786 @State endMargin: number = 0 787 @State nullFlag: boolean = false 788 789 build() { 790 Column() { 791 Tabs({ controller: this.controller1 }) { 792 TabContent() { 793 Column().width('100%').height('100%').backgroundColor(Color.Pink) 794 }.tabBar('pink') 795 796 TabContent() { 797 Column().width('100%').height('100%').backgroundColor(Color.Yellow) 798 }.tabBar('yellow') 799 800 TabContent() { 801 Column().width('100%').height('100%').backgroundColor(Color.Blue) 802 }.tabBar('blue') 803 804 TabContent() { 805 Column().width('100%').height('100%').backgroundColor(Color.Green) 806 }.tabBar('green') 807 808 TabContent() { 809 Column().width('100%').height('100%').backgroundColor(Color.Red) 810 }.tabBar('red') 811 } 812 .vertical(true) 813 .scrollable(true) 814 .barMode(BarMode.Fixed) 815 .barWidth(70) 816 .barHeight(200) 817 .animationDuration(400) 818 .onChange((index: number) => { 819 console.info(index.toString()) 820 }) 821 .height('200vp') 822 .margin({ bottom: '12vp' }) 823 .divider(this.nullFlag ? null : { 824 strokeWidth: this.strokeWidth, 825 color: this.dividerColor, 826 startMargin: this.startMargin, 827 endMargin: this.endMargin 828 }) 829 830 Button ('Regular Divider').width('100%').margin({ bottom: '12vp'}) 831 .onClick(() => { 832 this.nullFlag = false; 833 this.strokeWidth = 2; 834 this.dividerColor = 'red'; 835 this.startMargin = 0; 836 this.endMargin = 0; 837 }) 838 Button('Empty Divider').width('100%').margin({ bottom: '12vp' }) 839 .onClick(() => { 840 this.nullFlag = true 841 }) 842 Button('Change to Blue').width('100%').margin({ bottom: '12vp'}) 843 .onClick(() => { 844 this.dividerColor = 'blue' 845 }) 846 Button('Increase Width').width('100%').margin({ bottom: '12vp' }) 847 .onClick(() => { 848 this.strokeWidth += 2 849 }) 850 Button('Decrease Width').width('100%').margin({ bottom: '12vp'}) 851 .onClick(() => { 852 if (this.strokeWidth > 2) { 853 this.strokeWidth -= 2 854 } 855 }) 856 Button ('Increase Top Margin').width ('100%').margin ({ bottom:'12vp'}) 857 .onClick(() => { 858 this.startMargin += 2 859 }) 860 Button ('Decrease Top Margin').width ('100%').margin ({ bottom:'12vp' }) 861 .onClick(() => { 862 if (this.startMargin > 2) { 863 this.startMargin -= 2 864 } 865 }) 866 Button ('Increase Bottom Margin').width ('100%').margin ({ bottom:'12vp'}) 867 .onClick(() => { 868 this.endMargin += 2 869 }) 870 Button ('Decrease Bottom Margin').width ('100%').margin ({ bottom:'12vp' }) 871 .onClick(() => { 872 if (this.endMargin > 2) { 873 this.endMargin -= 2 874 } 875 }) 876 }.padding({ top: '24vp', left: '24vp', right: '24vp' }) 877 } 878} 879``` 880 881 882 883### Example 3 884 885This example uses **fadingEdge** to specify whether to fade out tabs. 886 887```ts 888// xxx.ets 889@Entry 890@Component 891struct TabsOpaque { 892 @State message: string = 'Hello World' 893 private controller: TabsController = new TabsController() 894 private controller1: TabsController = new TabsController() 895 @State selfFadingFade: boolean = true; 896 897 build() { 898 Column() { 899 Button (Set Tab to Fade').width ('100%').margin ({bottom: '12vp'}) 900 .onClick((event?: ClickEvent) => { 901 this.selfFadingFade = true; 902 }) 903 Button (Set Tab Not to Fade').width ('100%').margin ({bottom: '12vp'}) 904 .onClick((event?: ClickEvent) => { 905 this.selfFadingFade = false; 906 }) 907 Tabs({ barPosition: BarPosition.End, controller: this.controller }) { 908 TabContent() { 909 Column().width('100%').height('100%').backgroundColor(Color.Pink) 910 }.tabBar('pink') 911 912 TabContent() { 913 Column().width('100%').height('100%').backgroundColor(Color.Yellow) 914 }.tabBar('yellow') 915 916 TabContent() { 917 Column().width('100%').height('100%').backgroundColor(Color.Blue) 918 }.tabBar('blue') 919 920 TabContent() { 921 Column().width('100%').height('100%').backgroundColor(Color.Green) 922 }.tabBar('green') 923 924 TabContent() { 925 Column().width('100%').height('100%').backgroundColor(Color.Green) 926 }.tabBar('green') 927 928 TabContent() { 929 Column().width('100%').height('100%').backgroundColor(Color.Green) 930 }.tabBar('green') 931 932 TabContent() { 933 Column().width('100%').height('100%').backgroundColor(Color.Green) 934 }.tabBar('green') 935 936 TabContent() { 937 Column().width('100%').height('100%').backgroundColor(Color.Green) 938 }.tabBar('green') 939 } 940 .vertical(false) 941 .scrollable(true) 942 .barMode(BarMode.Scrollable) 943 .barHeight(80) 944 .animationDuration(400) 945 .onChange((index: number) => { 946 console.info(index.toString()) 947 }) 948 .fadingEdge(this.selfFadingFade) 949 .height('30%') 950 .width('100%') 951 952 Tabs({ barPosition: BarPosition.Start, controller: this.controller1 }) { 953 TabContent() { 954 Column().width('100%').height('100%').backgroundColor(Color.Pink) 955 }.tabBar('pink') 956 957 TabContent() { 958 Column().width('100%').height('100%').backgroundColor(Color.Yellow) 959 }.tabBar('yellow') 960 961 TabContent() { 962 Column().width('100%').height('100%').backgroundColor(Color.Blue) 963 }.tabBar('blue') 964 965 TabContent() { 966 Column().width('100%').height('100%').backgroundColor(Color.Green) 967 }.tabBar('green') 968 969 TabContent() { 970 Column().width('100%').height('100%').backgroundColor(Color.Green) 971 }.tabBar('green') 972 973 TabContent() { 974 Column().width('100%').height('100%').backgroundColor(Color.Green) 975 }.tabBar('green') 976 } 977 .vertical(true) 978 .scrollable(true) 979 .barMode(BarMode.Scrollable) 980 .barHeight(200) 981 .barWidth(80) 982 .animationDuration(400) 983 .onChange((index: number) => { 984 console.info(index.toString()) 985 }) 986 .fadingEdge(this.selfFadingFade) 987 .height('30%') 988 .width('100%') 989 } 990 .padding({ top: '24vp', left: '24vp', right: '24vp' }) 991 } 992} 993``` 994 995 996 997### Example 4 998 999This example uses **barOverlap** to specify whether the tab bar is superimposed on the **TabContent** component after having its background blurred. 1000 1001```ts 1002// xxx.ets 1003@Entry 1004@Component 1005struct barBackgroundColorTest { 1006 private controller: TabsController = new TabsController() 1007 @State barOverlap: boolean = true; 1008 @State barBackgroundColor: string = '#88888888'; 1009 1010 build() { 1011 Column() { 1012 Button ("Change barOverlap").width ('100%').margin ({ bottom:'12vp'}) 1013 .onClick((event?: ClickEvent) => { 1014 if (this.barOverlap) { 1015 this.barOverlap = false; 1016 } else { 1017 this.barOverlap = true; 1018 } 1019 }) 1020 1021 Tabs({ barPosition: BarPosition.Start, index: 0, controller: this.controller }) { 1022 TabContent() { 1023 Column() { 1024 Text(`barOverlap ${this.barOverlap}`).fontSize(16).margin({ top: this.barOverlap ? '56vp' : 0 }) 1025 Text(`barBackgroundColor ${this.barBackgroundColor}`).fontSize(16) 1026 }.width('100%').width('100%').height('100%') 1027 .backgroundColor(Color.Pink) 1028 } 1029 .tabBar(new BottomTabBarStyle($r('sys.media.ohos_app_icon'), "1")) 1030 1031 TabContent() { 1032 Column() { 1033 Text(`barOverlap ${this.barOverlap}`).fontSize(16).margin({ top: this.barOverlap ? '56vp' : 0 }) 1034 Text(`barBackgroundColor ${this.barBackgroundColor}`).fontSize(16) 1035 }.width('100%').width('100%').height('100%') 1036 .backgroundColor(Color.Yellow) 1037 } 1038 .tabBar(new BottomTabBarStyle($r('sys.media.ohos_app_icon'), "2")) 1039 1040 TabContent() { 1041 Column() { 1042 Text(`barOverlap ${this.barOverlap}`).fontSize(16).margin({ top: this.barOverlap ? '56vp' : 0 }) 1043 Text(`barBackgroundColor ${this.barBackgroundColor}`).fontSize(16) 1044 }.width('100%').width('100%').height('100%') 1045 .backgroundColor(Color.Green) 1046 } 1047 .tabBar(new BottomTabBarStyle($r('sys.media.ohos_app_icon'), "3")) 1048 } 1049 .vertical(false) 1050 .barMode(BarMode.Fixed) 1051 .height('60%') 1052 .barOverlap(this.barOverlap) 1053 .scrollable(true) 1054 .animationDuration(10) 1055 .barBackgroundColor(this.barBackgroundColor) 1056 } 1057 .height(500) 1058 .padding({ top: '24vp', left: '24vp', right: '24vp' }) 1059 } 1060} 1061``` 1062 1063 1064 1065### Example 5 1066 1067This example uses **barGridAlign** to set the visible area of the tab bar in grid mode. 1068 1069```ts 1070// xxx.ets 1071@Entry 1072@Component 1073struct TabsExample5 { 1074 private controller: TabsController = new TabsController() 1075 @State gridMargin: number = 10 1076 @State gridGutter: number = 10 1077 @State sm: number = -2 1078 @State clickedContent: string = ""; 1079 1080 build() { 1081 Column() { 1082 Row() { 1083 Button("gridMargin+10 " + this.gridMargin) 1084 .width('47%') 1085 .height(50) 1086 .margin({ top: 5 }) 1087 .onClick((event?: ClickEvent) => { 1088 this.gridMargin += 10 1089 }) 1090 .margin({ right: '6%', bottom: '12vp' }) 1091 Button("gridMargin-10 " + this.gridMargin) 1092 .width('47%') 1093 .height(50) 1094 .margin({ top: 5 }) 1095 .onClick((event?: ClickEvent) => { 1096 this.gridMargin -= 10 1097 }) 1098 .margin({ bottom: '12vp' }) 1099 } 1100 1101 Row() { 1102 Button("gridGutter+10 " + this.gridGutter) 1103 .width('47%') 1104 .height(50) 1105 .margin({ top: 5 }) 1106 .onClick((event?: ClickEvent) => { 1107 this.gridGutter += 10 1108 }) 1109 .margin({ right: '6%', bottom: '12vp' }) 1110 Button("gridGutter-10 " + this.gridGutter) 1111 .width('47%') 1112 .height(50) 1113 .margin({ top: 5 }) 1114 .onClick((event?: ClickEvent) => { 1115 this.gridGutter -= 10 1116 }) 1117 .margin({ bottom: '12vp' }) 1118 } 1119 1120 Row() { 1121 Button("sm+2 " + this.sm) 1122 .width('47%') 1123 .height(50) 1124 .margin({ top: 5 }) 1125 .onClick((event?: ClickEvent) => { 1126 this.sm += 2 1127 }) 1128 .margin({ right: '6%' }) 1129 Button("sm-2 " + this.sm).width('47%').height(50).margin({ top: 5 }) 1130 .onClick((event?: ClickEvent) => { 1131 this.sm -= 2 1132 }) 1133 } 1134 1135 Text ("Tab clicks: "+ this.clickedContent).width ('100%').height (200).margin ({ top: 5 }) 1136 1137 1138 Tabs({ barPosition: BarPosition.End, controller: this.controller }) { 1139 TabContent() { 1140 Column().width('100%').height('100%').backgroundColor(Color.Pink) 1141 }.tabBar(BottomTabBarStyle.of($r("sys.media.ohos_app_icon"), "1")) 1142 1143 TabContent() { 1144 Column().width('100%').height('100%').backgroundColor(Color.Green) 1145 }.tabBar(BottomTabBarStyle.of($r("sys.media.ohos_app_icon"), "2")) 1146 1147 TabContent() { 1148 Column().width('100%').height('100%').backgroundColor(Color.Blue) 1149 }.tabBar(BottomTabBarStyle.of($r("sys.media.ohos_app_icon"), "3")) 1150 } 1151 .width('350vp') 1152 .animationDuration(300) 1153 .height('60%') 1154 .barGridAlign({ sm: this.sm, margin: this.gridMargin, gutter: this.gridGutter }) 1155 .backgroundColor(0xf1f3f5) 1156 .onTabBarClick((index: number) => { 1157 this.clickedContent += "index " + index + " was clicked\n"; 1158 }) 1159 } 1160 .width('100%') 1161 .height(500) 1162 .margin({ top: 5 }) 1163 .padding('10vp') 1164 } 1165} 1166``` 1167 1168 1169 1170### Example 6 1171 1172This example implements the **ScrollableBarModeOptions** parameter of **barMode**. This parameter is effective only in **Scrollable** mode. 1173 1174```ts 1175// xxx.ets 1176@Entry 1177@Component 1178struct TabsExample6 { 1179 private controller: TabsController = new TabsController() 1180 @State scrollMargin: number = 0 1181 @State layoutStyle: LayoutStyle = LayoutStyle.ALWAYS_CENTER 1182 @State text: string = "Text" 1183 1184 build() { 1185 Column() { 1186 Row() { 1187 Button("scrollMargin+10 " + this.scrollMargin) 1188 .width('47%') 1189 .height(50) 1190 .margin({ top: 5 }) 1191 .onClick((event?: ClickEvent) => { 1192 this.scrollMargin += 10 1193 }) 1194 .margin({ right: '6%', bottom: '12vp' }) 1195 Button("scrollMargin-10 " + this.scrollMargin) 1196 .width('47%') 1197 .height(50) 1198 .margin({ top: 5 }) 1199 .onClick((event?: ClickEvent) => { 1200 this.scrollMargin -= 10 1201 }) 1202 .margin({ bottom: '12vp' }) 1203 } 1204 1205 Row() { 1206 Button ("Add Text") 1207 .width('47%') 1208 .height(50) 1209 .margin({ top: 5 }) 1210 .onClick((event?: ClickEvent) => { 1211 this.text += 'Add Text' 1212 }) 1213 .margin({ right: '6%', bottom: '12vp' }) 1214 Button ("Reset Text") 1215 .width('47%') 1216 .height(50) 1217 .margin({ top: 5 }) 1218 .onClick((event?: ClickEvent) => { 1219 this.text = "Text" 1220 }) 1221 .margin({ bottom: '12vp' }) 1222 } 1223 1224 Row() { 1225 Button("layoutStyle.ALWAYS_CENTER") 1226 .width('100%') 1227 .height(50) 1228 .margin({ top: 5 }) 1229 .fontSize(15) 1230 .onClick((event?: ClickEvent) => { 1231 this.layoutStyle = LayoutStyle.ALWAYS_CENTER; 1232 }) 1233 .margin({ bottom: '12vp' }) 1234 } 1235 1236 Row() { 1237 Button("layoutStyle.ALWAYS_AVERAGE_SPLIT") 1238 .width('100%') 1239 .height(50) 1240 .margin({ top: 5 }) 1241 .fontSize(15) 1242 .onClick((event?: ClickEvent) => { 1243 this.layoutStyle = LayoutStyle.ALWAYS_AVERAGE_SPLIT; 1244 }) 1245 .margin({ bottom: '12vp' }) 1246 } 1247 1248 Row() { 1249 Button("layoutStyle.SPACE_BETWEEN_OR_CENTER") 1250 .width('100%') 1251 .height(50) 1252 .margin({ top: 5 }) 1253 .fontSize(15) 1254 .onClick((event?: ClickEvent) => { 1255 this.layoutStyle = LayoutStyle.SPACE_BETWEEN_OR_CENTER; 1256 }) 1257 .margin({ bottom: '12vp' }) 1258 } 1259 1260 Tabs({ barPosition: BarPosition.End, controller: this.controller }) { 1261 TabContent() { 1262 Column().width('100%').height('100%').backgroundColor(Color.Pink) 1263 }.tabBar(SubTabBarStyle.of(this.text)) 1264 1265 TabContent() { 1266 Column().width('100%').height('100%').backgroundColor(Color.Green) 1267 }.tabBar(SubTabBarStyle.of(this.text)) 1268 1269 TabContent() { 1270 Column().width('100%').height('100%').backgroundColor(Color.Blue) 1271 }.tabBar(SubTabBarStyle.of(this.text)) 1272 } 1273 .animationDuration(300) 1274 .height('60%') 1275 .backgroundColor(0xf1f3f5) 1276 .barMode(BarMode.Scrollable, { margin: this.scrollMargin, nonScrollableLayoutStyle: this.layoutStyle }) 1277 } 1278 .width('100%') 1279 .height(500) 1280 .margin({ top: 5 }) 1281 .padding('24vp') 1282 } 1283} 1284``` 1285 1286 1287 1288### Example 7 1289 1290This example uses **customContentTransition** to implement a custom tab switching animation. 1291 1292```ts 1293// xxx.ets 1294interface itemType { 1295 text: string, 1296 backgroundColor: Color 1297} 1298 1299@Entry 1300@Component 1301struct TabsCustomAnimationExample { 1302 @State data: itemType[] = [ 1303 { 1304 text: 'Red', 1305 backgroundColor: Color.Red 1306 }, 1307 { 1308 text: 'Yellow', 1309 backgroundColor: Color.Yellow 1310 }, 1311 { 1312 text: 'Blue', 1313 backgroundColor: Color.Blue 1314 }] 1315 @State opacityList: number[] = [] 1316 @State scaleList: number[] = [] 1317 1318 private durationList: number[] = [] 1319 private timeoutList: number[] = [] 1320 private customContentTransition: (from: number, to: number) => TabContentAnimatedTransition = (from: number, to: number) => { 1321 let tabContentAnimatedTransition = { 1322 timeout: this.timeoutList[from], 1323 transition: (proxy: TabContentTransitionProxy) => { 1324 this.scaleList[from] = 1.0 1325 this.scaleList[to] = 0.5 1326 this.opacityList[from] = 1.0 1327 this.opacityList[to] = 0.5 1328 animateTo({ 1329 duration: this.durationList[from], 1330 onFinish: () => { 1331 proxy.finishTransition() 1332 } 1333 }, () => { 1334 this.scaleList[from] = 0.5 1335 this.scaleList[to] = 1.0 1336 this.opacityList[from] = 0.5 1337 this.opacityList[to] = 1.0 1338 }) 1339 } 1340 } as TabContentAnimatedTransition 1341 return tabContentAnimatedTransition 1342 } 1343 1344 aboutToAppear(): void { 1345 let duration = 1000 1346 let timeout = 1000 1347 for (let i = 1; i <= this.data.length; i++) { 1348 this.opacityList.push(1.0) 1349 this.scaleList.push(1.0) 1350 this.durationList.push(duration * i) 1351 this.timeoutList.push(timeout * i) 1352 } 1353 } 1354 1355 build() { 1356 Column() { 1357 Tabs() { 1358 ForEach(this.data, (item: itemType, index: number) => { 1359 TabContent() {} 1360 .tabBar(item.text) 1361 .backgroundColor(item.backgroundColor) 1362 // Customize the opacity and scale animation. 1363 .opacity(this.opacityList[index]) 1364 .scale({ x: this.scaleList[index], y: this.scaleList[index] }) 1365 }) 1366 } 1367 .backgroundColor(0xf1f3f5) 1368 .width('100%') 1369 .height(500) 1370 .customContentTransition(this.customContentTransition) 1371 } 1372 } 1373} 1374``` 1375 1376 1377### Example 8 1378 1379This example uses **onContentWillChange** to switch to a new page on swiping. 1380 1381```ts 1382//xxx.ets 1383@Entry 1384@Component 1385struct TabsExample { 1386 @State currentIndex: number = 2 1387 private controller: TabsController = new TabsController() 1388 @Builder tabBuilder(title: string,targetIndex: number) { 1389 Column(){ 1390 Text(title).fontColor(this.currentIndex === targetIndex ? '#1698CE' : '#6B6B6B') 1391 }.width('100%') 1392 .height(50) 1393 .justifyContent(FlexAlign.Center) 1394 } 1395 build() { 1396 Column() { 1397 Tabs({ barPosition: BarPosition.End, controller: this.controller, index: this.currentIndex }) { 1398 TabContent() { 1399 Column(){ 1400 Text('Content of the Home tab') 1401 }.width('100%').height('100%').backgroundColor('#00CB87').justifyContent(FlexAlign.Center) 1402 }.tabBar(this.tabBuilder('Home',0)) 1403 1404 TabContent() { 1405 Column(){ 1406 Text('Content of the Discover tab') 1407 }.width('100%').height('100%').backgroundColor('#007DFF').justifyContent(FlexAlign.Center) 1408 }.tabBar (this.tabBuilder ('Discover', 1)) 1409 1410 TabContent() { 1411 Column(){ 1412 Text('Content of the Recommended tab') 1413 }.width('100%').height('100%').backgroundColor('#FFBF00').justifyContent(FlexAlign.Center) 1414 }.tabBar (this.tabBuilder ('Recommended', 2)) 1415 1416 TabContent() { 1417 Column(){ 1418 Text('Content of the Me tab') 1419 }.width('100%').height('100%').backgroundColor('#E67C92').justifyContent(FlexAlign.Center) 1420 }.tabBar(this.tabBuilder('Me',3)) 1421 } 1422 .vertical(false) 1423 .barMode(BarMode.Fixed) 1424 .barWidth(360) 1425 .barHeight(60) 1426 .animationDuration(0) 1427 .onChange((index: number) => { 1428 this.currentIndex = index 1429 }) 1430 .width(360) 1431 .height(600) 1432 .backgroundColor('#F1F3F5') 1433 .scrollable(true) 1434 .onContentWillChange((currentIndex, comingIndex) => { 1435 if (comingIndex == 2) { 1436 return false 1437 } 1438 return true 1439 }) 1440 1441 Button('Change Index').width('50%').margin({ top: 20 }) 1442 .onClick(()=>{ 1443 this.currentIndex = (this.currentIndex + 1) % 4 1444 }) 1445 1446 Button('changeIndex').width('50%').margin({ top: 20 }) 1447 .onClick(()=>{ 1448 this.currentIndex = (this.currentIndex + 1) % 4 1449 this.controller.changeIndex(this.currentIndex) 1450 }) 1451 }.width('100%') 1452 } 1453} 1454``` 1455 1456 1457### Example 9 1458 1459This example uses **onChange**, **onAnimationStart**, **onAnimationEnd**, and **onGestureSwipe** APIs to customize the tab bar switching animation. 1460 1461```ts 1462// xxx.ets 1463import { ComponentUtils } from '@kit.ArkUI' 1464 1465@Entry 1466@Component 1467struct TabsExample { 1468 @State currentIndex: number = 0 1469 @State animationDuration: number = 300 1470 @State indicatorLeftMargin: number = 0 1471 @State indicatorWidth: number = 0 1472 private tabsWidth: number = 0 1473 private componentUtils: ComponentUtils = this.getUIContext().getComponentUtils() 1474 1475 @Builder 1476 tabBuilder(index: number, name: string) { 1477 Column() { 1478 Text(name) 1479 .fontSize(16) 1480 .fontColor(this.currentIndex === index ? '#007DFF' : '#182431') 1481 .fontWeight(this.currentIndex === index ? 500 : 400) 1482 .id(index.toString()) 1483 .onAreaChange((oldValue: Area,newValue: Area) => { 1484 if (this.currentIndex === index && (this.indicatorLeftMargin === 0 || this.indicatorWidth === 0)){ 1485 if (newValue.position.x != undefined) { 1486 let positionX = Number.parseFloat(newValue.position.x.toString()) 1487 this.indicatorLeftMargin = Number.isNaN(positionX) ? 0 : positionX 1488 } 1489 let width = Number.parseFloat(newValue.width.toString()) 1490 this.indicatorWidth = Number.isNaN(width) ? 0 : width 1491 } 1492 }) 1493 }.width('100%') 1494 } 1495 1496 build() { 1497 Stack({ alignContent: Alignment.TopStart }) { 1498 Tabs({ barPosition: BarPosition.Start }) { 1499 TabContent() { 1500 Column().width('100%').height('100%').backgroundColor('#00CB87') 1501 }.tabBar(this.tabBuilder(0, 'green')) 1502 1503 TabContent() { 1504 Column().width('100%').height('100%').backgroundColor('#007DFF') 1505 }.tabBar(this.tabBuilder(1, 'blue')) 1506 1507 TabContent() { 1508 Column().width('100%').height('100%').backgroundColor('#FFBF00') 1509 }.tabBar(this.tabBuilder(2, 'yellow')) 1510 1511 TabContent() { 1512 Column().width('100%').height('100%').backgroundColor('#E67C92') 1513 }.tabBar(this.tabBuilder(3, 'pink')) 1514 } 1515 .onAreaChange((oldValue: Area,newValue: Area)=> { 1516 let width = Number.parseFloat(newValue.width.toString()) 1517 this.tabsWidth = Number.isNaN(width) ? 0 : width 1518 }) 1519 .barWidth('100%') 1520 .barHeight(56) 1521 .width('100%') 1522 .height(296) 1523 .backgroundColor('#F1F3F5') 1524 .animationDuration(this.animationDuration) 1525 .onChange((index: number) => { 1526 this.currentIndex = index // Listen for index changes to switch the tab page content. 1527 }) 1528 .onAnimationStart((index: number, targetIndex: number, event: TabsAnimationEvent) => { 1529 // Triggered when the tab switching animation starts. The underline moves with the active tab, along with a width gradient. 1530 this.currentIndex = targetIndex 1531 let targetIndexInfo = this.getTextInfo(targetIndex) 1532 this.startAnimateTo(this.animationDuration, targetIndexInfo.left, targetIndexInfo.width) 1533 }) 1534 .onAnimationEnd((index: number,event: TabsAnimationEvent) => { 1535 // Triggered when the tab switching animation ends. The underline animation stops. 1536 let currentIndicatorInfo = this.getCurrentIndicatorInfo(index,event) 1537 this.startAnimateTo(0,currentIndicatorInfo.left,currentIndicatorInfo.width) 1538 }) 1539 .onGestureSwipe((index: number,event: TabsAnimationEvent) => { 1540 // Triggered on a frame-by-frame basis when the tab is switched by a swipe. 1541 let currentIndicatorInfo = this.getCurrentIndicatorInfo(index,event) 1542 this.currentIndex = currentIndicatorInfo.index 1543 this.indicatorLeftMargin = currentIndicatorInfo.left 1544 this.indicatorWidth = currentIndicatorInfo.width 1545 }) 1546 1547 Column() 1548 .height(2) 1549 .width(this.indicatorWidth) 1550 .margin({ left: this.indicatorLeftMargin, top:48}) 1551 .backgroundColor('#007DFF') 1552 }.width('100%') 1553 } 1554 1555 private getTextInfo(index: number): Record<string, number> { 1556 let rectangle = this.componentUtils.getRectangleById(index.toString()) 1557 return { 'left': px2vp(rectangle.windowOffset.x), 'width': px2vp(rectangle.size.width) } 1558 } 1559 1560 private getCurrentIndicatorInfo(index: number, event: TabsAnimationEvent): Record<string, number> { 1561 let nextIndex = index 1562 if (index > 0 && event.currentOffset > 0) { 1563 nextIndex-- 1564 } else if (index < 3 && event.currentOffset < 0) { 1565 nextIndex++ 1566 } 1567 let indexInfo = this.getTextInfo(index) 1568 let nextIndexInfo = this.getTextInfo(nextIndex) 1569 let swipeRatio = Math.abs(event.currentOffset / this.tabsWidth) 1570 let currentIndex = swipeRatio > 0.5 ? nextIndex : index // When the scroll distance exceeds half of the page, the tab bar switches to the next page. 1571 let currentLeft = indexInfo.left + (nextIndexInfo.left - indexInfo.left) * swipeRatio 1572 let currentWidth = indexInfo.width + (nextIndexInfo.width - indexInfo.width) * swipeRatio 1573 return { 'index': currentIndex, 'left': currentLeft, 'width': currentWidth } 1574 } 1575 1576 private startAnimateTo(duration: number, leftMargin: number, width: number) { 1577 animateTo({ 1578 duration: duration, // Animation duration. 1579 curve: Curve.Linear, // Animation curve. 1580 iterations: 1, // Number of playback times. 1581 playMode: PlayMode.Normal // Animation playback mode. 1582 onFinish: () => { 1583 console.info('play end') 1584 } 1585 }, () => { 1586 this.indicatorLeftMargin = leftMargin 1587 this.indicatorWidth = width 1588 }) 1589 } 1590} 1591``` 1592 1593 1594 1595### Example 10 1596 1597In this example, the **preloadItems** API is used to preload specified child nodes. 1598 1599```ts 1600// xxx.ets 1601import { BusinessError } from '@kit.BasicServicesKit' 1602 1603@Entry 1604@Component 1605struct TabsPreloadItems { 1606 @State currentIndex: number = 1 1607 private tabsController: TabsController = new TabsController() 1608 1609 build() { 1610 Column() { 1611 Tabs({ index: this.currentIndex, controller: this.tabsController }) { 1612 TabContent() { 1613 MyComponent({ color: '#00CB87' }) 1614 }.tabBar(SubTabBarStyle.of('green')) 1615 1616 TabContent() { 1617 MyComponent({ color: '#007DFF' }) 1618 }.tabBar(SubTabBarStyle.of('blue')) 1619 1620 TabContent() { 1621 MyComponent({ color: '#FFBF00' }) 1622 }.tabBar(SubTabBarStyle.of('yellow')) 1623 1624 TabContent() { 1625 MyComponent({ color: '#E67C92' }) 1626 }.tabBar(SubTabBarStyle.of('pink')) 1627 } 1628 .width(360) 1629 .height(296) 1630 .backgroundColor('#F1F3F5') 1631 .onChange((index: number) => { 1632 this.currentIndex = index 1633 }) 1634 1635 Button('preload items: [0, 2, 3]') 1636 .margin(5) 1637 .onClick(() => { 1638 // Preload child nodes 0, 2, and 3 to improve the performance when users swipe or click to switch to these nodes. 1639 this.tabsController.preloadItems([0, 2, 3]) 1640 .then(() => { 1641 console.info('preloadItems success.') 1642 }) 1643 .catch((error: BusinessError) => { 1644 console.error('preloadItems failed, error code: ' + error.code + ', error message: ' + error.message) 1645 }) 1646 }) 1647 } 1648 } 1649} 1650 1651@Component 1652struct MyComponent { 1653 private color: string = "" 1654 1655 aboutToAppear(): void { 1656 console.info('aboutToAppear backgroundColor:' + this.color) 1657 } 1658 1659 aboutToDisappear(): void { 1660 console.info('aboutToDisappear backgroundColor:' + this.color) 1661 } 1662 1663 build() { 1664 Column() 1665 .width('100%') 1666 .height('100%') 1667 .backgroundColor(this.color) 1668 } 1669} 1670``` 1671