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(options?: TabsOptions) 26 27Creates a **Tabs** component. 28 29**Atomic service API**: This API can be used in atomic services since API version 11. 30 31**System capability**: SystemCapability.ArkUI.ArkUI.Full 32 33**Parameters** 34 35| Name| Type | Mandatory| Description| 36| -------- | -------- | -------- | -------- | 37| options | [TabsOptions](#tabsoptions15) | No| Options of the **Tabs** component.| 38 39## TabsOptions<sup>15+</sup> 40 41Provides parameters for configuring the **Tabs** component, including tab positions, the current index of the displayed tab, the **Tabs** controller, and [universal attributes](ts-component-general-attributes.md) for the **TabBar**. 42 43**Atomic service API**: This API can be used in atomic services since API version 15. 44 45**System capability**: SystemCapability.ArkUI.ArkUI.Full 46 47| Name | Type | Mandatory | Description | 48| ----------- | --------------------------------- | ---- | ---------------------------------------- | 49| barPosition | [BarPosition](#barposition)| No | Position of the **Tabs** component.<br>Default value: **BarPosition.Start** | 50| 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).<br>When the **Tabs** component is rebuilt, system resources are switched (for example, system font or theme changes), or component attributes change, the **Tab** component will switch to the one specified by **index**. To prevent this behavior, you are advised to use two-way binding.| 51| controller | [TabsController](#tabscontroller) | No | Tab controller. | 52| barModifier<sup>15+</sup> | [CommonModifier](#commonmodifier15) | No | [Universal attributes](ts-component-general-attributes.md) of the tab bar.<br>**NOTE**<br>If this parameter is dynamically set to **undefined**, the current state will be preserved, and universal attributes will not be reset.<br>If the setting switches from one **CommonModifier** to another, overlapping attributes will be overwritten, while non-overlapping attributes will coexist without resetting the attributes of the previous **CommonModifier**.<br>The [barWidth](#barwidth), [barHeight](#barheight), [barBackgroundColor](#barbackgroundcolor10), and [barBackgroundBlurStyle](#barbackgroundblurstyle11) attributes of **Tabs** will overwrite the [width](ts-universal-attributes-size.md#width), [height](ts-universal-attributes-size.md#height), [backgroundColor](ts-universal-attributes-background.md#backgroundcolor), and [backgroundBlurStyle](ts-universal-attributes-background.md#backgroundblurstyle9) attributes of **CommonModifier**.<br>The [align](ts-universal-attributes-location.md#align) attribute works only in [BarMode.Scrollable](#barmode10-1) mode. In addition, for a horizontal **Tabs** component, it only takes effect when [nonScrollableLayoutStyle](#scrollablebarmodeoptions10) is set to an invalid value or is not set.<br>When set to the bottom tab style, [tabBar](ts-container-tabcontent.md#tabbar9) attribute of the [TabContent](ts-container-tabcontent.md) component does not support the dragging feature.| 53 54## BarPosition 55 56Enumerates the positions of the **Tabs** component. 57 58**Atomic service API**: This API can be used in atomic services since API version 11. 59 60**System capability**: SystemCapability.ArkUI.ArkUI.Full 61 62| Name | Description | 63| ----- | ------------------------------------------------------------ | 64| 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.| 65| 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.| 66 67 68## Attributes 69 70In addition to the [universal attributes](ts-component-general-attributes.md), the following attributes are supported. 71 72### vertical 73 74vertical(value: boolean) 75 76Sets whether to use vertical tabs. 77 78**Atomic service API**: This API can be used in atomic services since API version 11. 79 80**System capability**: SystemCapability.ArkUI.ArkUI.Full 81 82**Parameters** 83 84| Name| Type | Mandatory| Description | 85| ------ | ------- | ---- | ------------------------------------------------------------ | 86| 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.| 87 88### scrollable 89 90scrollable(value: boolean) 91 92Sets whether the tabs are scrollable. 93 94**Atomic service API**: This API can be used in atomic services since API version 11. 95 96**System capability**: SystemCapability.ArkUI.ArkUI.Full 97 98**Parameters** 99 100| Name| Type | Mandatory| Description | 101| ------ | ------- | ---- | ------------------------------------------------------------ | 102| value | boolean | Yes | Whether the tabs are scrollable.<br>**true** (default): The tabs are scrollable.<br> **false**: The tabs are not scrollable.| 103 104### barMode 105 106barMode(value: BarMode, options?: ScrollableBarModeOptions) 107 108Sets the tab bar layout mode. 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](#barmode) | Yes | Layout mode.<br>Default value: **BarMode.Fixed** | 119| 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.| 120 121### barMode<sup>10+</sup> 122 123barMode(value: BarMode.Fixed) 124 125Sets the tab bar layout mode to **BarMode.Fixed**. 126 127**Atomic service API**: This API can be used in atomic services since API version 11. 128 129**System capability**: SystemCapability.ArkUI.ArkUI.Full 130 131**Parameters** 132 133| Name | Type | Mandatory| Description | 134| -------- | -------------------------------- | ---- | ------------------------------------ | 135| 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). | 136 137### barMode<sup>10+</sup> 138 139barMode(value: BarMode.Scrollable, options: ScrollableBarModeOptions) 140 141Sets the tab bar layout mode to **BarMode.Scrollable**. 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 | [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. | 152| 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. | 153 154### barWidth 155 156barWidth(value: Length) 157 158Sets 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. 159 160**Atomic service API**: This API can be used in atomic services since API version 11. 161 162**System capability**: SystemCapability.ArkUI.ArkUI.Full 163 164**Parameters** 165 166| Name| Type | Mandatory| Description | 167| ------ | ----------------------------------------- | ---- | ------------------------------------------------------------ | 168| value | [Length](ts-types.md#length)<sup>8+</sup> | Yes | Width of the tab bar.<br>Default value:<br>If the tab bar has the **vertical** attribute set to **false** and does not have [SubTabBarStyle](ts-container-tabcontent.md#subtabbarstyle9) or [BottomTabBarStyle](ts-container-tabcontent.md#bottomtabbarstyle9) specified, 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.| 169 170### barHeight 171 172barHeight(value: Length) 173 174Sets 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. 175 176In 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. 177 178**Atomic service API**: This API can be used in atomic services since API version 11. 179 180**System capability**: SystemCapability.ArkUI.ArkUI.Full 181 182**Parameters** 183 184| Name| Type | Mandatory| Description | 185| ------ | ----------------------------------------- | ---- | ------------------------------------------------------------ | 186| 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.| 187 188### animationDuration 189 190animationDuration(value: number) 191 192Sets 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. 193 194**Atomic service API**: This API can be used in atomic services since API version 11. 195 196**System capability**: SystemCapability.ArkUI.ArkUI.Full 197 198**Parameters** 199 200| Name| Type | Mandatory| Description | 201| ------ | ------ | ---- | ------------------------------------------------------------ | 202| 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| 203 204### animationMode<sup>12+</sup> 205 206animationMode(mode: Optional\<AnimationMode\>) 207 208Sets the animation mode for tab switching initiated by clicking a specific tab or by calling the **changeIndex** API of **TabsController**. 209 210**Atomic service API**: This API can be used in atomic services since API version 12. 211 212**System capability**: SystemCapability.ArkUI.ArkUI.Full 213 214**Parameters** 215 216| Name| Type | Mandatory| Description | 217| ------ | ------ | ---- | ------------------------------------------------------------ | 218| mode | Optional\<[AnimationMode](#animationmode12)\>| Yes | Animation mode for tab switching initiated by clicking a specific tab or by calling the **changeIndex** API of **TabsController**.<br>Default value: **AnimationMode.CONTENT_FIRST**, which means the target page content is loaded first, followed by the animation.| 219 220### barPosition<sup>9+</sup> 221 222barPosition(value: BarPosition) 223 224Sets the position of the **Tabs** component. 225 226**Atomic service API**: This API can be used in atomic services since API version 11. 227 228**System capability**: SystemCapability.ArkUI.ArkUI.Full 229 230**Parameters** 231 232| Name| Type | Mandatory| Description | 233| ----- | ---------------------------------- | ---- | -------------------- | 234| value | [BarPosition](#barposition)| Yes | Position of the **Tabs** component.<br>Default value: **BarPosition.Start** | 235 236### divider<sup>10+</sup> 237 238divider(value: DividerStyle | null) 239 240Sets the divider between the **TabBar** and **TabContent** components. 241 242**Atomic service API**: This API can be used in atomic services since API version 11. 243 244**System capability**: SystemCapability.ArkUI.ArkUI.Full 245 246**Parameters** 247 248| Name| Type | Mandatory| Description | 249| ------ | --------------------------------------------------------- | ---- | ------------------------------------------------------------ | 250| 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.| 251 252### fadingEdge<sup>10+</sup> 253 254fadingEdge(value: boolean) 255 256Sets 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. 257 258**Atomic service API**: This API can be used in atomic services since API version 11. 259 260**System capability**: SystemCapability.ArkUI.ArkUI.Full 261 262**Parameters** 263 264| Name| Type | Mandatory| Description | 265| ------ | ------- | ---- | -------------------------------------------------- | 266| value | boolean | Yes | Whether the tab fades out when it exceeds the container width.<br>Default value: **true**| 267 268### barOverlap<sup>10+</sup> 269 270barOverlap(value: boolean) 271 272Sets whether the tab bar is superimposed on the **TabContent** component after having its background blurred. 273 274**Atomic service API**: This API can be used in atomic services since API version 11. 275 276**System capability**: SystemCapability.ArkUI.ArkUI.Full 277 278**Parameters** 279 280| Name| Type | Mandatory| Description | 281| ------ | ------- | ---- | ------------------------------------------------------------ | 282| 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**| 283 284### barBackgroundColor<sup>10+</sup> 285 286barBackgroundColor(value: ResourceColor) 287 288Background color of the tab bar. 289 290**Atomic service API**: This API can be used in atomic services since API version 11. 291 292**System capability**: SystemCapability.ArkUI.ArkUI.Full 293 294**Parameters** 295 296| Name| Type | Mandatory| Description | 297| ------ | ------------------------------------------ | ---- | ------------------------------------ | 298| value | [ResourceColor](ts-types.md#resourcecolor) | Yes | Background color of the tab bar.<br>Default value: **Color.Transparent**| 299 300### barBackgroundBlurStyle<sup>11+</sup> 301 302barBackgroundBlurStyle(value: BlurStyle) 303 304Sets the background blur style of the tab bar. 305 306**Atomic service API**: This API can be used in atomic services since API version 11. 307 308**System capability**: SystemCapability.ArkUI.ArkUI.Full 309 310**Parameters** 311 312| Name| Type | Mandatory| Description | 313| ------ | -------------------------------------------- | ---- | ---------------------------------------- | 314| value | [BlurStyle](ts-universal-attributes-background.md#blurstyle9) | Yes | Background blur style of the tab bar.<br>Default value: **BlurStyle.NONE**| 315 316### barGridAlign<sup>10+</sup> 317 318barGridAlign(value: BarGridColumnOptions) 319 320Sets 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). 321 322**Atomic service API**: This API can be used in atomic services since API version 11. 323 324**System capability**: SystemCapability.ArkUI.ArkUI.Full 325 326**Parameters** 327 328| Name| Type | Mandatory| Description | 329| ------ | ------------------------------------------------------- | ---- | ---------------------------------- | 330| value | [BarGridColumnOptions](#bargridcolumnoptions10) | Yes | Visible area of the tab bar in grid mode.| 331 332### edgeEffect<sup>12+</sup> 333 334edgeEffect(edgeEffect: Optional<EdgeEffect>) 335 336Sets the edge effect used when the boundary of the scrolling area is reached. 337 338**Atomic service API**: This API can be used in atomic services since API version 12. 339 340**System capability**: SystemCapability.ArkUI.ArkUI.Full 341 342**Parameters** 343 344| Name| Type | Mandatory| Description | 345| ------ | --------------------------------------------- | ---- | -------------------------------------------- | 346| 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**| 347 348### pageFlipMode<sup>15+</sup> 349 350pageFlipMode(mode: Optional\<PageFlipMode>) 351 352Sets the mode for flipping pages using the mouse wheel. 353 354**Atomic service API**: This API can be used in atomic services since API version 15. 355 356**System capability**: SystemCapability.ArkUI.ArkUI.Full 357 358**Parameters** 359 360| Name| Type | Mandatory| Description | 361| ------ | ----------------------------------------------------------- | ---- | ------------------------------------------------------------ | 362| mode | Optional\<[PageFlipMode](ts-appendix-enums.md#pageflipmode15)> | Yes | Mode for flipping pages using the mouse wheel.<br>Default value: **PageFlipMode.CONTINUOUS**| 363 364## DividerStyle<sup>10+</sup> 365 366Describes the divider style. 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| 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 | 375| color | [ResourceColor](ts-types.md#resourcecolor) | No | Color of the divider.<br>Default value: **#33182431** | 376| 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| 377| 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| 378 379## BarGridColumnOptions<sup>10+</sup> 380 381Implements 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. 382 383**Atomic service API**: This API can be used in atomic services since API version 11. 384 385**System capability**: SystemCapability.ArkUI.ArkUI.Full 386 387| Name | Type | Mandatory | Description | 388| ----------- | ---------------------------------------- | ---- | ---------------------------------------- | 389| 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 | 390| 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 | 391| 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.| 392| 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.| 393| 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.| 394 395## ScrollableBarModeOptions<sup>10+</sup> 396 397Implements a **ScrollableBarModeOptions** object. 398 399**Atomic service API**: This API can be used in atomic services since API version 11. 400 401**System capability**: SystemCapability.ArkUI.ArkUI.Full 402 403| Name | Type | Mandatory | Description | 404| ----------- | ---------------------------------------- | ---- | ---------------------------------------- | 405| 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 | 406| nonScrollableLayoutStyle | [LayoutStyle](#layoutstyle10) | No | Tab layout mode of the tab bar when not scrolling in scrollable mode.<br>Default value: **LayoutStyle.ALWAYS_CENTER** | 407 408## BarMode 409 410Enumerates layout modes of the tab bar. 411 412**Atomic service API**: This API can be used in atomic services since API version 11. 413 414**System capability**: SystemCapability.ArkUI.ArkUI.Full 415 416| Name | Value| Description | 417| ---------- | -- | ---------------------------------------- | 418| 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.| 419| 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).| 420 421## AnimationMode<sup>12+</sup> 422 423Enumerates the animation modes for switching between tabs. 424 425**System capability**: SystemCapability.ArkUI.ArkUI.Full 426 427| Name | Value | Description | 428| ------------- | ---- | ------------------------------------------------------------ | 429| CONTENT_FIRST | 0 | Load the content of the target page before starting the switching animation.<br>**Atomic service API**: This API can be used in atomic services since API version 12.| 430| 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**.<br>**Atomic service API**: This API can be used in atomic services since API version 12.| 431| NO_ANIMATION | 2 | Disable the default switching animation. This value is ineffective when **TabContent** is switched using the **changeIndex** API of **TabsController**.<br>To disable the animation under this scenario, set **animationDuration** to **0**.<br>**Atomic service API**: This API can be used in atomic services since API version 12.| 432| CONTENT_FIRST_WITH_JUMP<sup>15+</sup> | 3 | Load the content of the target page first, then jump to the vicinity of the target page without animation, and finally jump to the target page with animation.<br>**Atomic service API**: This API can be used in atomic services since API version 15.| 433| ACTION_FIRST_WITH_JUMP<sup>15+</sup> | 4 | Jump to the vicinity of the target page without animation first, then jump to the target page with animation, and finally load the content of the target page. For the settings to take effect, the height and width of tabs must be set to **auto**.<br>**Atomic service API**: This API can be used in atomic services since API version 15.| 434 435## LayoutStyle<sup>10+</sup> 436 437Enumerates the tab layout styles of the tab bar when not scrolling in scrollable mode. 438 439**Atomic service API**: This API can be used in atomic services since API version 11. 440 441**System capability**: SystemCapability.ArkUI.ArkUI.Full 442 443| Name | Value| Description | 444| ---------- | -- | ---------------------------------------- | 445| 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.| 446| 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.| 447| 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.| 448 449## CommonModifier<sup>15+</sup> 450 451type CommonModifier = CommonModifier 452 453Defines a parameter object for the **Tabs** component. 454 455**Atomic service API**: This API can be used in atomic services since API version 15. 456 457**System capability**: SystemCapability.ArkUI.ArkUI.Full 458 459| Type | Description | 460| ---------- | ---------------------------------------- | 461| [CommonModifier](ts-universal-attributes-attribute-modifier.md#attributemodifier) | Universal attributes for the tab bar.| 462 463## Events 464 465In addition to the [universal events](ts-component-general-events.md), the following events are supported. 466 467### onChange 468 469onChange(event: (index: number) => void) 470 471Triggered when a tab is switched. 472 473This event is triggered when any of the following conditions is met: 474 4751. The swiping animation is completed, followed by tab switching. 476 4772. The [Controller](#tabscontroller) API is called. 478 4793. The attribute value is updated using a [state variable](../../../quick-start/arkts-state.md). 480 4814. A tab is clicked. 482 483> **NOTE** 484> 485> 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). 486 487**Atomic service API**: This API can be used in atomic services since API version 11. 488 489**System capability**: SystemCapability.ArkUI.ArkUI.Full 490 491**Parameters** 492 493| Name| Type | Mandatory| Description | 494| ------ | ------ | ---- | ------------------------------------ | 495| index | number | Yes | Index of the clicked tab. The index starts from 0.| 496 497### onTabBarClick<sup>10+</sup> 498 499onTabBarClick(event: (index: number) => void) 500 501Triggered when a tab is clicked. 502 503**Atomic service API**: This API can be used in atomic services since API version 11. 504 505**System capability**: SystemCapability.ArkUI.ArkUI.Full 506 507**Parameters** 508 509| Name| Type | Mandatory| Description | 510| ------ | ------ | ---- | ------------------------------------ | 511| event | [Callback](./ts-types.md#callback12)\<number> | Yes | Index of the clicked tab. The index starts from 0.| 512 513### onAnimationStart<sup>11+</sup> 514 515onAnimationStart(handler: OnTabsAnimationStartCallback) 516 517Triggered when the tab switching animation starts. This callback is not triggered when **animationDuration** is set to **0**, which effectively disables the animation. 518 519**Atomic service API**: This API can be used in atomic services since API version 12. 520 521**System capability**: SystemCapability.ArkUI.ArkUI.Full 522 523**Parameters** 524 525| Name | Type | Mandatory| Description | 526| ----------- | ------------------------------------------------------ | ---- | ------------------------------------------------------------ | 527| index | number | Yes | Index of the currently displayed element. | 528| targetIndex | number | Yes | Index of the target element to switch to. | 529| 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.| 530 531### onAnimationEnd<sup>11+</sup> 532 533onAnimationEnd(handler: (index: number, event: TabsAnimationEvent) => void) 534 535Triggered 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. 536 537**Atomic service API**: This API can be used in atomic services since API version 12. 538 539**System capability**: SystemCapability.ArkUI.ArkUI.Full 540 541**Parameters** 542 543| Name| Type | Mandatory| Description | 544| ------ | ------------------------------------------------------ | ---- | ------------------------------------------------------------ | 545| index | number | Yes | Index of the currently displayed element. | 546| 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.| 547 548### onGestureSwipe<sup>11+</sup> 549 550onGestureSwipe(handler: (index: number, event: TabsAnimationEvent) => void) 551 552Triggered on a frame-by-frame basis when the tab is switched by a swipe. 553 554**Atomic service API**: This API can be used in atomic services since API version 12. 555 556**System capability**: SystemCapability.ArkUI.ArkUI.Full 557 558**Parameters** 559 560| Name| Type | Mandatory| Description | 561| ------ | ------------------------------------------------------ | ---- | ------------------------------------------------------------ | 562| index | number | Yes | Index of the currently displayed element. | 563| 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.| 564 565### customContentTransition<sup>11+</sup> 566 567customContentTransition(delegate: (from: number, to: number) => TabContentAnimatedTransition \| undefined) 568 569Sets the custom tab switching animation. 570 571Instructions: 572 5731. 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. 574 575**Widget capability**: This API can be used in ArkTS widgets since API version 11. 576 577**Atomic service API**: This API can be used in atomic services since API version 12. 578 579**System capability**: SystemCapability.ArkUI.ArkUI.Full 580 581**Parameters** 582 583| Name| Type | Mandatory| Description | 584| ------ | ------ | ---- | ------------------------------- | 585| from | number | Yes | Index of the currently displayed tab before the animation starts.| 586| to | number | Yes | Index of the target tab before the animation starts.| 587 588**Return value** 589 590| Type | Description | 591| ------------------------------------------------------------ | ------------------------ | 592| [TabContentAnimatedTransition](#tabcontentanimatedtransition11) \| undefined | Information about the custom tab switching animation.| 593 594### onContentWillChange<sup>12+</sup> 595 596onContentWillChange(handler: (currentIndex: number, comingIndex: number) => boolean) 597 598Triggered when a new page is about to be displayed. 599 600Specifically, this event is triggered in the following cases: 601 6021. When the user swipes on the **TabContent** component (provided that it supports swiping) to switch to a new page. 603 6042. When **TabsController.changeIndex** is called to switch to a new page. 605 6063. When the **index** attribute is changed to switch to a new page. 607 6084. When the user clicks a tab on the tab bar to switch to a new page. 609 6105. 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. 611 612**Atomic service API**: This API can be used in atomic services since API version 12. 613 614**System capability**: SystemCapability.ArkUI.ArkUI.Full 615 616**Parameters** 617 618| Name | Type | Mandatory| Description | 619| ------------ | ------ | ---- | ------------------------------------------ | 620| currentIndex | number | Yes | Index of the active tab. The index starts from 0.| 621| comingIndex | number | Yes | Index of the new tab to be displayed. | 622 623**Return value** 624 625| Type | Description | 626| ------- | ------------------------------------------------------------ | 627| 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.| 628 629## TabsAnimationEvent<sup>11+</sup> 630 631Describes the animation information of the **Tabs** component. 632 633**Atomic service API**: This API can be used in atomic services since API version 12. 634 635**System capability**: SystemCapability.ArkUI.ArkUI.Full 636 637| Name | Type | Read Only| Optional| Description | 638| ------------- | ---------- | ---- | ---- | ------------------------ | 639| 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**| 640| 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**| 641| velocity | number | No| No| Hands-off velocity at the beginning of the animation. Unit: VP/S<br>Default value: **0**| 642 643## TabContentAnimatedTransition<sup>11+</sup> 644 645Provides the information about the custom tab page switching animation. 646 647**Widget capability**: This API can be used in ArkTS widgets since API version 11. 648 649**Atomic service API**: This API can be used in atomic services since API version 12. 650 651**System capability**: SystemCapability.ArkUI.ArkUI.Full 652 653| Name | Type | Mandatory | Description | 654| ------------- | ---------------------- | ---- |---------------------- | 655| 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**| 656| transition | [Callback](./ts-types.md#callback12)\<[TabContentTransitionProxy](#tabcontenttransitionproxy11)> | Yes| Content of the custom switching animation.| 657 658## TabContentTransitionProxy<sup>11+</sup> 659 660Implements 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. 661 662**Widget capability**: This API can be used in ArkTS widgets since API version 11. 663 664**Atomic service API**: This API can be used in atomic services since API version 12. 665 666**System capability**: SystemCapability.ArkUI.ArkUI.Full 667 668### Attributes 669 670| Name | Type | Read Only| Optional| Description | 671| ----- | ------- | ---- | ---- | --------------------------- | 672| from | number | No| No| Index of the starting page of the custom animation.| 673| to | number | No| No| Index of the target tab to switch to.| 674 675### finishTransition 676 677finishTransition(): void 678 679Notifies the **Tabs** component that the custom animation has finished playing. 680 681**Widget capability**: This API can be used in ArkTS widgets since API version 11. 682 683**Atomic service API**: This API can be used in atomic services since API version 12. 684 685**System capability**: SystemCapability.ArkUI.ArkUI.Full 686 687## TabsController 688 689Defines a tab controller, which is used to control switching of tabs. One **TabsController** cannot control multiple **Tabs** components. 690 691**Atomic service API**: This API can be used in atomic services since API version 11. 692 693**System capability**: SystemCapability.ArkUI.ArkUI.Full 694 695### Objects to Import 696 697```ts 698let controller: TabsController = new TabsController() 699``` 700 701### constructor 702 703constructor() 704 705A constructor used to create a **TabsController** object. 706 707**Atomic service API**: This API can be used in atomic services since API version 11. 708 709**System capability**: SystemCapability.ArkUI.ArkUI.Full 710 711### changeIndex 712 713changeIndex(value: number): void 714 715Switches to the specified tab. 716 717**Atomic service API**: This API can be used in atomic services since API version 11. 718 719**System capability**: SystemCapability.ArkUI.ArkUI.Full 720 721**Parameters** 722 723| Name | Type | Mandatory | Description | 724| ----- | ------ | ---- | ---------------------------------------- | 725| 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.| 726 727### preloadItems<sup>12+</sup> 728 729preloadItems(indices: Optional\<Array\<number>>): Promise\<void> 730 731Preloads 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. 732 733**Atomic service API**: This API can be used in atomic services since API version 12. 734 735**System capability**: SystemCapability.ArkUI.ArkUI.Full 736 737**Parameters** 738 739| Name | Type | Mandatory | Description | 740| ----- | ------ | ---- | ---------------------------------------- | 741| indices | Optional\<Array\<number>> | Yes| Array of indexes of the child nodes to preload.<br>The default value is an empty array.| 742 743**Return value** 744 745| Type | Description | 746| ------------------------------------------------------------ | ------------------------ | 747| Promise\<void> | Promise used to return the value.| 748 749**Error codes** 750 751For details about the error codes, see [Universal Error Codes](../../errorcode-universal.md). 752 753| ID | Error Message | 754| -------- | -------------------------------------------- | 755| 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. | 756 757### setTabBarTranslate<sup>13+</sup> 758 759setTabBarTranslate(translate: TranslateOptions): void 760 761Sets the translation distance of the tab bar. 762 763> **NOTE** 764> 765> 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. 766> 767 768**Atomic service API**: This API can be used in atomic services since API version 13. 769 770**System capability**: SystemCapability.ArkUI.ArkUI.Full 771 772**Parameters** 773 774| Name | Type | Mandatory | Description | 775| ----- | ------ | ---- | ---------------------------------------- | 776| translate | [TranslateOptions](ts-universal-attributes-transformation.md#translateoptions) | Yes| Translation distance of the tab bar.| 777 778### setTabBarOpacity<sup>13+</sup> 779 780setTabBarOpacity(opacity: number): void 781 782Sets the opacity of the tab bar. 783 784> **NOTE** 785> 786> 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. 787> 788 789**Atomic service API**: This API can be used in atomic services since API version 13. 790 791**System capability**: SystemCapability.ArkUI.ArkUI.Full 792 793**Parameters** 794 795| Name | Type | Mandatory | Description | 796| ----- | ------ | ---- | ---------------------------------------- | 797| opacity | number | Yes| Opacity of the tab bar. The value range is [0.0, 1.0].| 798 799## Example 800 801### Example 1: Setting Up Custom Tab Switching Synchronization 802 803This example demonstrates how to use **onAnimationStart** and **onChange** to synchronize tabs with swiping gestures during tab switching. 804 805```ts 806// xxx.ets 807@Entry 808@Component 809struct TabsExample { 810 @State fontColor: string = '#182431' 811 @State selectedFontColor: string = '#007DFF' 812 @State currentIndex: number = 0 813 @State selectedIndex: number = 0 814 private controller: TabsController = new TabsController() 815 816 @Builder tabBuilder(index: number, name: string) { 817 Column() { 818 Text(name) 819 .fontColor(this.selectedIndex === index ? this.selectedFontColor : this.fontColor) 820 .fontSize(16) 821 .fontWeight(this.selectedIndex === index ? 500 : 400) 822 .lineHeight(22) 823 .margin({ top: 17, bottom: 7 }) 824 Divider() 825 .strokeWidth(2) 826 .color('#007DFF') 827 .opacity(this.selectedIndex === index ? 1 : 0) 828 }.width('100%') 829 } 830 831 build() { 832 Column() { 833 Tabs({ barPosition: BarPosition.Start, index: this.currentIndex, controller: this.controller }) { 834 TabContent() { 835 Column().width('100%').height('100%').backgroundColor('#00CB87') 836 }.tabBar(this.tabBuilder(0, 'green')) 837 838 TabContent() { 839 Column().width('100%').height('100%').backgroundColor('#007DFF') 840 }.tabBar(this.tabBuilder(1, 'blue')) 841 842 TabContent() { 843 Column().width('100%').height('100%').backgroundColor('#FFBF00') 844 }.tabBar(this.tabBuilder(2, 'yellow')) 845 846 TabContent() { 847 Column().width('100%').height('100%').backgroundColor('#E67C92') 848 }.tabBar(this.tabBuilder(3, 'pink')) 849 } 850 .vertical(false) 851 .barMode(BarMode.Fixed) 852 .barWidth(360) 853 .barHeight(56) 854 .animationDuration(400) 855 .onChange((index: number) => { 856 // currentIndex controls which tab is displayed. 857 this.currentIndex = index 858 this.selectedIndex = index 859 }) 860 .onAnimationStart((index: number, targetIndex: number, event: TabsAnimationEvent) => { 861 if (index === targetIndex) { 862 return 863 } 864 // selectedIndex controls the color switching for the image and text in the custom tab bar. 865 this.selectedIndex = targetIndex 866 }) 867 .width(360) 868 .height(296) 869 .margin({ top: 52 }) 870 .backgroundColor('#F1F3F5') 871 }.width('100%') 872 } 873} 874``` 875 876 877 878### Example 2: Setting the Basic Attributes of the Divider 879 880This example uses **divider** to present dividers in different styles. 881 882```ts 883// xxx.ets 884@Entry 885@Component 886struct TabsDivider1 { 887 private controller1: TabsController = new TabsController() 888 @State dividerColor: string = 'red' 889 @State strokeWidth: number = 2 890 @State startMargin: number = 0 891 @State endMargin: number = 0 892 @State nullFlag: boolean = false 893 894 build() { 895 Column() { 896 Tabs({ controller: this.controller1 }) { 897 TabContent() { 898 Column().width('100%').height('100%').backgroundColor(Color.Pink) 899 }.tabBar('pink') 900 901 TabContent() { 902 Column().width('100%').height('100%').backgroundColor(Color.Yellow) 903 }.tabBar('yellow') 904 905 TabContent() { 906 Column().width('100%').height('100%').backgroundColor(Color.Blue) 907 }.tabBar('blue') 908 909 TabContent() { 910 Column().width('100%').height('100%').backgroundColor(Color.Green) 911 }.tabBar('green') 912 913 TabContent() { 914 Column().width('100%').height('100%').backgroundColor(Color.Red) 915 }.tabBar('red') 916 } 917 .vertical(true) 918 .scrollable(true) 919 .barMode(BarMode.Fixed) 920 .barWidth(70) 921 .barHeight(200) 922 .animationDuration(400) 923 .onChange((index: number) => { 924 console.info(index.toString()) 925 }) 926 .height('200vp') 927 .margin({ bottom: '12vp' }) 928 .divider(this.nullFlag ? null : { 929 strokeWidth: this.strokeWidth, 930 color: this.dividerColor, 931 startMargin: this.startMargin, 932 endMargin: this.endMargin 933 }) 934 935 Button('Regular Divider').width('100%').margin({ bottom: '12vp' }) 936 .onClick(() => { 937 this.nullFlag = false; 938 this.strokeWidth = 2; 939 this.dividerColor = 'red'; 940 this.startMargin = 0; 941 this.endMargin = 0; 942 }) 943 Button('Empty Divider').width('100%').margin({ bottom: '12vp' }) 944 .onClick(() => { 945 this.nullFlag = true 946 }) 947 Button('Change to Blue').width('100%').margin({ bottom: '12vp'}) 948 .onClick(() => { 949 this.dividerColor = 'blue' 950 }) 951 Button('Increase Width').width('100%').margin({ bottom: '12vp' }) 952 .onClick(() => { 953 this.strokeWidth += 2 954 }) 955 Button('Decrease Width').width('100%').margin({ bottom: '12vp'}) 956 .onClick(() => { 957 if (this.strokeWidth > 2) { 958 this.strokeWidth -= 2 959 } 960 }) 961 Button('Increase Top Margin').width('100%').margin({ bottom: '12vp' }) 962 .onClick(() => { 963 this.startMargin += 2 964 }) 965 Button('Decrease Top Margin').width('100%').margin({ bottom: '12vp' }) 966 .onClick(() => { 967 if (this.startMargin > 2) { 968 this.startMargin -= 2 969 } 970 }) 971 Button('Increase Bottom Margin').width('100%').margin({ bottom:'12vp'}) 972 .onClick(() => { 973 this.endMargin += 2 974 }) 975 Button('Decrease Bottom Margin').width('100%').margin({ bottom:'12vp' }) 976 .onClick(() => { 977 if (this.endMargin > 2) { 978 this.endMargin -= 2 979 } 980 }) 981 }.padding({ top: '24vp', left: '24vp', right: '24vp' }) 982 } 983} 984``` 985 986 987 988### Example 3: Setting Tab Bar Fading 989 990This example uses **fadingEdge** to specify whether to fade out tabs. 991 992```ts 993// xxx.ets 994@Entry 995@Component 996struct TabsOpaque { 997 @State message: string = 'Hello World' 998 private controller: TabsController = new TabsController() 999 private controller1: TabsController = new TabsController() 1000 @State selfFadingFade: boolean = true; 1001 1002 build() { 1003 Column() { 1004 Button('Set Tab to Fade').width('100%').margin({ bottom: '12vp' }) 1005 .onClick((event?: ClickEvent) => { 1006 this.selfFadingFade = true; 1007 }) 1008 Button('Set Tab Not to Fade').width('100%').margin({ bottom: '12vp' }) 1009 .onClick((event?: ClickEvent) => { 1010 this.selfFadingFade = false; 1011 }) 1012 Tabs({ barPosition: BarPosition.End, controller: this.controller }) { 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 TabContent() { 1038 Column().width('100%').height('100%').backgroundColor(Color.Green) 1039 }.tabBar('green') 1040 1041 TabContent() { 1042 Column().width('100%').height('100%').backgroundColor(Color.Green) 1043 }.tabBar('green') 1044 } 1045 .vertical(false) 1046 .scrollable(true) 1047 .barMode(BarMode.Scrollable) 1048 .barHeight(80) 1049 .animationDuration(400) 1050 .onChange((index: number) => { 1051 console.info(index.toString()) 1052 }) 1053 .fadingEdge(this.selfFadingFade) 1054 .height('30%') 1055 .width('100%') 1056 1057 Tabs({ barPosition: BarPosition.Start, controller: this.controller1 }) { 1058 TabContent() { 1059 Column().width('100%').height('100%').backgroundColor(Color.Pink) 1060 }.tabBar('pink') 1061 1062 TabContent() { 1063 Column().width('100%').height('100%').backgroundColor(Color.Yellow) 1064 }.tabBar('yellow') 1065 1066 TabContent() { 1067 Column().width('100%').height('100%').backgroundColor(Color.Blue) 1068 }.tabBar('blue') 1069 1070 TabContent() { 1071 Column().width('100%').height('100%').backgroundColor(Color.Green) 1072 }.tabBar('green') 1073 1074 TabContent() { 1075 Column().width('100%').height('100%').backgroundColor(Color.Green) 1076 }.tabBar('green') 1077 1078 TabContent() { 1079 Column().width('100%').height('100%').backgroundColor(Color.Green) 1080 }.tabBar('green') 1081 } 1082 .vertical(true) 1083 .scrollable(true) 1084 .barMode(BarMode.Scrollable) 1085 .barHeight(200) 1086 .barWidth(80) 1087 .animationDuration(400) 1088 .onChange((index: number) => { 1089 console.info(index.toString()) 1090 }) 1091 .fadingEdge(this.selfFadingFade) 1092 .height('30%') 1093 .width('100%') 1094 } 1095 .padding({ top: '24vp', left: '24vp', right: '24vp' }) 1096 } 1097} 1098``` 1099 1100 1101 1102### Example 4: Setting the Tab Bar to Be Superimposed on the TabContent Component 1103 1104This example uses **barOverlap** to specify whether the tab bar is superimposed on the **TabContent** component after having its background blurred. 1105 1106```ts 1107// xxx.ets 1108@Entry 1109@Component 1110struct barHeightTest { 1111 @State arr: number[] = [0, 1, 2, 3] 1112 @State barOverlap: boolean = true; 1113 build() { 1114 Column() { 1115 Text(`barOverlap ${this.barOverlap}`).fontSize(16) 1116 Button("Change barOverlap").width('100%').margin({ bottom: '12vp' }) 1117 .onClick((event?: ClickEvent) => { 1118 if (this.barOverlap) { 1119 this.barOverlap = false; 1120 } else { 1121 this.barOverlap = true; 1122 } 1123 }) 1124 1125 Tabs({ barPosition: BarPosition.End }) { 1126 TabContent() { 1127 Column() { 1128 List({ space: 10 }) { 1129 ForEach(this.arr, (item: number) => { 1130 ListItem() { 1131 Text("item" + item).width('80%').height(200).fontSize(16).textAlign(TextAlign.Center).backgroundColor('#fff8b81e') 1132 } 1133 }, (item: string) => item) 1134 }.width('100%').height('100%') 1135 .lanes(2).alignListItem(ListItemAlign.Center) 1136 }.width('100%').height('100%') 1137 .backgroundColor(Color.Pink) 1138 } 1139 .tabBar(new BottomTabBarStyle($r('sys.media.ohos_icon_mask_svg'), "test 0")) 1140 } 1141 .scrollable(false) 1142 .height('60%') 1143 .barOverlap(this.barOverlap) 1144 } 1145 .height(500) 1146 .padding({ top: '24vp', left: '24vp', right: '24vp' }) 1147 } 1148} 1149``` 1150 1151 1152 1153### Example 5: Setting the Grid-Aligned Visible Area for the TabBar 1154 1155This example uses **barGridAlign** to set the visible area of the tab bar in grid mode. 1156 1157```ts 1158// xxx.ets 1159@Entry 1160@Component 1161struct TabsExample5 { 1162 private controller: TabsController = new TabsController() 1163 @State gridMargin: number = 10 1164 @State gridGutter: number = 10 1165 @State sm: number = -2 1166 @State clickedContent: string = ""; 1167 1168 build() { 1169 Column() { 1170 Row() { 1171 Button("gridMargin+10 " + this.gridMargin) 1172 .width('47%') 1173 .height(50) 1174 .margin({ top: 5 }) 1175 .onClick((event?: ClickEvent) => { 1176 this.gridMargin += 10 1177 }) 1178 .margin({ right: '6%', bottom: '12vp' }) 1179 Button("gridMargin-10 " + this.gridMargin) 1180 .width('47%') 1181 .height(50) 1182 .margin({ top: 5 }) 1183 .onClick((event?: ClickEvent) => { 1184 this.gridMargin -= 10 1185 }) 1186 .margin({ bottom: '12vp' }) 1187 } 1188 1189 Row() { 1190 Button("gridGutter+10 " + this.gridGutter) 1191 .width('47%') 1192 .height(50) 1193 .margin({ top: 5 }) 1194 .onClick((event?: ClickEvent) => { 1195 this.gridGutter += 10 1196 }) 1197 .margin({ right: '6%', bottom: '12vp' }) 1198 Button("gridGutter-10 " + this.gridGutter) 1199 .width('47%') 1200 .height(50) 1201 .margin({ top: 5 }) 1202 .onClick((event?: ClickEvent) => { 1203 this.gridGutter -= 10 1204 }) 1205 .margin({ bottom: '12vp' }) 1206 } 1207 1208 Row() { 1209 Button("sm+2 " + this.sm) 1210 .width('47%') 1211 .height(50) 1212 .margin({ top: 5 }) 1213 .onClick((event?: ClickEvent) => { 1214 this.sm += 2 1215 }) 1216 .margin({ right: '6%' }) 1217 Button("sm-2 " + this.sm).width('47%').height(50).margin({ top: 5 }) 1218 .onClick((event?: ClickEvent) => { 1219 this.sm -= 2 1220 }) 1221 } 1222 1223 Text("Clicked content: " + this.clickedContent).width('100%').height(200).margin({ top: 5 }) 1224 1225 1226 Tabs({ barPosition: BarPosition.End, controller: this.controller }) { 1227 TabContent() { 1228 Column().width('100%').height('100%').backgroundColor(Color.Pink) 1229 }.tabBar(BottomTabBarStyle.of($r("sys.media.ohos_app_icon"), "1")) 1230 1231 TabContent() { 1232 Column().width('100%').height('100%').backgroundColor(Color.Green) 1233 }.tabBar(BottomTabBarStyle.of($r("sys.media.ohos_app_icon"), "2")) 1234 1235 TabContent() { 1236 Column().width('100%').height('100%').backgroundColor(Color.Blue) 1237 }.tabBar(BottomTabBarStyle.of($r("sys.media.ohos_app_icon"), "3")) 1238 } 1239 .width('350vp') 1240 .animationDuration(300) 1241 .height('60%') 1242 .barGridAlign({ sm: this.sm, margin: this.gridMargin, gutter: this.gridGutter }) 1243 .backgroundColor(0xf1f3f5) 1244 .onTabBarClick((index: number) => { 1245 this.clickedContent += "index " + index + " was clicked\n"; 1246 }) 1247 } 1248 .width('100%') 1249 .height(500) 1250 .margin({ top: 5 }) 1251 .padding('10vp') 1252 } 1253} 1254``` 1255 1256 1257 1258### Example 6: Setting the Layout Style for a Scrollable TabBar 1259 1260This example implements the **ScrollableBarModeOptions** parameter of **barMode**. This parameter is effective only in **Scrollable** mode. 1261 1262```ts 1263// xxx.ets 1264@Entry 1265@Component 1266struct TabsExample6 { 1267 private controller: TabsController = new TabsController() 1268 @State scrollMargin: number = 0 1269 @State layoutStyle: LayoutStyle = LayoutStyle.ALWAYS_CENTER 1270 @State text: string = "Text" 1271 1272 build() { 1273 Column() { 1274 Row() { 1275 Button("scrollMargin+10 " + this.scrollMargin) 1276 .width('47%') 1277 .height(50) 1278 .margin({ top: 5 }) 1279 .onClick((event?: ClickEvent) => { 1280 this.scrollMargin += 10 1281 }) 1282 .margin({ right: '6%', bottom: '12vp' }) 1283 Button("scrollMargin-10 " + this.scrollMargin) 1284 .width('47%') 1285 .height(50) 1286 .margin({ top: 5 }) 1287 .onClick((event?: ClickEvent) => { 1288 this.scrollMargin -= 10 1289 }) 1290 .margin({ bottom: '12vp' }) 1291 } 1292 1293 Row() { 1294 Button("Add Text") 1295 .width('47%') 1296 .height(50) 1297 .margin({ top: 5 }) 1298 .onClick((event?: ClickEvent) => { 1299 this.text += 'Add Text' 1300 }) 1301 .margin({ right: '6%', bottom: '12vp' }) 1302 Button("Reset Text") 1303 .width('47%') 1304 .height(50) 1305 .margin({ top: 5 }) 1306 .onClick((event?: ClickEvent) => { 1307 this.text = "Text" 1308 }) 1309 .margin({ bottom: '12vp' }) 1310 } 1311 1312 Row() { 1313 Button("layoutStyle.ALWAYS_CENTER") 1314 .width('100%') 1315 .height(50) 1316 .margin({ top: 5 }) 1317 .fontSize(15) 1318 .onClick((event?: ClickEvent) => { 1319 this.layoutStyle = LayoutStyle.ALWAYS_CENTER; 1320 }) 1321 .margin({ bottom: '12vp' }) 1322 } 1323 1324 Row() { 1325 Button("layoutStyle.ALWAYS_AVERAGE_SPLIT") 1326 .width('100%') 1327 .height(50) 1328 .margin({ top: 5 }) 1329 .fontSize(15) 1330 .onClick((event?: ClickEvent) => { 1331 this.layoutStyle = LayoutStyle.ALWAYS_AVERAGE_SPLIT; 1332 }) 1333 .margin({ bottom: '12vp' }) 1334 } 1335 1336 Row() { 1337 Button("layoutStyle.SPACE_BETWEEN_OR_CENTER") 1338 .width('100%') 1339 .height(50) 1340 .margin({ top: 5 }) 1341 .fontSize(15) 1342 .onClick((event?: ClickEvent) => { 1343 this.layoutStyle = LayoutStyle.SPACE_BETWEEN_OR_CENTER; 1344 }) 1345 .margin({ bottom: '12vp' }) 1346 } 1347 1348 Tabs({ barPosition: BarPosition.End, controller: this.controller }) { 1349 TabContent() { 1350 Column().width('100%').height('100%').backgroundColor(Color.Pink) 1351 }.tabBar(SubTabBarStyle.of(this.text)) 1352 1353 TabContent() { 1354 Column().width('100%').height('100%').backgroundColor(Color.Green) 1355 }.tabBar(SubTabBarStyle.of(this.text)) 1356 1357 TabContent() { 1358 Column().width('100%').height('100%').backgroundColor(Color.Blue) 1359 }.tabBar(SubTabBarStyle.of(this.text)) 1360 } 1361 .animationDuration(300) 1362 .height('60%') 1363 .backgroundColor(0xf1f3f5) 1364 .barMode(BarMode.Scrollable, { margin: this.scrollMargin, nonScrollableLayoutStyle: this.layoutStyle }) 1365 } 1366 .width('100%') 1367 .height(500) 1368 .margin({ top: 5 }) 1369 .padding('24vp') 1370 } 1371} 1372``` 1373 1374 1375 1376### Example 7: Implementing a Custom Tab Switching Animation 1377 1378This example uses **customContentTransition** to implement a custom tab switching animation. 1379 1380```ts 1381// xxx.ets 1382interface itemType { 1383 text: string, 1384 backgroundColor: Color 1385} 1386 1387@Entry 1388@Component 1389struct TabsCustomAnimationExample { 1390 @State data: itemType[] = [ 1391 { 1392 text: 'Red', 1393 backgroundColor: Color.Red 1394 }, 1395 { 1396 text: 'Yellow', 1397 backgroundColor: Color.Yellow 1398 }, 1399 { 1400 text: 'Blue', 1401 backgroundColor: Color.Blue 1402 }] 1403 @State opacityList: number[] = [] 1404 @State scaleList: number[] = [] 1405 1406 private durationList: number[] = [] 1407 private timeoutList: number[] = [] 1408 private customContentTransition: (from: number, to: number) => TabContentAnimatedTransition = (from: number, to: number) => { 1409 let tabContentAnimatedTransition = { 1410 timeout: this.timeoutList[from], 1411 transition: (proxy: TabContentTransitionProxy) => { 1412 this.scaleList[from] = 1.0 1413 this.scaleList[to] = 0.5 1414 this.opacityList[from] = 1.0 1415 this.opacityList[to] = 0.5 1416 animateTo({ 1417 duration: this.durationList[from], 1418 onFinish: () => { 1419 proxy.finishTransition() 1420 } 1421 }, () => { 1422 this.scaleList[from] = 0.5 1423 this.scaleList[to] = 1.0 1424 this.opacityList[from] = 0.5 1425 this.opacityList[to] = 1.0 1426 }) 1427 } 1428 } as TabContentAnimatedTransition 1429 return tabContentAnimatedTransition 1430 } 1431 1432 aboutToAppear(): void { 1433 let duration = 1000 1434 let timeout = 1000 1435 for (let i = 1; i <= this.data.length; i++) { 1436 this.opacityList.push(1.0) 1437 this.scaleList.push(1.0) 1438 this.durationList.push(duration * i) 1439 this.timeoutList.push(timeout * i) 1440 } 1441 } 1442 1443 build() { 1444 Column() { 1445 Tabs() { 1446 ForEach(this.data, (item: itemType, index: number) => { 1447 TabContent() {} 1448 .tabBar(item.text) 1449 .backgroundColor(item.backgroundColor) 1450 // Customize the opacity and scale animation. 1451 .opacity(this.opacityList[index]) 1452 .scale({ x: this.scaleList[index], y: this.scaleList[index] }) 1453 }) 1454 } 1455 .backgroundColor(0xf1f3f5) 1456 .width('100%') 1457 .height(500) 1458 .customContentTransition(this.customContentTransition) 1459 } 1460 } 1461} 1462``` 1463 1464 1465 1466### Example 8: Intercepting Page Switching 1467 1468This example uses **onContentWillChange** to intercept and customize the gesture-based swiping actions for page switching. 1469 1470```ts 1471//xxx.ets 1472@Entry 1473@Component 1474struct TabsExample { 1475 @State selectedIndex: number = 2 1476 @State currentIndex: number = 2 1477 private controller: TabsController = new TabsController() 1478 @Builder tabBuilder(title: string,targetIndex: number) { 1479 Column(){ 1480 Text(title).fontColor(this.selectedIndex === targetIndex ? '#1698CE' : '#6B6B6B') 1481 }.width('100%') 1482 .height(50) 1483 .justifyContent(FlexAlign.Center) 1484 } 1485 build() { 1486 Column() { 1487 Tabs({ barPosition: BarPosition.End, index: this.currentIndex, controller: this.controller }) { 1488 TabContent() { 1489 Column(){ 1490 Text('Home tab content') 1491 }.width('100%').height('100%').backgroundColor('#00CB87').justifyContent(FlexAlign.Center) 1492 }.tabBar(this.tabBuilder('Home',0)) 1493 1494 TabContent() { 1495 Column(){ 1496 Text('Discover tab content') 1497 }.width('100%').height('100%').backgroundColor('#007DFF').justifyContent(FlexAlign.Center) 1498 }.tabBar(this.tabBuilder('Discover',1)) 1499 1500 TabContent() { 1501 Column(){ 1502 Text('Recommended tab content') 1503 }.width('100%').height('100%').backgroundColor('#FFBF00').justifyContent(FlexAlign.Center) 1504 }.tabBar(this.tabBuilder('Recommended',2)) 1505 1506 TabContent() { 1507 Column(){ 1508 Text('Me tab content') 1509 }.width('100%').height('100%').backgroundColor('#E67C92').justifyContent(FlexAlign.Center) 1510 }.tabBar(this.tabBuilder('Me',3)) 1511 } 1512 .vertical(false) 1513 .barMode(BarMode.Fixed) 1514 .barWidth(360) 1515 .barHeight(60) 1516 .animationDuration(0) 1517 .onChange((index: number) => { 1518 this.currentIndex = index 1519 this.selectedIndex = index 1520 }) 1521 .width(360) 1522 .height(600) 1523 .backgroundColor('#F1F3F5') 1524 .scrollable(true) 1525 .onContentWillChange((currentIndex, comingIndex) => { 1526 if (comingIndex == 2) { 1527 return false 1528 } 1529 return true 1530 }) 1531 1532 Button('Change Index').width('50%').margin({ top: 20 }) 1533 .onClick(()=>{ 1534 this.currentIndex = (this.currentIndex + 1) % 4 1535 }) 1536 1537 Button('changeIndex').width('50%').margin({ top: 20 }) 1538 .onClick(()=>{ 1539 this.currentIndex = (this.currentIndex + 1) % 4 1540 this.controller.changeIndex(this.currentIndex) 1541 }) 1542 }.width('100%') 1543 } 1544} 1545``` 1546 1547 1548 1549### Example 9: Customizing the Tab Bar Switching Animation 1550 1551This example uses **onChange**, **onAnimationStart**, **onAnimationEnd**, and **onGestureSwipe** APIs to customize the tab bar switching animation. 1552 1553<!--code_no_check--> 1554 1555```ts 1556// EntryAbility.ets 1557import { Configuration, UIAbility } from '@kit.AbilityKit'; 1558import { i18n } from '@kit.LocalizationKit'; 1559import { CommonUtil } from '../common/CommonUtil'; 1560 1561export default class EntryAbility extends UIAbility { 1562 onConfigurationUpdate(newConfig: Configuration): void { 1563 // Listen for system configuration changes. 1564 if (newConfig.language) { 1565 CommonUtil.setIsRTL(i18n.isRTL(newConfig.language)) 1566 } 1567 } 1568} 1569``` 1570 1571<!--code_no_check--> 1572 1573```ts 1574// CommonUtil.ets 1575import { i18n, intl } from '@kit.LocalizationKit'; 1576 1577export class CommonUtil { 1578 private static isRTL: boolean = i18n.isRTL((new intl.Locale()).language) 1579 1580 public static setIsRTL(isRTL: boolean): void { 1581 CommonUtil.isRTL = isRTL 1582 } 1583 1584 public static getIsRTL(): boolean { 1585 return CommonUtil.isRTL 1586 } 1587} 1588``` 1589 1590<!--code_no_check--> 1591 1592```ts 1593// xxx.ets 1594import { LengthMetrics } from '@kit.ArkUI'; 1595import { CommonUtil } from '../common/CommonUtil'; 1596 1597@Entry 1598@Component 1599struct TabsExample { 1600 @State colorArray: [string, string][] = 1601 [['green', '#00CB87'], ['blue', '#007DFF'], ['yellow', '#FFBF00'], ['pink', '#E67C92']] 1602 @State currentIndex: number = 0 1603 @State animationDuration: number = 300 1604 @State indicatorLeftMargin: number = 0 1605 @State indicatorWidth: number = 0 1606 private tabsWidth: number = 0 1607 private textInfos: [number, number][] = [] 1608 private isStartAnimateTo: boolean = false 1609 1610 aboutToAppear():void { 1611 for (let i = 0; i < this.colorArray.length; i++) { 1612 this.textInfos.push([0, 0]); 1613 } 1614 } 1615 1616 @Builder 1617 tabBuilder(index: number, name: string) { 1618 Column() { 1619 Text(name) 1620 .fontSize(16) 1621 .fontColor(this.currentIndex === index ? '#007DFF' : '#182431') 1622 .fontWeight(this.currentIndex === index ? 500 : 400) 1623 .id(index.toString()) 1624 .onAreaChange((oldValue: Area, newValue: Area) => { 1625 this.textInfos[index] = [newValue.globalPosition.x as number, newValue.width as number] 1626 if (!this.isStartAnimateTo && this.currentIndex === index && this.tabsWidth > 0) { 1627 this.setIndicatorAttr(this.textInfos[this.currentIndex][0], this.textInfos[this.currentIndex][1]) 1628 } 1629 }) 1630 }.width('100%') 1631 } 1632 1633 build() { 1634 Stack({ alignContent: Alignment.TopStart }) { 1635 Tabs({ barPosition: BarPosition.Start }) { 1636 ForEach(this.colorArray, (item: [string, string], index:number) => { 1637 TabContent() { 1638 Column().width('100%').height('100%').backgroundColor(item[1]) 1639 }.tabBar(this.tabBuilder(index, item[0])) 1640 }) 1641 } 1642 .onAreaChange((oldValue: Area, newValue: Area)=> { 1643 this.tabsWidth = newValue.width as number 1644 if (!this.isStartAnimateTo) { 1645 this.setIndicatorAttr(this.textInfos[this.currentIndex][0], this.textInfos[this.currentIndex][1]) 1646 } 1647 }) 1648 .barWidth('100%') 1649 .barHeight(56) 1650 .width('100%') 1651 .height(296) 1652 .backgroundColor('#F1F3F5') 1653 .animationDuration(this.animationDuration) 1654 .onChange((index: number) => { 1655 this.currentIndex = index // Listen for index changes to switch between tab pages. 1656 }) 1657 .onAnimationStart((index: number, targetIndex: number, event: TabsAnimationEvent) => { 1658 // Triggered when the tab switching animation starts. The underline moves with the active tab, along with a width gradient. 1659 this.currentIndex = targetIndex 1660 this.startAnimateTo(this.animationDuration, this.textInfos[targetIndex][0], this.textInfos[targetIndex][1]) 1661 }) 1662 .onAnimationEnd((index: number, event: TabsAnimationEvent) => { 1663 // Triggered when the tab switching animation ends. The underline animation stops. 1664 let currentIndicatorInfo = this.getCurrentIndicatorInfo(index, event) 1665 this.startAnimateTo(0, currentIndicatorInfo.left, currentIndicatorInfo.width) 1666 }) 1667 .onGestureSwipe((index: number, event: TabsAnimationEvent) => { 1668 // Triggered on a frame-by-frame basis when the tab is switched by a swipe. 1669 let currentIndicatorInfo = this.getCurrentIndicatorInfo(index, event) 1670 this.currentIndex = currentIndicatorInfo.index 1671 this.setIndicatorAttr(currentIndicatorInfo.left, currentIndicatorInfo.width) 1672 }) 1673 1674 Column() 1675 .height(2) 1676 .width(this.indicatorWidth) 1677 .margin({ start: LengthMetrics.vp(this.indicatorLeftMargin), top: LengthMetrics.vp(48) }) 1678 .backgroundColor('#007DFF') 1679 }.width('100%') 1680 } 1681 1682 private getCurrentIndicatorInfo(index: number, event: TabsAnimationEvent): Record<string, number> { 1683 let nextIndex = index 1684 if (index > 0 && (CommonUtil.getIsRTL() ? event.currentOffset < 0 : event.currentOffset > 0)) { 1685 nextIndex-- 1686 } else if (index < this.textInfos.length - 1 && 1687 (CommonUtil.getIsRTL() ? event.currentOffset > 0 : event.currentOffset < 0)) { 1688 nextIndex++ 1689 } 1690 let indexInfo = this.textInfos[index] 1691 let nextIndexInfo = this.textInfos[nextIndex] 1692 let swipeRatio = Math.abs(event.currentOffset / this.tabsWidth) 1693 let currentIndex = swipeRatio > 0.5 ? nextIndex : index // If the swipe distance exceeds half the page width, the tab bar switches to the next page. 1694 let currentLeft = indexInfo[0] + (nextIndexInfo[0] - indexInfo[0]) * swipeRatio 1695 let currentWidth = indexInfo[1] + (nextIndexInfo[1] - indexInfo[1]) * swipeRatio 1696 return { 'index': currentIndex, 'left': currentLeft, 'width': currentWidth } 1697 } 1698 1699 private startAnimateTo(duration: number, leftMargin: number, width: number) { 1700 this.isStartAnimateTo = true 1701 animateTo({ 1702 duration: duration, // Animation duration. 1703 curve: Curve.Linear, // Animation curve. 1704 iterations: 1, // Number of playback times. 1705 playMode: PlayMode.Normal, // Animation playback mode. 1706 onFinish: () => { 1707 this.isStartAnimateTo = false 1708 console.info('play end') 1709 } 1710 }, () => { 1711 this.setIndicatorAttr(leftMargin, width) 1712 }) 1713 } 1714 1715 private setIndicatorAttr(leftMargin: number, width: number) { 1716 this.indicatorWidth = width 1717 if (CommonUtil.getIsRTL()) { 1718 this.indicatorLeftMargin = this.tabsWidth - leftMargin - width 1719 } else { 1720 this.indicatorLeftMargin = leftMargin 1721 } 1722 } 1723} 1724``` 1725 1726 1727 1728### Example 10: Preloading Child Nodes 1729 1730In this example, the **preloadItems** API is used to preload specified child nodes. 1731 1732```ts 1733// xxx.ets 1734import { BusinessError } from '@kit.BasicServicesKit'; 1735 1736@Entry 1737@Component 1738struct TabsPreloadItems { 1739 @State currentIndex: number = 1 1740 private tabsController: TabsController = new TabsController() 1741 1742 build() { 1743 Column() { 1744 Tabs({ index: this.currentIndex, controller: this.tabsController }) { 1745 TabContent() { 1746 MyComponent({ color: '#00CB87' }) 1747 }.tabBar(SubTabBarStyle.of('green')) 1748 1749 TabContent() { 1750 MyComponent({ color: '#007DFF' }) 1751 }.tabBar(SubTabBarStyle.of('blue')) 1752 1753 TabContent() { 1754 MyComponent({ color: '#FFBF00' }) 1755 }.tabBar(SubTabBarStyle.of('yellow')) 1756 1757 TabContent() { 1758 MyComponent({ color: '#E67C92' }) 1759 }.tabBar(SubTabBarStyle.of('pink')) 1760 } 1761 .width(360) 1762 .height(296) 1763 .backgroundColor('#F1F3F5') 1764 .onChange((index: number) => { 1765 this.currentIndex = index 1766 }) 1767 1768 Button('preload items: [0, 2, 3]') 1769 .margin(5) 1770 .onClick(() => { 1771 // Preload child nodes 0, 2, and 3 to improve the performance when users swipe or click to switch to these nodes. 1772 this.tabsController.preloadItems([0, 2, 3]) 1773 .then(() => { 1774 console.info('preloadItems success.') 1775 }) 1776 .catch((error: BusinessError) => { 1777 console.error('preloadItems failed, error code: ' + error.code + ', error message: ' + error.message) 1778 }) 1779 }) 1780 } 1781 } 1782} 1783 1784@Component 1785struct MyComponent { 1786 private color: string = "" 1787 1788 aboutToAppear(): void { 1789 console.info('aboutToAppear backgroundColor:' + this.color) 1790 } 1791 1792 aboutToDisappear(): void { 1793 console.info('aboutToDisappear backgroundColor:' + this.color) 1794 } 1795 1796 build() { 1797 Column() 1798 .width('100%') 1799 .height('100%') 1800 .backgroundColor(this.color) 1801 } 1802} 1803``` 1804 1805### Example 11: Setting Tab Bar Translation and Opacity 1806 1807This example demonstrates how to set the translation distance and opacity of the tab bar using the **setTabBarTranslate** and **setTabBarOpacity** APIs. 1808 1809```ts 1810// xxx.ets 1811@Entry 1812@Component 1813struct TabsExample { 1814 private controller: TabsController = new TabsController() 1815 1816 build() { 1817 Column() { 1818 Button('Set TabBar Translation Distance').margin({ top: 20 }) 1819 .onClick(() => { 1820 this.controller.setTabBarTranslate({ x: -20, y: -20 }) 1821 }) 1822 1823 Button('Set TabBar Opacity').margin({ top: 20 }) 1824 .onClick(() => { 1825 this.controller.setTabBarOpacity(0.5) 1826 }) 1827 1828 Tabs({ barPosition: BarPosition.End, controller: this.controller }) { 1829 TabContent() { 1830 Column().width('100%').height('100%').backgroundColor('#00CB87') 1831 }.tabBar(BottomTabBarStyle.of($r('app.media.startIcon'), 'green')) 1832 1833 TabContent() { 1834 Column().width('100%').height('100%').backgroundColor('#007DFF') 1835 }.tabBar(BottomTabBarStyle.of($r('app.media.startIcon'), 'blue')) 1836 1837 TabContent() { 1838 Column().width('100%').height('100%').backgroundColor('#FFBF00') 1839 }.tabBar(BottomTabBarStyle.of($r('app.media.startIcon'), 'yellow')) 1840 1841 TabContent() { 1842 Column().width('100%').height('100%').backgroundColor('#E67C92') 1843 }.tabBar(BottomTabBarStyle.of($r('app.media.startIcon'), 'pink')) 1844 } 1845 .width(360) 1846 .height(296) 1847 .margin({ top: 20 }) 1848 .barBackgroundColor('#F1F3F5') 1849 } 1850 .width('100%') 1851 } 1852} 1853``` 1854 1855 1856 1857### Example 12: Implementing Lazy Loading and Resource Release of Pages 1858 1859This example demonstrates how to use a custom tab bar with the **Swiper** component and **LazyForEach** to implement lazy loading and resource release of pages. 1860 1861```ts 1862// xxx.ets 1863class MyDataSource implements IDataSource { 1864 private list: number[] = [] 1865 1866 constructor(list: number[]) { 1867 this.list = list 1868 } 1869 1870 totalCount(): number { 1871 return this.list.length 1872 } 1873 1874 getData(index: number): number { 1875 return this.list[index] 1876 } 1877 1878 registerDataChangeListener(listener: DataChangeListener): void { 1879 } 1880 1881 unregisterDataChangeListener() { 1882 } 1883} 1884 1885@Entry 1886@Component 1887struct TabsSwiperExample { 1888 @State fontColor: string = '#182431' 1889 @State selectedFontColor: string = '#007DFF' 1890 @State currentIndex: number = 0 1891 private list: number[] = [] 1892 private tabsController: TabsController = new TabsController() 1893 private swiperController: SwiperController = new SwiperController() 1894 private swiperData: MyDataSource = new MyDataSource([]) 1895 1896 aboutToAppear(): void { 1897 for (let i = 0; i <= 9; i++) { 1898 this.list.push(i); 1899 } 1900 this.swiperData = new MyDataSource(this.list) 1901 } 1902 1903 @Builder tabBuilder(index: number, name: string) { 1904 Column() { 1905 Text(name) 1906 .fontColor(this.currentIndex === index ? this.selectedFontColor : this.fontColor) 1907 .fontSize(16) 1908 .fontWeight(this.currentIndex === index ? 500 : 400) 1909 .lineHeight(22) 1910 .margin({ top: 17, bottom: 7 }) 1911 Divider() 1912 .strokeWidth(2) 1913 .color('#007DFF') 1914 .opacity(this.currentIndex === index ? 1 : 0) 1915 }.width('20%') 1916 } 1917 1918 build() { 1919 Column() { 1920 Tabs({ barPosition: BarPosition.Start, controller: this.tabsController }) { 1921 ForEach(this.list, (item: number) => { 1922 TabContent().tabBar(this.tabBuilder(item, 'Tab ' + this.list[item])) 1923 }) 1924 } 1925 .onTabBarClick((index: number) => { 1926 this.currentIndex = index 1927 this.swiperController.changeIndex(index, true) 1928 }) 1929 .barMode(BarMode.Scrollable) 1930 .backgroundColor('#F1F3F5') 1931 .height(56) 1932 .width('100%') 1933 1934 Swiper(this.swiperController) { 1935 LazyForEach(this.swiperData, (item: string) => { 1936 Text(item.toString()) 1937 .onAppear(()=>{ 1938 console.info('onAppear ' + item.toString()) 1939 }) 1940 .onDisAppear(()=>{ 1941 console.info('onDisAppear ' + item.toString()) 1942 }) 1943 .width('100%') 1944 .height('100%') 1945 .backgroundColor(0xAFEEEE) 1946 .textAlign(TextAlign.Center) 1947 .fontSize(30) 1948 }, (item: string) => item) 1949 } 1950 .loop(false) 1951 .onChange((index: number) => { 1952 this.currentIndex = index 1953 }) 1954 .onAnimationStart((index: number, targetIndex: number, extraInfo: SwiperAnimationEvent) => { 1955 this.currentIndex = targetIndex 1956 this.tabsController.changeIndex(targetIndex) 1957 }) 1958 } 1959 } 1960} 1961``` 1962 1963 1964 1965### Example 13: Enabling Tabs to Exceed the Tab Bar Area 1966 1967This example shows how to enable tabs to exceed the tab bar area by setting the **clip** property of the **TabBar** using **barModifier**. 1968 1969```ts 1970// xxx.ets 1971import { CommonModifier } from '@kit.ArkUI'; 1972 1973@Entry 1974@Component 1975struct TabsBarModifierExample { 1976 @State selectedIndex: number = 2 1977 @State currentIndex: number = 2 1978 @State isClip: boolean = false 1979 @State tabBarModifier: CommonModifier = new CommonModifier() 1980 private controller: TabsController = new TabsController() 1981 1982 aboutToAppear(): void { 1983 this.tabBarModifier.clip(this.isClip) 1984 } 1985 1986 @Builder 1987 tabBuilder(title: string, targetIndex: number) { 1988 Column() { 1989 Image($r("app.media.startIcon")).width(30).height(30) 1990 Text(title).fontColor(this.selectedIndex === targetIndex ? '#1698CE' : '#6B6B6B') 1991 }.width('100%') 1992 .height(50) 1993 .justifyContent(FlexAlign.Center) 1994 .offset({ y: this.selectedIndex === targetIndex ? -15 : 0 }) 1995 } 1996 1997 build() { 1998 Column() { 1999 Tabs({ 2000 barPosition: BarPosition.End, 2001 index: this.currentIndex, 2002 controller: this.controller, 2003 barModifier: this.tabBarModifier 2004 }) { 2005 TabContent() { 2006 Column() { 2007 Text('Home tab content') 2008 }.width('100%').height('100%').backgroundColor('#00CB87').justifyContent(FlexAlign.Center) 2009 }.tabBar(this.tabBuilder('Home', 0)) 2010 2011 TabContent() { 2012 Column() { 2013 Text('Discover tab content') 2014 }.width('100%').height('100%').backgroundColor('#007DFF').justifyContent(FlexAlign.Center) 2015 }.tabBar(this.tabBuilder('Discover', 1)) 2016 2017 TabContent() { 2018 Column() { 2019 Text('Recommended tab content') 2020 }.width('100%').height('100%').backgroundColor('#FFBF00').justifyContent(FlexAlign.Center) 2021 }.tabBar(this.tabBuilder('Recommended', 2)) 2022 2023 TabContent() { 2024 Column() { 2025 Text('Me tab content') 2026 }.width('100%').height('100%').backgroundColor('#E67C92').justifyContent(FlexAlign.Center) 2027 }.tabBar(this.tabBuilder('Me',3)) 2028 } 2029 .vertical(false) 2030 .barMode(BarMode.Fixed) 2031 .barWidth(340) 2032 .barHeight(60) 2033 .onChange((index: number) => { 2034 this.currentIndex = index 2035 this.selectedIndex = index 2036 }) 2037 .width(340) 2038 .height(400) 2039 .backgroundColor('#F1F3F5') 2040 .scrollable(true) 2041 2042 Button("isClip: " + this.isClip) 2043 .margin({ top: 30 }) 2044 .onClick(() => { 2045 this.isClip = !this.isClip 2046 this.tabBarModifier.clip(this.isClip) 2047 }) 2048 }.width('100%') 2049 } 2050} 2051``` 2052 2053 2054 2055### Example 14: Aligning Tabs 2056 2057This example demonstrates how to align tabs by setting the **align** property of the **TabBar** using **barModifier**. 2058 2059```ts 2060// xxx.ets 2061import { CommonModifier } from '@kit.ArkUI'; 2062 2063@Entry 2064@Component 2065struct TabsBarModifierExample { 2066 private controller: TabsController = new TabsController() 2067 @State text: string = "Text" 2068 @State isVertical: boolean = false 2069 @State tabBarModifier: CommonModifier = new CommonModifier() 2070 2071 build() { 2072 Column() { 2073 Row() { 2074 Button("Alignment.Start ") 2075 .width('47%') 2076 .height(50) 2077 .margin({ top: 5 }) 2078 .onClick((event?: ClickEvent) => { 2079 this.tabBarModifier.align(Alignment.Start) 2080 }) 2081 .margin({ right: '6%', bottom: '12vp' }) 2082 Button("Alignment.End") 2083 .width('47%') 2084 .height(50) 2085 .margin({ top: 5 }) 2086 .onClick((event?: ClickEvent) => { 2087 this.tabBarModifier.align(Alignment.End) 2088 }) 2089 .margin({ bottom: '12vp' }) 2090 } 2091 2092 Row() { 2093 Button("Alignment.Center") 2094 .width('47%') 2095 .height(50) 2096 .margin({ top: 5 }) 2097 .onClick((event?: ClickEvent) => { 2098 this.tabBarModifier.align(Alignment.Center) 2099 }) 2100 .margin({ right: '6%', bottom: '12vp' }) 2101 Button("isVertical: " + this.isVertical) 2102 .width('47%') 2103 .height(50) 2104 .margin({ top: 5 }) 2105 .onClick((event?: ClickEvent) => { 2106 this.isVertical = !this.isVertical 2107 }) 2108 .margin({ bottom: '12vp' }) 2109 } 2110 2111 Row() { 2112 Button("Alignment.Top") 2113 .width('47%') 2114 .height(50) 2115 .margin({ top: 5 }) 2116 .onClick((event?: ClickEvent) => { 2117 this.tabBarModifier.align(Alignment.Top) 2118 }) 2119 .margin({ right: '6%', bottom: '12vp' }) 2120 Button("Alignment.Bottom") 2121 .width('47%') 2122 .height(50) 2123 .margin({ top: 5 }) 2124 .onClick((event?: ClickEvent) => { 2125 this.tabBarModifier.align(Alignment.Bottom) 2126 }) 2127 .margin({ bottom: '12vp' }) 2128 } 2129 2130 Tabs({ barPosition: BarPosition.End, controller: this.controller, barModifier: this.tabBarModifier }) { 2131 TabContent() { 2132 Column().width('100%').height('100%').backgroundColor(Color.Pink) 2133 }.tabBar(SubTabBarStyle.of(this.text)) 2134 2135 TabContent() { 2136 Column().width('100%').height('100%').backgroundColor(Color.Green) 2137 }.tabBar(SubTabBarStyle.of(this.text)) 2138 2139 TabContent() { 2140 Column().width('100%').height('100%').backgroundColor(Color.Blue) 2141 }.tabBar(SubTabBarStyle.of(this.text)) 2142 } 2143 .vertical(this.isVertical) 2144 .height('60%') 2145 .backgroundColor(0xf1f3f5) 2146 .barMode(BarMode.Scrollable) 2147 } 2148 .width('100%') 2149 .height(500) 2150 .margin({ top: 5 }) 2151 .padding('24vp') 2152 } 2153} 2154``` 2155 2156 2157 2158### Example 15: Setting the Tab Switching Animation 2159 2160This example demonstrates how to implement a tab switching animation by setting **animationMode**. 2161 2162```ts 2163// xxx.ets 2164class TabsMyDataSource implements IDataSource { 2165 private list: number[] = [] 2166 constructor(list: number[]) { 2167 this.list = list 2168 } 2169 totalCount(): number { 2170 return this.list.length 2171 } 2172 getData(index: number): number { 2173 return this.list[index] 2174 } 2175 registerDataChangeListener(listener: DataChangeListener): void { 2176 } 2177 unregisterDataChangeListener() { 2178 } 2179} 2180@Entry 2181@Component 2182struct TabsExample { 2183 @State currentIndex: number = 0 2184 @State currentAnimationMode: AnimationMode = AnimationMode.CONTENT_FIRST 2185 private controller: TabsController = new TabsController() 2186 private data: TabsMyDataSource = new TabsMyDataSource([]) 2187 private sum : number = 10 2188 aboutToAppear(): void { 2189 let list: number[] = [] 2190 for (let i = 0; i < this.sum; i++) { 2191 list.push(i); 2192 } 2193 this.data = new TabsMyDataSource(list) 2194 } 2195 @Builder 2196 tabBuilder(title: string,targetIndex: number) { 2197 Column(){ 2198 Text(title).fontColor(this.currentIndex === targetIndex ? '#FF0000' : '#6B6B6B') 2199 }.width('100%') 2200 .height(50) 2201 .justifyContent(FlexAlign.Center) 2202 } 2203 build() { 2204 Column() { 2205 Tabs({ barPosition: BarPosition.End, controller: this.controller, index: this.currentIndex }) { 2206 LazyForEach(this.data, (item: string) => { 2207 TabContent() { 2208 Column(){ 2209 Text('' + item) 2210 }.width('100%').height('100%').backgroundColor('#00CB87').justifyContent(FlexAlign.Center) 2211 }.tabBar(this.tabBuilder('P' + item, parseInt(item))) 2212 }, (item: string) => item) 2213 } 2214 .vertical(false) 2215 .barMode(BarMode.Fixed) 2216 .barWidth(360) 2217 .barHeight(60) 2218 .animationMode(this.currentAnimationMode) 2219 .animationDuration(4000) 2220 .onChange((index: number) => { 2221 this.currentIndex = index 2222 }) 2223 .width(360) 2224 .height(120) 2225 .backgroundColor('#F1F3F5') 2226 .scrollable(true) 2227 Text('AnimationMode:' + AnimationMode[this.currentAnimationMode]) 2228 Button('AnimationMode').width('50%').margin({ top: 1 }).height(25) 2229 .onClick(()=>{ 2230 if (this.currentAnimationMode === AnimationMode.CONTENT_FIRST) { 2231 this.currentAnimationMode = AnimationMode.ACTION_FIRST 2232 } else if (this.currentAnimationMode === AnimationMode.ACTION_FIRST) { 2233 this.currentAnimationMode = AnimationMode.NO_ANIMATION 2234 } else if (this.currentAnimationMode === AnimationMode.NO_ANIMATION) { 2235 this.currentAnimationMode = AnimationMode.CONTENT_FIRST_WITH_JUMP 2236 } else if (this.currentAnimationMode === AnimationMode.CONTENT_FIRST_WITH_JUMP) { 2237 this.currentAnimationMode = AnimationMode.ACTION_FIRST_WITH_JUMP 2238 } else if (this.currentAnimationMode === AnimationMode.ACTION_FIRST_WITH_JUMP) { 2239 this.currentAnimationMode = AnimationMode.CONTENT_FIRST 2240 } 2241 }) 2242 }.width('100%') 2243 } 2244} 2245``` 2246 2247 2248 2249