1# Tabs
2
3The **Tabs** component is a container component that allows users to switch between content views through tabs. Each tab page corresponds to a content view.
4
5>  **NOTE**
6>
7>  This component is supported since API version 7. Updates will be marked with a superscript to indicate their earliest API version.
8>
9>  Since API version 11, this component supports the safe area attribute by default, with the default attribute value being **expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.BOTTOM]))**. You can override this attribute to change the default behavior. In earlier versions, you need to use the [expandSafeArea](ts-universal-attributes-expand-safe-area.md) attribute to implement the safe area feature.
10
11
12## Child Components
13
14Custom components cannot be used as child components. Only the [TabContent](ts-container-tabcontent.md) child component is allowed, with support for [if/else](../../../quick-start/arkts-rendering-control-ifelse.md) and [ForEach](../../../quick-start/arkts-rendering-control-foreach.md) rendering control. In addition, the **if/else** and **ForEach** statements support **TabContent** components only, but not custom components.
15
16>  **NOTE**
17>
18>  If the child component has the **visibility** attribute set to **None** or **Hidden**, it is hidden but still takes up space in the layout.
19>
20>  The **TabContent** child component is not destroyed once it is displayed. If you need to implement lazy loading and resource release of pages, see [Example 12](#example-12-implementing-lazy-loading-and-resource-release-of-pages).
21
22
23## APIs
24
25Tabs(value?: {barPosition?: BarPosition, index?: number, controller?: TabsController})
26
27**Atomic service API**: This API can be used in atomic services since API version 14.
28
29**System capability**: SystemCapability.ArkUI.ArkUI.Full
30
31**Parameters**
32
33| Name        | Type                             | Mandatory  | Description                                    |
34| ----------- | --------------------------------- | ---- | ---------------------------------------- |
35| barPosition | [BarPosition](#barposition)| No   | Position of the **Tabs** component.<br>Default value: **BarPosition.Start**  |
36| index       | number                            | No   | Index of the currently displayed tab.<br>Default value: **0**<br>**NOTE**<br>A value less than 0 evaluates to the default value.<br>The value ranges from 0 to the number of **TabContent** nodes minus 1.<br>When the tab is switched by changing the index, the tab switching animation does not take effect. When **changeIndex** of **TabController** is used for tab switching, the tab switching animation is enabled by default. You can disable the animation by setting **animationDuration** to **0**.<br>Since API version 10, this parameter supports two-way binding through [$$](../../../quick-start/arkts-two-way-sync.md).|
37| controller  | [TabsController](#tabscontroller) | No   | Tab controller.                              |
38
39## BarPosition
40
41Enumerates the positions of the **Tabs** component.
42
43**Atomic service API**: This API can be used in atomic services since API version 11.
44
45**System capability**: SystemCapability.ArkUI.ArkUI.Full
46
47| Name | Description                                                        |
48| ----- | ------------------------------------------------------------ |
49| Start | If the **vertical** attribute is set to **true**, the tab is on the left of the container. If the **vertical** attribute is set to **false**, the tab is on the top of the container.|
50| End   | If the **vertical** attribute is set to **true**, the tab is on the right of the container. If the **vertical** attribute is set to **false**, the tab is at the bottom of the container.|
51
52
53## Attributes
54
55In addition to the [universal attributes](ts-universal-attributes-size.md), the following attributes are supported.
56
57### vertical
58
59vertical(value: boolean)
60
61Sets whether to use vertical tabs.
62
63**Atomic service API**: This API can be used in atomic services since API version 11.
64
65**System capability**: SystemCapability.ArkUI.ArkUI.Full
66
67**Parameters**
68
69| Name| Type   | Mandatory| Description                                                        |
70| ------ | ------- | ---- | ------------------------------------------------------------ |
71| value  | boolean | Yes  | Whether to use vertical tabs.<br>The value **true** means to use vertical tabs, and **false** means to use horizontal tabs.<br>Default value: **false**<br>If set to have a height of **auto**, horizontal tabs auto-adapt the height to child components, which is calculated as follows: Tab bar height + Divider width + Tab content height + Top and bottom paddings + Top and bottom border widths.<br>If set to have a width of **auto**, vertical tabs auto-adapt the width to child components, which is calculated as follows: Tab bar width + Divider width + Tab content width + Left and right paddings + Left and right border widths.<br>To avoid animation jitter when switching between tabs, maintain a consistent size for child components on each tab.|
72
73### scrollable
74
75scrollable(value: boolean)
76
77Sets whether the tabs are scrollable.
78
79**Atomic service API**: This API can be used in atomic services since API version 11.
80
81**System capability**: SystemCapability.ArkUI.ArkUI.Full
82
83**Parameters**
84
85| Name| Type   | Mandatory| Description                                                        |
86| ------ | ------- | ---- | ------------------------------------------------------------ |
87| value  | boolean | Yes  | Whether the tabs are scrollable.<br>**true** (default): The tabs are scrollable.<br> **false**: The tabs are not scrollable.|
88
89### barMode
90
91barMode(value: BarMode, options?: ScrollableBarModeOptions)
92
93Sets the tab bar layout mode.
94
95**Atomic service API**: This API can be used in atomic services since API version 11.
96
97**System capability**: SystemCapability.ArkUI.ArkUI.Full
98
99**Parameters**
100
101| Name               | Type                                                        | Mandatory| Description                                                        |
102| --------------------- | ------------------------------------------------------------ | ---- | ------------------------------------------------------------ |
103| value                 | [BarMode](#barmode)                                  | Yes  | Layout mode.<br>Default value: **BarMode.Fixed**                                                |
104| options<sup>10+</sup> | [ScrollableBarModeOptions](#scrollablebarmodeoptions10)| No  | Layout style of the tab bar in scrollable mode.<br>**NOTE**<br>This parameter is effective only when the tab bar is in horizontal scrollable mode.|
105
106### barMode<sup>10+</sup>
107
108barMode(value: BarMode.Fixed)
109
110Sets the tab bar layout mode to **BarMode.Fixed**.
111
112**Atomic service API**: This API can be used in atomic services since API version 11.
113
114**System capability**: SystemCapability.ArkUI.ArkUI.Full
115
116**Parameters**
117
118| Name   | Type                            | Mandatory| Description                                   |
119| -------- | -------------------------------- | ---- | ------------------------------------ |
120| value    | [BarMode.Fixed](#barmode)| Yes  | The width of each tab is determined by equally dividing the number of tabs by the bar width (or bar height in the vertical layout).  |
121
122### barMode<sup>10+</sup>
123
124barMode(value: BarMode.Scrollable, options: ScrollableBarModeOptions)
125
126Sets the tab bar layout mode to **BarMode.Scrollable**.
127
128**Atomic service API**: This API can be used in atomic services since API version 11.
129
130**System capability**: SystemCapability.ArkUI.ArkUI.Full
131
132**Parameters**
133
134| Name   | Type                             | Mandatory| Description                                   |
135| -------- | --------------------------------- | ---- | ------------------------------------- |
136| value    | [BarMode.Scrollable](#barmode)| Yes  | The width of each tab is determined by the actual layout. The tabs are scrollable in the following case: In horizontal layout, the total width exceeds the tab bar width; in vertical layout, the total height exceeds the tab bar height.       |
137| options | [ScrollableBarModeOptions](#scrollablebarmodeoptions10)| Yes  | Layout style of the tab bar in scrollable mode.<br>**NOTE**<br>This parameter is effective only when the tab bar is in scrollable mode. |
138
139### barWidth
140
141barWidth(value: Length)
142
143Sets the width of the tab bar. If the set value is less than 0 or greater than the width of the **Tabs** component, the default value is used.
144
145**Atomic service API**: This API can be used in atomic services since API version 11.
146
147**System capability**: SystemCapability.ArkUI.ArkUI.Full
148
149**Parameters**
150
151| Name| Type                                     | Mandatory| Description                                                        |
152| ------ | ----------------------------------------- | ---- | ------------------------------------------------------------ |
153| value  | [Length](ts-types.md#length)<sup>8+</sup> | Yes  | Width of the tab bar.<br>Default value:<br>If neither [SubTabBarStyle](ts-container-tabcontent.md#subtabbarstyle9) nor [BottomTabBarStyle](ts-container-tabcontent.md#bottomtabbarstyle9) is set, and the **vertical** attribute is **false**, the default value is the width of the **Tabs** component.<br>If neither **SubTabBarStyle** nor **BottomTabBarStyle** is set, and the **vertical** attribute is **true**, the default value is 56 vp.<br>If **SubTabBarStyle** is set, and the **vertical** attribute is **false**, the default value is the width of the **Tabs** component.<br>If **SubTabBarStyle** is set, and the **vertical** attribute is **true**, the default value is 56 vp.<br>If **BottomTabBarStyle** is set, and the **vertical** attribute is **true**, the default value is 96 vp.<br>If **BottomTabBarStyle** is set, and the **vertical** attribute is **false**, the default value is the width of the **Tabs** component.|
154
155### barHeight
156
157barHeight(value: Length)
158
159Sets the height of the tab bar. If this attribute is set to **'auto'**, which takes effect only in horizontal mode, the tab bar adapts to the height of its child components. If the set value is less than 0 or greater than the height of the **Tabs** component, the default value is used.
160
161In versions earlier than API version 14, setting **barHeight** to a fixed value restricts the tab bar from extending beyond the bottom safe area. Since API version 14, the [safeAreaPadding](./ts-universal-attributes-size.md#safeareapadding14) attribute is supported. When **safeAreaPadding** is set to 0 or is not explicitly set, the tab bar is allowed to extend beyond the bottom safe area.
162
163**Atomic service API**: This API can be used in atomic services since API version 11.
164
165**System capability**: SystemCapability.ArkUI.ArkUI.Full
166
167**Parameters**
168
169| Name| Type                                     | Mandatory| Description                                                        |
170| ------ | ----------------------------------------- | ---- | ------------------------------------------------------------ |
171| value  | [Length](ts-types.md#length)<sup>8+</sup> | Yes  | Height of the tab bar.<br>Default value:<br>If the tab bar has the **vertical** attribute set to **false** and does not have a style specified, the default value is 56 vp.<br>If the tab bar has the **vertical** attribute set to **true** and does not have a style specified, the default value is the height of the **Tabs** component.<br>If [SubTabBarStyle](ts-container-tabcontent.md#subtabbarstyle9) is set, and the **vertical** attribute is **false**, the default value is 56 vp.<br>If **SubTabBarStyle** is set, and the **vertical** attribute is **true**, the default value is the height of the **Tabs** component.<br>If [BottomTabBarStyle](ts-container-tabcontent.md#bottomtabbarstyle9) is set, and the **vertical** attribute is **true**, the default value is the height of the **Tabs** component.<br>If **BottomTabBarStyle** is set, and the **vertical** attribute is **false**, the default value is 56 vp in versions earlier than API version 12 and 48 vp since API version 12.|
172
173### animationDuration
174
175animationDuration(value: number)
176
177Sets the length of time required to complete the tab switching animation, which is initiated by clicking a specific tab or by calling the **changeIndex** API of **TabsController**. This parameter cannot be set in percentage.
178
179**Atomic service API**: This API can be used in atomic services since API version 11.
180
181**System capability**: SystemCapability.ArkUI.ArkUI.Full
182
183**Parameters**
184
185| Name| Type  | Mandatory| Description                                                        |
186| ------ | ------ | ---- | ------------------------------------------------------------ |
187| value  | number | Yes  | Length of time required to complete the tab switching animation, which is initiated by clicking a specific tab or by calling the **changeIndex** API of **TabsController**.<br>The default value varies.<br>API version 10 and earlier versions: If this parameter is set to **null** or is not set, the default value **0** is used, which means that no tab switching animation is displayed when a specific tab is clicked or the **changeIndex** API of **TabsController** is called. If this parameter is set to **undefined** or a value less than 0, the default value **300** is used.<br>API version 11 and later versions: If this parameter is set to an invalid value or is not set, the default value is **0** when the tab bar is set to **BottomTabBarStyle** and **300** when the tab bar is set to any other style.<br>Unit: ms|
188
189### animationMode<sup>12+</sup>
190
191animationMode(mode: Optional\<AnimationMode\>)
192
193Sets the animation mode for switching between tabs.
194
195**Atomic service API**: This API can be used in atomic services since API version 12.
196
197**System capability**: SystemCapability.ArkUI.ArkUI.Full
198
199**Parameters**
200
201| Name| Type  | Mandatory| Description                                                        |
202| ------ | ------ | ---- | ------------------------------------------------------------ |
203| mode  | Optional\<[AnimationMode](#animationmode12)\>| Yes  | Animation mode for switching between tabs.<br>Default value:<br>**AnimationMode.CONTENT_FIRST**, indicating that the content of the target page is loaded before the switching animation starts in the process of switching between tabs.|
204
205### barPosition<sup>9+</sup>
206
207barPosition(value: BarPosition)
208
209Position of the **Tabs** component.
210
211**Atomic service API**: This API can be used in atomic services since API version 11.
212
213**System capability**: SystemCapability.ArkUI.ArkUI.Full
214
215**Parameters**
216
217| Name| Type                              | Mandatory| Description                 |
218| ----- | ---------------------------------- | ---- | -------------------- |
219| value | [BarPosition](#barposition)| Yes | Position of the **Tabs** component.<br>Default value: **BarPosition.Start**  |
220
221### divider<sup>10+</sup>
222
223divider(value: DividerStyle | null)
224
225Sets the divider style for the **TabBar** and **TabContent** components.
226
227**Atomic service API**: This API can be used in atomic services since API version 11.
228
229**System capability**: SystemCapability.ArkUI.ArkUI.Full
230
231**Parameters**
232
233| Name| Type                                                     | Mandatory| Description                                                        |
234| ------ | --------------------------------------------------------- | ---- | ------------------------------------------------------------ |
235| value  | [DividerStyle](#dividerstyle10) \| null | Yes  | Divider style. By default, the divider is not displayed.<br>**DividerStyle**: divider style.<br>**null**: The divider is not displayed.|
236
237### fadingEdge<sup>10+</sup>
238
239fadingEdge(value: boolean)
240
241Sets whether the tab fades out when it exceeds the container width. It is recommended that this attribute be used together with the **barBackgroundColor** attribute. If the **barBackgroundColor** attribute is not defined, the tab fades out in white when it exceeds the container width by default.
242
243**Atomic service API**: This API can be used in atomic services since API version 11.
244
245**System capability**: SystemCapability.ArkUI.ArkUI.Full
246
247**Parameters**
248
249| Name| Type   | Mandatory| Description                                              |
250| ------ | ------- | ---- | -------------------------------------------------- |
251| value  | boolean | Yes  | Whether the tab fades out when it exceeds the container width.<br>Default value: **true**|
252
253### barOverlap<sup>10+</sup>
254
255barOverlap(value: boolean)
256
257Sets whether the tab bar is superimposed on the **TabContent** component after having its background blurred.
258
259**Atomic service API**: This API can be used in atomic services since API version 11.
260
261**System capability**: SystemCapability.ArkUI.ArkUI.Full
262
263**Parameters**
264
265| Name| Type   | Mandatory| Description                                                        |
266| ------ | ------- | ---- | ------------------------------------------------------------ |
267| value  | boolean | Yes  | Whether the tab bar is superimposed on the **TabContent** component after having its background blurred. When **barOverlap** is set to **true**, the default value of **BlurStyle** for the **TabBar** is automatically changed to **'BlurStyle.COMPONENT_THICK'**.<br>Default value: **false**|
268
269### barBackgroundColor<sup>10+</sup>
270
271barBackgroundColor(value: ResourceColor)
272
273Background color of the tab bar.
274
275**Atomic service API**: This API can be used in atomic services since API version 11.
276
277**System capability**: SystemCapability.ArkUI.ArkUI.Full
278
279**Parameters**
280
281| Name| Type                                      | Mandatory| Description                                |
282| ------ | ------------------------------------------ | ---- | ------------------------------------ |
283| value  | [ResourceColor](ts-types.md#resourcecolor) | Yes  | Background color of the tab bar.<br>Default value: **Color.Transparent**|
284
285### barBackgroundBlurStyle<sup>11+</sup>
286
287barBackgroundBlurStyle(value: BlurStyle)
288
289Sets the background blur style of the tab bar.
290
291**Atomic service API**: This API can be used in atomic services since API version 11.
292
293**System capability**: SystemCapability.ArkUI.ArkUI.Full
294
295**Parameters**
296
297| Name| Type                                        | Mandatory| Description                                    |
298| ------ | -------------------------------------------- | ---- | ---------------------------------------- |
299| value  | [BlurStyle](ts-universal-attributes-background.md#blurstyle9) | Yes  | Background blur style of the tab bar.<br>Default value: **BlurStyle.NONE**|
300
301### barGridAlign<sup>10+</sup>
302
303barGridAlign(value: BarGridColumnOptions)
304
305Sets the visible area of the tab bar in grid mode. For details, see **BarGridColumnOptions**. This attribute is effective only in horizontal mode. It is not applicable to [XS, XL, and XXL devices](../../../ui/arkts-layout-development-grid-layout.md#grid-breakpoints).
306
307**Atomic service API**: This API can be used in atomic services since API version 11.
308
309**System capability**: SystemCapability.ArkUI.ArkUI.Full
310
311**Parameters**
312
313| Name| Type                                                   | Mandatory| Description                              |
314| ------ | ------------------------------------------------------- | ---- | ---------------------------------- |
315| value  | [BarGridColumnOptions](#bargridcolumnoptions10) | Yes  | Visible area of the tab bar in grid mode.|
316
317### edgeEffect<sup>12+</sup>
318
319edgeEffect(edgeEffect: Optional&lt;EdgeEffect&gt;)
320
321Sets the edge effect used when the boundary of the scrolling area is reached.
322
323**Atomic service API**: This API can be used in atomic services since API version 12.
324
325**System capability**: SystemCapability.ArkUI.ArkUI.Full
326
327**Parameters**
328
329| Name| Type                                         | Mandatory| Description                                        |
330| ------ | --------------------------------------------- | ---- | -------------------------------------------- |
331| edgeEffect  | Optional&lt;[EdgeEffect](ts-appendix-enums.md#edgeeffect)&gt; | Yes  | Effect used when the boundary of the scrolling area is reached.<br>Default value: **EdgeEffect.Spring**|
332
333## DividerStyle<sup>10+</sup>
334
335Describes the divider style.
336
337**Atomic service API**: This API can be used in atomic services since API version 11.
338
339**System capability**: SystemCapability.ArkUI.ArkUI.Full
340
341| Name         | Type                                    | Mandatory  | Description                                      |
342| ----------- | ---------------------------------------- | ---- | ---------------------------------------- |
343| strokeWidth | [Length](ts-types.md#length)             | Yes   | Width of the divider. It cannot be set in percentage.<br>Default value: **0.0**<br>Unit: vp          |
344| color       | [ResourceColor](ts-types.md#resourcecolor) | No   | Color of the divider.<br>Default value: **#33182431**               |
345| startMargin | [Length](ts-types.md#length)             | No   | Distance between the divider and the top of the sidebar. It cannot be set in percentage.<br>Default value: **0.0**<br>Unit: vp|
346| endMargin   | [Length](ts-types.md#length)             | No   | Distance between the divider and the bottom of the sidebar. It cannot be set in percentage.<br>Default value: **0.0**<br>Unit: vp|
347
348## BarGridColumnOptions<sup>10+</sup>
349
350Implements a **BarGridColumnOptions** object for setting the visible area of the tab bar in grid mode, including the column margin and gutter, as well as the number of columns occupied by tabs under small, medium, and large screen sizes.
351
352**Atomic service API**: This API can be used in atomic services since API version 11.
353
354**System capability**: SystemCapability.ArkUI.ArkUI.Full
355
356| Name         | Type                                    | Mandatory  | Description                                      |
357| ----------- | ---------------------------------------- | ---- | ---------------------------------------- |
358| margin | [Dimension](ts-types.md#dimension10)             | No   | Column margin in grid mode. It cannot be set in percentage.<br>Default value: **24.0**<br>Unit: vp                       |
359| gutter      | [Dimension](ts-types.md#dimension10) | No   | Column gutter (that is, gap between columns) in grid mode. It cannot be set in percentage.<br>Default value: **24.0**<br>Unit: vp                    |
360| sm | number            | No   | Number of columns occupied by a tab on a screen whose width is greater than or equal to 320 vp but less than 600 vp.<br>The value must be a non-negative even number. The default value is **-1**, indicating that the tab takes up the entire width of the tab bar.|
361| md   | number          | No   | Number of columns occupied by a tab on a screen whose width is greater than or equal to 600 vp but less than 800 vp.<br>The value must be a non-negative even number. The default value is **-1**, indicating that the tab takes up the entire width of the tab bar.|
362| lg   | number           | No   | Number of columns occupied by a tab on a screen whose width is greater than or equal to 840 vp but less than 1024 vp.<br>The value must be a non-negative even number. The default value is **-1**, indicating that the tab takes up the entire width of the tab bar.|
363
364## ScrollableBarModeOptions<sup>10+</sup>
365
366Implements a **ScrollableBarModeOptions** object.
367
368**Atomic service API**: This API can be used in atomic services since API version 11.
369
370**System capability**: SystemCapability.ArkUI.ArkUI.Full
371
372| Name         | Type                                    | Mandatory  | Description                                      |
373| ----------- | ---------------------------------------- | ---- | ---------------------------------------- |
374| margin | [Dimension](ts-types.md#dimension10)          | No   | Left and right margin of the tab bar in scrollable mode. It cannot be set in percentage.<br>Default value: **0.0**<br>Unit: vp                   |
375| nonScrollableLayoutStyle      | [LayoutStyle](#layoutstyle10) | No   | Tab layout mode of the tab bar when not scrolling in scrollable mode.<br>Default value: **LayoutStyle.ALWAYS_CENTER**          |
376
377## BarMode
378
379Enumerates layout modes of the tab bar.
380
381**Atomic service API**: This API can be used in atomic services since API version 11.
382
383**System capability**: SystemCapability.ArkUI.ArkUI.Full
384
385| Name       | Value| Description                                    |
386| ---------- | -- | ---------------------------------------- |
387| Scrollable | 0  | The width of each tab is determined by the actual layout. The tabs are scrollable in the following case: In horizontal layout, the total width exceeds the tab bar width; in vertical layout, the total height exceeds the tab bar height.|
388| Fixed      | 1  | The width of each tab is determined by equally dividing the number of tabs by the bar width (or bar height in the vertical layout).|
389
390## AnimationMode<sup>12+</sup>
391
392Enumerates the animation modes for switching between tabs.
393
394**Atomic service API**: This API can be used in atomic services since API version 12.
395
396**System capability**: SystemCapability.ArkUI.ArkUI.Full
397
398| Name         | Value  | Description                                                        |
399| ------------- | ---- | ------------------------------------------------------------ |
400| CONTENT_FIRST | 0    | Load the content of the target page before starting the switching animation.                            |
401| ACTION_FIRST  | 1    | Start the switching animation before loading the content of the target page. For the settings to take effect, the height and width of tabs must be set to **auto**.|
402| NO_ANIMATION  | 2    | The switching animation is disabled.                                                |
403
404## LayoutStyle<sup>10+</sup>
405
406Enumerates the tab layout styles of the tab bar when not scrolling in scrollable mode.
407
408**Atomic service API**: This API can be used in atomic services since API version 11.
409
410**System capability**: SystemCapability.ArkUI.ArkUI.Full
411
412| Name        | Value| Description                                    |
413| ---------- | -- | ---------------------------------------- |
414| ALWAYS_CENTER | 0 | If the tab content exceeds the tab bar width, the tabs are scrollable.<br>If not, the tabs are compactly centered on the tab bar and not scrollable.|
415| ALWAYS_AVERAGE_SPLIT | 1 | If the tab content exceeds the tab bar width, the tabs are scrollable.<br>If not, the tabs are not scrollable, and the width of the tab bar is evenly distributed among all tabs.|
416| SPACE_BETWEEN_OR_CENTER      | 2 | If the tab content exceeds the tab bar width, the tabs are scrollable.<br>If the tab content exceeds half the width of the tab bar but is still within the tab bar width, the tabs are compactly centered and not scrollable.<br>If the tab content does not exceed half the width of the tab bar, the tabs are centered within half the width of the tab bar with even spacing between them and are not scrollable.|
417
418## Events
419
420In addition to the [universal events](ts-universal-events-click.md), the following events are supported.
421
422### onChange
423
424onChange(event: (index: number) =&gt; void)
425
426Triggered when a tab is switched.
427
428This event is triggered when any of the following conditions is met:
429
4301. The swiping animation is completed, followed by tab switching.
431
4322. The [Controller](#tabscontroller) API is called.
433
4343. The attribute value is updated using a [state variable](../../../quick-start/arkts-state.md).
435
4364. A tab is clicked.
437
438>  **NOTE**
439>
440>  When a custom tab is used, the synchronization between tabs and swipe gestures in the **onChange** event may be performed only after tab switching occurs. This can lead to a delay in switching to the custom tab. To address this issue, listen for the current tab index in [onAnimationStart](#onanimationstart11) and update the tab index accordingly. For details about the implementation, see [Example 1](#example-1-setting-up-custom-tab-switching-synchronization).
441
442**Atomic service API**: This API can be used in atomic services since API version 11.
443
444**System capability**: SystemCapability.ArkUI.ArkUI.Full
445
446**Parameters**
447
448| Name| Type  | Mandatory| Description                                |
449| ------ | ------ | ---- | ------------------------------------ |
450| index  | number | Yes  | Index of the clicked tab. The index starts from 0.|
451
452### onTabBarClick<sup>10+</sup>
453
454onTabBarClick(event: (index: number) =&gt; void)
455
456Triggered when a tab is clicked.
457
458**Atomic service API**: This API can be used in atomic services since API version 11.
459
460**System capability**: SystemCapability.ArkUI.ArkUI.Full
461
462**Parameters**
463
464| Name| Type  | Mandatory| Description                                |
465| ------ | ------ | ---- | ------------------------------------ |
466| event  | [Callback](./ts-types.md#callback12)\<number> | Yes  | Index of the clicked tab. The index starts from 0.|
467
468### onAnimationStart<sup>11+</sup>
469
470onAnimationStart(handler: OnTabsAnimationStartCallback)
471
472Triggered when the tab switching animation starts. This callback is not triggered when **animationDuration** is set to **0**, which effectively disables the animation.
473
474**Atomic service API**: This API can be used in atomic services since API version 12.
475
476**System capability**: SystemCapability.ArkUI.ArkUI.Full
477
478**Parameters**
479
480| Name     | Type                                                  | Mandatory| Description                                                        |
481| ----------- | ------------------------------------------------------ | ---- | ------------------------------------------------------------ |
482| index       | number                                                 | Yes  | Index of the currently displayed element.                                        |
483| targetIndex | number                                                 | Yes  | Index of the target element to switch to.                                    |
484| event       | [TabsAnimationEvent](#tabsanimationevent11) | Yes  | Extra information of the animation, including the offset of the currently displayed element and target element relative to the start position of the **Tabs** along the main axis, and the hands-off velocity.|
485
486### onAnimationEnd<sup>11+</sup>
487
488onAnimationEnd(handler: (index: number, event: TabsAnimationEvent) => void)
489
490Triggered when the tab switching animation ends. This event is triggered when the tab switching animation ends, whether it is caused by gesture interruption or not. This callback is not triggered when **animationDuration** is set to **0**, which effectively disables the animation.
491
492**Atomic service API**: This API can be used in atomic services since API version 12.
493
494**System capability**: SystemCapability.ArkUI.ArkUI.Full
495
496**Parameters**
497
498| Name| Type                                                  | Mandatory| Description                                                        |
499| ------ | ------------------------------------------------------ | ---- | ------------------------------------------------------------ |
500| index  | number                                                 | Yes  | Index of the currently displayed element.                                        |
501| event  | [TabsAnimationEvent](#tabsanimationevent11) | Yes  | Extra information of the animation, which is the offset of the currently displayed element relative to the start position of the **Tabs** along the main axis.|
502
503### onGestureSwipe<sup>11+</sup>
504
505onGestureSwipe(handler: (index: number, event: TabsAnimationEvent) => void)
506
507Triggered on a frame-by-frame basis when the tab is switched by a swipe.
508
509**Atomic service API**: This API can be used in atomic services since API version 12.
510
511**System capability**: SystemCapability.ArkUI.ArkUI.Full
512
513**Parameters**
514
515| Name| Type                                                  | Mandatory| Description                                                        |
516| ------ | ------------------------------------------------------ | ---- | ------------------------------------------------------------ |
517| index  | number                                                 | Yes  | Index of the currently displayed element.                                        |
518| event  | [TabsAnimationEvent](#tabsanimationevent11) | Yes  | Extra information of the animation, which is the offset of the currently displayed element relative to the start position of the **Tabs** along the main axis.|
519
520### customContentTransition<sup>11+</sup>
521
522customContentTransition(delegate: (from: number, to: number) => TabContentAnimatedTransition \| undefined)
523
524Sets the custom tab switching animation.
525
526Instructions:
527
5281. When the custom tab switching animation is used, the default switching animation of the **Tabs** component is disabled, and tabs cannot be switched through swiping.<br>2. The value **undefined** means not to use the custom tab switching animation, in which case the default switching animation is used.<br>3. The custom tab switching animation cannot be interrupted.<br>4. Currently, the custom tab switching animation can be triggered only by clicking a tab or by calling the **TabsController.changeIndex()** API.<br>5. When the custom tab switching animation is used, the **Tabs** component supports all events except **onGestureSwipe**.<br>6. Notes about the **onChange** and **onAnimationEnd** events: If the second custom animation is triggered during the execution of the first custom animation, the **onChange** and **onAnimationEnd** events of the first custom animation will be triggered when the second custom animation starts.<br>7. When the custom animation is used, the stack layout is used for pages involved in the animation. If the **zIndex** attribute is not set for related pages, the **zIndex** values of all pages are the same. In this case, the pages are rendered in the order in which they are added to the component tree (that is, the sequence of page indexes). In light of this, to control the rendering levels of pages, set the **zIndex** attribute of the pages.
529
530**Widget capability**: This API can be used in ArkTS widgets since API version 11.
531
532**Atomic service API**: This API can be used in atomic services since API version 12.
533
534**System capability**: SystemCapability.ArkUI.ArkUI.Full
535
536**Parameters**
537
538| Name| Type  | Mandatory| Description                           |
539| ------ | ------ | ---- | ------------------------------- |
540| from   | number | Yes  | Index of the currently displayed tab before the animation starts.|
541| to     | number | Yes  | Index of the target tab before the animation starts.|
542
543**Return value**
544
545| Type                                                        | Description                    |
546| ------------------------------------------------------------ | ------------------------ |
547| [TabContentAnimatedTransition](#tabcontentanimatedtransition11) \| undefined | Information about the custom tab switching animation.|
548
549### onContentWillChange<sup>12+</sup>
550
551onContentWillChange(handler: (currentIndex: number, comingIndex: number) => boolean)
552
553Triggered when a new page is about to be displayed.
554
555Specifically, this event is triggered in the following cases:
556
5571. When the user swipes on the **TabContent** component (provided that it supports swiping) to switch to a new page.
558
5592. When **TabsController.changeIndex** is called to switch to a new page.
560
5613. When the **index** attribute is changed to switch to a new page.
562
5634. When the user clicks a tab on the tab bar to switch to a new page.
564
5655. When the user presses the left or right arrow key on the keyboard to switch to a new page while the tab bar is focused.
566
567**Atomic service API**: This API can be used in atomic services since API version 12.
568
569**System capability**: SystemCapability.ArkUI.ArkUI.Full
570
571**Parameters**
572
573| Name      | Type  | Mandatory| Description                                      |
574| ------------ | ------ | ---- | ------------------------------------------ |
575| currentIndex | number | Yes  | Index of the active tab. The index starts from 0.|
576| comingIndex  | number | Yes  | Index of the new tab to be displayed.             |
577
578**Return value**
579
580| Type   | Description                                                        |
581| ------- | ------------------------------------------------------------ |
582| boolean | The value **true** means that the tab can switch to the new page.<br>The value **false** means that the tab cannot switch to the new page and will remain on the current page.|
583
584## TabsAnimationEvent<sup>11+</sup>
585
586Describes the animation information of the **Tabs** component.
587
588**Atomic service API**: This API can be used in atomic services since API version 12.
589
590**System capability**: SystemCapability.ArkUI.ArkUI.Full
591
592| Name           | Type     | Read Only| Optional| Description                                      |
593| ------------- | ---------- | ---- | ---- | ------------------------ |
594| currentOffset | number | No| No| Offset of the currently displayed element relative to the start position of the **Tabs** component along the main axis.<br> Unit: vp<br>Default value: **0**|
595| targetOffset | number | No| No| Offset of the target element relative to the start position of the **Tabs** component along the main axis.<br> Unit: vp<br>Default value: **0**|
596| velocity | number | No| No| Hands-off velocity at the beginning of the animation. Unit: VP/S<br>Default value: **0**|
597
598## TabContentAnimatedTransition<sup>11+</sup>
599
600Provides the information about the custom tab page switching animation.
601
602**Widget capability**: This API can be used in ArkTS widgets since API version 11.
603
604**Atomic service API**: This API can be used in atomic services since API version 12.
605
606**System capability**: SystemCapability.ArkUI.ArkUI.Full
607
608| Name           | Type        | Mandatory  | Description                                      |
609| ------------- | ---------------------- | ---- |---------------------- |
610| timeout | number | No| Timeout for the custom switching animation. The timer starts when the switching begins. If this timeframe passes without you calling the **finishTransition** API in [TabContentTransitionProxy](#tabcontenttransitionproxy11), the component will assume that the custom animation has ended and will proceed directly with subsequent operations. Unit: ms<br>Default value: **1000**|
611| transition | [Callback](./ts-types.md#callback12)\<[TabContentTransitionProxy](#tabcontenttransitionproxy11)> | Yes| Content of the custom switching animation.|
612
613## TabContentTransitionProxy<sup>11+</sup>
614
615Implements the proxy object returned during the execution of the custom switching animation of the **Tabs** component. You can use this object to obtain the start and target pages for the custom tab switching animation. In addition, you can call the **finishTransition** API of this object to notify the **Tabs** component of the ending of the custom animation.
616
617**Widget capability**: This API can be used in ArkTS widgets since API version 11.
618
619**Atomic service API**: This API can be used in atomic services since API version 12.
620
621**System capability**: SystemCapability.ArkUI.ArkUI.Full
622
623### Attributes
624
625| Name | Type    | Read Only| Optional| Description                        |
626| ----- | ------- | ---- | ---- | --------------------------- |
627| from | number | No| No| Index of the starting page of the custom animation.|
628| to | number | No| No| Index of the target tab to switch to.|
629
630### finishTransition
631
632finishTransition(): void
633
634Notifies the **Tabs** component that the custom animation has finished playing.
635
636**Widget capability**: This API can be used in ArkTS widgets since API version 11.
637
638**Atomic service API**: This API can be used in atomic services since API version 12.
639
640**System capability**: SystemCapability.ArkUI.ArkUI.Full
641
642## TabsController
643
644Defines a tab controller, which is used to control switching of tabs. One **TabsController** cannot control multiple **Tabs** components.
645
646**Atomic service API**: This API can be used in atomic services since API version 11.
647
648**System capability**: SystemCapability.ArkUI.ArkUI.Full
649
650### Objects to Import
651
652```ts
653let controller: TabsController = new TabsController()
654```
655
656### constructor
657
658constructor()
659
660A constructor used to create a **TabsController** object.
661
662**Atomic service API**: This API can be used in atomic services since API version 11.
663
664**System capability**: SystemCapability.ArkUI.ArkUI.Full
665
666### changeIndex
667
668changeIndex(value: number): void
669
670Switches to the specified tab.
671
672**Atomic service API**: This API can be used in atomic services since API version 11.
673
674**System capability**: SystemCapability.ArkUI.ArkUI.Full
675
676**Parameters**
677
678| Name  | Type  | Mandatory  | Description                                    |
679| ----- | ------ | ---- | ---------------------------------------- |
680| value | number | Yes   | Index of the tab. The value starts from 0.<br>**NOTE**<br>If this parameter is set to a value less than 0 or greater than the maximum number, the default value **0** is used.|
681
682### preloadItems<sup>12+</sup>
683
684preloadItems(indices: Optional\<Array\<number>>): Promise\<void>
685
686Preloads child nodes. After this API is called, all specified child nodes will be loaded at once. Therefore, for performance considerations, it is recommended that you load child nodes in batches.
687
688**Atomic service API**: This API can be used in atomic services since API version 12.
689
690**System capability**: SystemCapability.ArkUI.ArkUI.Full
691
692**Parameters**
693
694| Name  | Type  | Mandatory  | Description                                    |
695| ----- | ------ | ---- | ---------------------------------------- |
696| indices | Optional\<Array\<number>> | Yes| Array of indexes of the child nodes to preload.<br>The default value is an empty array.|
697
698**Return value**
699
700| Type                                                        | Description                    |
701| ------------------------------------------------------------ | ------------------------ |
702| Promise\<void> | Promise used to return the value.|
703
704**Error codes**
705
706For details about the error codes, see [Universal Error Codes](../../errorcode-universal.md).
707
708| ID  | Error Message                                     |
709| --------   | -------------------------------------------- |
710| 401 | Parameter invalid. Possible causes: 1. The parameter type is not Array\<number>; 2. The parameter is an empty array; 3. The parameter contains an invalid index. |
711
712### setTabBarTranslate<sup>13+</sup>
713
714setTabBarTranslate(translate: TranslateOptions): void
715
716Sets the translation distance of the tab bar.
717
718> **NOTE**
719>
720> When **bindTabsToScrollable** or **bindTabsToNestedScrollable** is used to bind the **Tabs** component with a scrollable container, scrolling the container will trigger the display and hide animations of the tab bar for all **Tabs** components bound to it. In this case, calling the **setTabBarTranslate** API has no effect. Therefore, avoid using **bindTabsToScrollable**, **bindTabsToNestedScrollable**, and **setTabBarTranslate** simultaneously.
721>
722
723**Atomic service API**: This API can be used in atomic services since API version 13.
724
725**System capability**: SystemCapability.ArkUI.ArkUI.Full
726
727**Parameters**
728
729| Name  | Type  | Mandatory  | Description                                    |
730| ----- | ------ | ---- | ---------------------------------------- |
731| translate | [TranslateOptions](ts-universal-attributes-transformation.md#translateoptions) | Yes| Translation distance of the tab bar.|
732
733### setTabBarOpacity<sup>13+</sup>
734
735setTabBarOpacity(opacity: number): void
736
737Sets the opacity of the tab bar.
738
739> **NOTE**
740>
741> When **bindTabsToScrollable** or **bindTabsToNestedScrollable** is used to bind the **Tabs** component with a scrollable container, scrolling the container will trigger the display and hide animations of the tab bar for all **Tabs** components bound to it. In this case, calling the **setTabBarOpacity** API has no effect. Therefore, avoid using **bindTabsToScrollable**, **bindTabsToNestedScrollable**, and **setTabBarOpacity** simultaneously.
742>
743
744**Atomic service API**: This API can be used in atomic services since API version 13.
745
746**System capability**: SystemCapability.ArkUI.ArkUI.Full
747
748**Parameters**
749
750| Name  | Type  | Mandatory  | Description                                    |
751| ----- | ------ | ---- | ---------------------------------------- |
752| opacity | number | Yes| Opacity of the tab bar. The value range is [0.0, 1.0].|
753
754## Example
755
756### Example 1: Setting Up Custom Tab Switching Synchronization
757
758This example demonstrates how to use **onAnimationStart** and **onChange** to synchronize tabs with swiping gestures during tab switching.
759
760```ts
761// xxx.ets
762@Entry
763@Component
764struct TabsExample {
765  @State fontColor: string = '#182431'
766  @State selectedFontColor: string = '#007DFF'
767  @State currentIndex: number = 0
768  @State selectedIndex: number = 0
769  private controller: TabsController = new TabsController()
770
771  @Builder tabBuilder(index: number, name: string) {
772    Column() {
773      Text(name)
774        .fontColor(this.selectedIndex === index ? this.selectedFontColor : this.fontColor)
775        .fontSize(16)
776        .fontWeight(this.selectedIndex === index ? 500 : 400)
777        .lineHeight(22)
778        .margin({ top: 17, bottom: 7 })
779      Divider()
780        .strokeWidth(2)
781        .color('#007DFF')
782        .opacity(this.selectedIndex === index ? 1 : 0)
783    }.width('100%')
784  }
785
786  build() {
787    Column() {
788      Tabs({ barPosition: BarPosition.Start, index: this.currentIndex, controller: this.controller }) {
789        TabContent() {
790          Column().width('100%').height('100%').backgroundColor('#00CB87')
791        }.tabBar(this.tabBuilder(0, 'green'))
792
793        TabContent() {
794          Column().width('100%').height('100%').backgroundColor('#007DFF')
795        }.tabBar(this.tabBuilder(1, 'blue'))
796
797        TabContent() {
798          Column().width('100%').height('100%').backgroundColor('#FFBF00')
799        }.tabBar(this.tabBuilder(2, 'yellow'))
800
801        TabContent() {
802          Column().width('100%').height('100%').backgroundColor('#E67C92')
803        }.tabBar(this.tabBuilder(3, 'pink'))
804      }
805      .vertical(false)
806      .barMode(BarMode.Fixed)
807      .barWidth(360)
808      .barHeight(56)
809      .animationDuration(400)
810      .onChange((index: number) => {
811        // currentIndex controls which tab is displayed.
812        this.currentIndex = index
813        this.selectedIndex = index
814      })
815      .onAnimationStart((index: number, targetIndex: number, event: TabsAnimationEvent) => {
816        if (index === targetIndex) {
817          return
818        }
819        // selectedIndex controls the color switching for the image and text in the custom tab bar.
820        this.selectedIndex = targetIndex
821      })
822      .width(360)
823      .height(296)
824      .margin({ top: 52 })
825      .backgroundColor('#F1F3F5')
826    }.width('100%')
827  }
828}
829```
830
831![tabs2](figures/tabs2.gif)
832
833### Example 2: Setting the Basic Attributes of the Divider
834
835This example uses **divider** to present dividers in different styles.
836
837```ts
838// xxx.ets
839@Entry
840@Component
841struct TabsDivider1 {
842  private controller1: TabsController = new TabsController()
843  @State dividerColor: string = 'red'
844  @State strokeWidth: number = 2
845  @State startMargin: number = 0
846  @State endMargin: number = 0
847  @State nullFlag: boolean = false
848
849  build() {
850    Column() {
851      Tabs({ controller: this.controller1 }) {
852        TabContent() {
853          Column().width('100%').height('100%').backgroundColor(Color.Pink)
854        }.tabBar('pink')
855
856        TabContent() {
857          Column().width('100%').height('100%').backgroundColor(Color.Yellow)
858        }.tabBar('yellow')
859
860        TabContent() {
861          Column().width('100%').height('100%').backgroundColor(Color.Blue)
862        }.tabBar('blue')
863
864        TabContent() {
865          Column().width('100%').height('100%').backgroundColor(Color.Green)
866        }.tabBar('green')
867
868        TabContent() {
869          Column().width('100%').height('100%').backgroundColor(Color.Red)
870        }.tabBar('red')
871      }
872      .vertical(true)
873      .scrollable(true)
874      .barMode(BarMode.Fixed)
875      .barWidth(70)
876      .barHeight(200)
877      .animationDuration(400)
878      .onChange((index: number) => {
879        console.info(index.toString())
880      })
881      .height('200vp')
882      .margin({ bottom: '12vp' })
883      .divider(this.nullFlag ? null : {
884        strokeWidth: this.strokeWidth,
885        color: this.dividerColor,
886        startMargin: this.startMargin,
887        endMargin: this.endMargin
888      })
889
890      Button('Regular Divider').width('100%').margin({ bottom: '12vp' })
891        .onClick(() => {
892          this.nullFlag = false;
893          this.strokeWidth = 2;
894          this.dividerColor = 'red';
895          this.startMargin = 0;
896          this.endMargin = 0;
897        })
898      Button('Empty Divider').width('100%').margin({ bottom: '12vp' })
899        .onClick(() => {
900          this.nullFlag = true
901        })
902      Button('Change to Blue').width('100%').margin({ bottom: '12vp'})
903        .onClick(() => {
904          this.dividerColor = 'blue'
905        })
906      Button('Increase Width').width('100%').margin({ bottom: '12vp' })
907        .onClick(() => {
908          this.strokeWidth += 2
909        })
910      Button('Decrease Width').width('100%').margin({ bottom: '12vp'})
911        .onClick(() => {
912          if (this.strokeWidth > 2) {
913            this.strokeWidth -= 2
914          }
915        })
916      Button('Increase Top Margin').width('100%').margin({ bottom: '12vp' })
917        .onClick(() => {
918          this.startMargin += 2
919        })
920      Button('Decrease Top Margin').width('100%').margin({ bottom: '12vp' })
921        .onClick(() => {
922          if (this.startMargin > 2) {
923            this.startMargin -= 2
924          }
925        })
926      Button('Increase Bottom Margin').width('100%').margin({ bottom:'12vp'})
927        .onClick(() => {
928          this.endMargin += 2
929        })
930      Button('Decrease Bottom Margin').width('100%').margin({ bottom:'12vp' })
931        .onClick(() => {
932          if (this.endMargin > 2) {
933            this.endMargin -= 2
934          }
935        })
936    }.padding({ top: '24vp', left: '24vp', right: '24vp' })
937  }
938}
939```
940
941![tabs3](figures/tabs3.gif)
942
943### Example 3: Setting Tab Bar Fading
944
945This example uses **fadingEdge** to specify whether to fade out tabs.
946
947```ts
948// xxx.ets
949@Entry
950@Component
951struct TabsOpaque {
952  @State message: string = 'Hello World'
953  private controller: TabsController = new TabsController()
954  private controller1: TabsController = new TabsController()
955  @State selfFadingFade: boolean = true;
956
957  build() {
958    Column() {
959      Button('Set Tab to Fade').width('100%').margin({ bottom: '12vp' })
960        .onClick((event?: ClickEvent) => {
961          this.selfFadingFade = true;
962        })
963      Button('Set Tab Not to Fade').width('100%').margin({ bottom: '12vp' })
964        .onClick((event?: ClickEvent) => {
965          this.selfFadingFade = false;
966        })
967      Tabs({ barPosition: BarPosition.End, controller: this.controller }) {
968        TabContent() {
969          Column().width('100%').height('100%').backgroundColor(Color.Pink)
970        }.tabBar('pink')
971
972        TabContent() {
973          Column().width('100%').height('100%').backgroundColor(Color.Yellow)
974        }.tabBar('yellow')
975
976        TabContent() {
977          Column().width('100%').height('100%').backgroundColor(Color.Blue)
978        }.tabBar('blue')
979
980        TabContent() {
981          Column().width('100%').height('100%').backgroundColor(Color.Green)
982        }.tabBar('green')
983
984        TabContent() {
985          Column().width('100%').height('100%').backgroundColor(Color.Green)
986        }.tabBar('green')
987
988        TabContent() {
989          Column().width('100%').height('100%').backgroundColor(Color.Green)
990        }.tabBar('green')
991
992        TabContent() {
993          Column().width('100%').height('100%').backgroundColor(Color.Green)
994        }.tabBar('green')
995
996        TabContent() {
997          Column().width('100%').height('100%').backgroundColor(Color.Green)
998        }.tabBar('green')
999      }
1000      .vertical(false)
1001      .scrollable(true)
1002      .barMode(BarMode.Scrollable)
1003      .barHeight(80)
1004      .animationDuration(400)
1005      .onChange((index: number) => {
1006        console.info(index.toString())
1007      })
1008      .fadingEdge(this.selfFadingFade)
1009      .height('30%')
1010      .width('100%')
1011
1012      Tabs({ barPosition: BarPosition.Start, controller: this.controller1 }) {
1013        TabContent() {
1014          Column().width('100%').height('100%').backgroundColor(Color.Pink)
1015        }.tabBar('pink')
1016
1017        TabContent() {
1018          Column().width('100%').height('100%').backgroundColor(Color.Yellow)
1019        }.tabBar('yellow')
1020
1021        TabContent() {
1022          Column().width('100%').height('100%').backgroundColor(Color.Blue)
1023        }.tabBar('blue')
1024
1025        TabContent() {
1026          Column().width('100%').height('100%').backgroundColor(Color.Green)
1027        }.tabBar('green')
1028
1029        TabContent() {
1030          Column().width('100%').height('100%').backgroundColor(Color.Green)
1031        }.tabBar('green')
1032
1033        TabContent() {
1034          Column().width('100%').height('100%').backgroundColor(Color.Green)
1035        }.tabBar('green')
1036      }
1037      .vertical(true)
1038      .scrollable(true)
1039      .barMode(BarMode.Scrollable)
1040      .barHeight(200)
1041      .barWidth(80)
1042      .animationDuration(400)
1043      .onChange((index: number) => {
1044        console.info(index.toString())
1045      })
1046      .fadingEdge(this.selfFadingFade)
1047      .height('30%')
1048      .width('100%')
1049    }
1050    .padding({ top: '24vp', left: '24vp', right: '24vp' })
1051  }
1052}
1053```
1054
1055![tabs4](figures/tabs4.gif)
1056
1057### Example 4: Setting the Tab Bar to Be Superimposed on the TabContent Component
1058
1059This example uses **barOverlap** to specify whether the tab bar is superimposed on the **TabContent** component after having its background blurred.
1060
1061```ts
1062// xxx.ets
1063@Entry
1064@Component
1065struct barHeightTest {
1066  @State arr: number[] = [0, 1, 2, 3]
1067  @State barOverlap: boolean = true;
1068  build() {
1069    Column() {
1070      Text(`barOverlap ${this.barOverlap}`).fontSize(16)
1071      Button("Change barOverlap").width('100%').margin({ bottom: '12vp' })
1072        .onClick((event?: ClickEvent) => {
1073          if (this.barOverlap) {
1074            this.barOverlap = false;
1075          } else {
1076            this.barOverlap = true;
1077          }
1078        })
1079
1080      Tabs({ barPosition: BarPosition.End }) {
1081        TabContent() {
1082          Column() {
1083            List({ space: 10 }) {
1084              ForEach(this.arr, (item: number) => {
1085                ListItem() {
1086                  Text("item" + item).width('80%').height(200).fontSize(16).textAlign(TextAlign.Center).backgroundColor('#fff8b81e')
1087                }
1088              }, (item: string) => item)
1089            }.width('100%').height('100%')
1090            .lanes(2).alignListItem(ListItemAlign.Center)
1091          }.width('100%').height('100%')
1092          .backgroundColor(Color.Pink)
1093        }
1094        .tabBar(new BottomTabBarStyle($r('sys.media.ohos_icon_mask_svg'), "test 0"))
1095      }
1096      .scrollable(false)
1097      .height('60%')
1098      .barOverlap(this.barOverlap)
1099    }
1100    .height(500)
1101    .padding({ top: '24vp', left: '24vp', right: '24vp' })
1102  }
1103}
1104```
1105
1106![tabs5](figures/tabs5.gif)
1107
1108### Example 5: Setting the Grid-Aligned Visible Area for the TabBar
1109
1110This example uses **barGridAlign** to set the visible area of the tab bar in grid mode.
1111
1112```ts
1113// xxx.ets
1114@Entry
1115@Component
1116struct TabsExample5 {
1117  private controller: TabsController = new TabsController()
1118  @State gridMargin: number = 10
1119  @State gridGutter: number = 10
1120  @State sm: number = -2
1121  @State clickedContent: string = "";
1122
1123  build() {
1124    Column() {
1125      Row() {
1126        Button("gridMargin+10 " + this.gridMargin)
1127          .width('47%')
1128          .height(50)
1129          .margin({ top: 5 })
1130          .onClick((event?: ClickEvent) => {
1131            this.gridMargin += 10
1132          })
1133          .margin({ right: '6%', bottom: '12vp' })
1134        Button("gridMargin-10 " + this.gridMargin)
1135          .width('47%')
1136          .height(50)
1137          .margin({ top: 5 })
1138          .onClick((event?: ClickEvent) => {
1139            this.gridMargin -= 10
1140          })
1141          .margin({ bottom: '12vp' })
1142      }
1143
1144      Row() {
1145        Button("gridGutter+10 " + this.gridGutter)
1146          .width('47%')
1147          .height(50)
1148          .margin({ top: 5 })
1149          .onClick((event?: ClickEvent) => {
1150            this.gridGutter += 10
1151          })
1152          .margin({ right: '6%', bottom: '12vp' })
1153        Button("gridGutter-10 " + this.gridGutter)
1154          .width('47%')
1155          .height(50)
1156          .margin({ top: 5 })
1157          .onClick((event?: ClickEvent) => {
1158            this.gridGutter -= 10
1159          })
1160          .margin({ bottom: '12vp' })
1161      }
1162
1163      Row() {
1164        Button("sm+2 " + this.sm)
1165          .width('47%')
1166          .height(50)
1167          .margin({ top: 5 })
1168          .onClick((event?: ClickEvent) => {
1169            this.sm += 2
1170          })
1171          .margin({ right: '6%' })
1172        Button("sm-2 " + this.sm).width('47%').height(50).margin({ top: 5 })
1173          .onClick((event?: ClickEvent) => {
1174            this.sm -= 2
1175          })
1176      }
1177
1178      Text("Clicked content: " + this.clickedContent).width('100%').height(200).margin({ top: 5 })
1179
1180
1181      Tabs({ barPosition: BarPosition.End, controller: this.controller }) {
1182        TabContent() {
1183          Column().width('100%').height('100%').backgroundColor(Color.Pink)
1184        }.tabBar(BottomTabBarStyle.of($r("sys.media.ohos_app_icon"), "1"))
1185
1186        TabContent() {
1187          Column().width('100%').height('100%').backgroundColor(Color.Green)
1188        }.tabBar(BottomTabBarStyle.of($r("sys.media.ohos_app_icon"), "2"))
1189
1190        TabContent() {
1191          Column().width('100%').height('100%').backgroundColor(Color.Blue)
1192        }.tabBar(BottomTabBarStyle.of($r("sys.media.ohos_app_icon"), "3"))
1193      }
1194      .width('350vp')
1195      .animationDuration(300)
1196      .height('60%')
1197      .barGridAlign({ sm: this.sm, margin: this.gridMargin, gutter: this.gridGutter })
1198      .backgroundColor(0xf1f3f5)
1199      .onTabBarClick((index: number) => {
1200        this.clickedContent += "index " + index + " was clicked\n";
1201      })
1202    }
1203    .width('100%')
1204    .height(500)
1205    .margin({ top: 5 })
1206    .padding('10vp')
1207  }
1208}
1209```
1210
1211![tabs5](figures/tabs6.gif)
1212
1213### Example 6: Setting the Layout Style for a Scrollable TabBar
1214
1215This example implements the **ScrollableBarModeOptions** parameter of **barMode**. This parameter is effective only in **Scrollable** mode.
1216
1217```ts
1218// xxx.ets
1219@Entry
1220@Component
1221struct TabsExample6 {
1222  private controller: TabsController = new TabsController()
1223  @State scrollMargin: number = 0
1224  @State layoutStyle: LayoutStyle = LayoutStyle.ALWAYS_CENTER
1225  @State text: string = "Text"
1226
1227  build() {
1228    Column() {
1229      Row() {
1230        Button("scrollMargin+10 " + this.scrollMargin)
1231          .width('47%')
1232          .height(50)
1233          .margin({ top: 5 })
1234          .onClick((event?: ClickEvent) => {
1235            this.scrollMargin += 10
1236          })
1237          .margin({ right: '6%', bottom: '12vp' })
1238        Button("scrollMargin-10 " + this.scrollMargin)
1239          .width('47%')
1240          .height(50)
1241          .margin({ top: 5 })
1242          .onClick((event?: ClickEvent) => {
1243            this.scrollMargin -= 10
1244          })
1245          .margin({ bottom: '12vp' })
1246      }
1247
1248      Row() {
1249        Button("Add Text")
1250          .width('47%')
1251          .height(50)
1252          .margin({ top: 5 })
1253          .onClick((event?: ClickEvent) => {
1254            this.text += 'Add Text'
1255          })
1256          .margin({ right: '6%', bottom: '12vp' })
1257        Button("Reset Text")
1258          .width('47%')
1259          .height(50)
1260          .margin({ top: 5 })
1261          .onClick((event?: ClickEvent) => {
1262            this.text = "Text"
1263          })
1264          .margin({ bottom: '12vp' })
1265      }
1266
1267      Row() {
1268        Button("layoutStyle.ALWAYS_CENTER")
1269          .width('100%')
1270          .height(50)
1271          .margin({ top: 5 })
1272          .fontSize(15)
1273          .onClick((event?: ClickEvent) => {
1274            this.layoutStyle = LayoutStyle.ALWAYS_CENTER;
1275          })
1276          .margin({ bottom: '12vp' })
1277      }
1278
1279      Row() {
1280        Button("layoutStyle.ALWAYS_AVERAGE_SPLIT")
1281          .width('100%')
1282          .height(50)
1283          .margin({ top: 5 })
1284          .fontSize(15)
1285          .onClick((event?: ClickEvent) => {
1286            this.layoutStyle = LayoutStyle.ALWAYS_AVERAGE_SPLIT;
1287          })
1288          .margin({ bottom: '12vp' })
1289      }
1290
1291      Row() {
1292        Button("layoutStyle.SPACE_BETWEEN_OR_CENTER")
1293          .width('100%')
1294          .height(50)
1295          .margin({ top: 5 })
1296          .fontSize(15)
1297          .onClick((event?: ClickEvent) => {
1298            this.layoutStyle = LayoutStyle.SPACE_BETWEEN_OR_CENTER;
1299          })
1300          .margin({ bottom: '12vp' })
1301      }
1302
1303      Tabs({ barPosition: BarPosition.End, controller: this.controller }) {
1304        TabContent() {
1305          Column().width('100%').height('100%').backgroundColor(Color.Pink)
1306        }.tabBar(SubTabBarStyle.of(this.text))
1307
1308        TabContent() {
1309          Column().width('100%').height('100%').backgroundColor(Color.Green)
1310        }.tabBar(SubTabBarStyle.of(this.text))
1311
1312        TabContent() {
1313          Column().width('100%').height('100%').backgroundColor(Color.Blue)
1314        }.tabBar(SubTabBarStyle.of(this.text))
1315      }
1316      .animationDuration(300)
1317      .height('60%')
1318      .backgroundColor(0xf1f3f5)
1319      .barMode(BarMode.Scrollable, { margin: this.scrollMargin, nonScrollableLayoutStyle: this.layoutStyle })
1320    }
1321    .width('100%')
1322    .height(500)
1323    .margin({ top: 5 })
1324    .padding('24vp')
1325  }
1326}
1327```
1328
1329![tabs5](figures/tabs7.gif)
1330
1331### Example 7: Implementing a Custom Tab Switching Animation
1332
1333This example uses **customContentTransition** to implement a custom tab switching animation.
1334
1335```ts
1336// xxx.ets
1337interface itemType {
1338  text: string,
1339  backgroundColor: Color
1340}
1341
1342@Entry
1343@Component
1344struct TabsCustomAnimationExample {
1345  @State data: itemType[] = [
1346    {
1347      text: 'Red',
1348      backgroundColor: Color.Red
1349    },
1350    {
1351      text: 'Yellow',
1352      backgroundColor: Color.Yellow
1353    },
1354    {
1355      text: 'Blue',
1356      backgroundColor: Color.Blue
1357    }]
1358  @State opacityList: number[] = []
1359  @State scaleList: number[] = []
1360
1361  private durationList: number[] = []
1362  private timeoutList: number[] = []
1363  private customContentTransition: (from: number, to: number) => TabContentAnimatedTransition = (from: number, to: number) => {
1364    let tabContentAnimatedTransition = {
1365      timeout: this.timeoutList[from],
1366      transition: (proxy: TabContentTransitionProxy) => {
1367        this.scaleList[from] = 1.0
1368        this.scaleList[to] = 0.5
1369        this.opacityList[from] = 1.0
1370        this.opacityList[to] = 0.5
1371        animateTo({
1372          duration: this.durationList[from],
1373          onFinish: () => {
1374            proxy.finishTransition()
1375          }
1376        }, () => {
1377          this.scaleList[from] = 0.5
1378          this.scaleList[to] = 1.0
1379          this.opacityList[from] = 0.5
1380          this.opacityList[to] = 1.0
1381        })
1382      }
1383    } as TabContentAnimatedTransition
1384    return tabContentAnimatedTransition
1385  }
1386
1387  aboutToAppear(): void {
1388    let duration = 1000
1389    let timeout = 1000
1390    for (let i = 1; i <= this.data.length; i++) {
1391      this.opacityList.push(1.0)
1392      this.scaleList.push(1.0)
1393      this.durationList.push(duration * i)
1394      this.timeoutList.push(timeout * i)
1395    }
1396  }
1397
1398  build() {
1399    Column() {
1400      Tabs() {
1401        ForEach(this.data, (item: itemType, index: number) => {
1402          TabContent() {}
1403          .tabBar(item.text)
1404          .backgroundColor(item.backgroundColor)
1405          // Customize the opacity and scale animation.
1406          .opacity(this.opacityList[index])
1407          .scale({ x: this.scaleList[index], y: this.scaleList[index] })
1408        })
1409      }
1410      .backgroundColor(0xf1f3f5)
1411      .width('100%')
1412      .height(500)
1413      .customContentTransition(this.customContentTransition)
1414    }
1415  }
1416}
1417```
1418
1419![tabs5](figures/tabs8.gif)
1420
1421### Example 8: Intercepting Page Switching
1422
1423This example uses **onContentWillChange** to intercept and customize the gesture-based swiping actions for page switching.
1424
1425```ts
1426//xxx.ets
1427@Entry
1428@Component
1429struct TabsExample {
1430  @State currentIndex: number = 2
1431  private controller: TabsController = new TabsController()
1432  @Builder tabBuilder(title: string,targetIndex: number) {
1433    Column(){
1434      Text(title).fontColor(this.currentIndex === targetIndex ? '#1698CE' : '#6B6B6B')
1435    }.width('100%')
1436    .height(50)
1437    .justifyContent(FlexAlign.Center)
1438  }
1439  build() {
1440    Column() {
1441      Tabs({ barPosition: BarPosition.End, controller: this.controller, index: this.currentIndex }) {
1442        TabContent() {
1443          Column(){
1444            Text('Content of the Home tab')
1445          }.width('100%').height('100%').backgroundColor('#00CB87').justifyContent(FlexAlign.Center)
1446        }.tabBar(this.tabBuilder('Home',0))
1447
1448        TabContent() {
1449          Column(){
1450            Text('Content of the Discover tab')
1451          }.width('100%').height('100%').backgroundColor('#007DFF').justifyContent(FlexAlign.Center)
1452        }.tabBar(this.tabBuilder('Discover',1))
1453
1454        TabContent() {
1455          Column(){
1456            Text('Content of the Recommended tab')
1457          }.width('100%').height('100%').backgroundColor('#FFBF00').justifyContent(FlexAlign.Center)
1458        }.tabBar(this.tabBuilder('Recommended',2))
1459
1460        TabContent() {
1461          Column(){
1462            Text('Content of the Me tab')
1463          }.width('100%').height('100%').backgroundColor('#E67C92').justifyContent(FlexAlign.Center)
1464        }.tabBar(this.tabBuilder('Me',3))
1465      }
1466      .vertical(false)
1467      .barMode(BarMode.Fixed)
1468      .barWidth(360)
1469      .barHeight(60)
1470      .animationDuration(0)
1471      .onChange((index: number) => {
1472        this.currentIndex = index
1473      })
1474      .width(360)
1475      .height(600)
1476      .backgroundColor('#F1F3F5')
1477      .scrollable(true)
1478      .onContentWillChange((currentIndex, comingIndex) => {
1479        if (comingIndex == 2) {
1480          return false
1481        }
1482        return true
1483      })
1484
1485      Button('Change Index').width('50%').margin({ top: 20 })
1486        .onClick(()=>{
1487          this.currentIndex = (this.currentIndex + 1) % 4
1488        })
1489
1490      Button('changeIndex').width('50%').margin({ top: 20 })
1491        .onClick(()=>{
1492          this.currentIndex = (this.currentIndex + 1) % 4
1493          this.controller.changeIndex(this.currentIndex)
1494        })
1495    }.width('100%')
1496  }
1497}
1498```
1499
1500![tabs9](figures/tabs9.gif)
1501
1502### Example 9: Customizing the Tab Bar Switching Animation
1503
1504This example uses **onChange**, **onAnimationStart**, **onAnimationEnd**, and **onGestureSwipe** APIs to customize the tab bar switching animation.
1505
1506<!--code_no_check-->
1507
1508```ts
1509// EntryAbility.ets
1510import { Configuration, UIAbility } from '@kit.AbilityKit'
1511import { i18n } from '@kit.LocalizationKit'
1512import { CommonUtil } from '../common/CommonUtil'
1513
1514export default class EntryAbility extends UIAbility {
1515  onConfigurationUpdate(newConfig: Configuration): void {
1516    // Listen for system configuration changes.
1517    if (newConfig.language) {
1518      CommonUtil.setIsRTL(i18n.isRTL(newConfig.language))
1519    }
1520  }
1521}
1522```
1523
1524<!--code_no_check-->
1525
1526```ts
1527// CommonUtil.ets
1528import { i18n, intl } from '@kit.LocalizationKit'
1529
1530export class CommonUtil {
1531  private static isRTL: boolean = i18n.isRTL((new intl.Locale()).language)
1532
1533  public static setIsRTL(isRTL: boolean): void {
1534    CommonUtil.isRTL = isRTL
1535  }
1536
1537  public static getIsRTL(): boolean {
1538    return CommonUtil.isRTL
1539  }
1540}
1541```
1542
1543<!--code_no_check-->
1544
1545```ts
1546// xxx.ets
1547import { LengthMetrics } from '@kit.ArkUI'
1548import { CommonUtil } from '../common/CommonUtil'
1549
1550@Entry
1551@Component
1552struct TabsExample {
1553  @State colorArray: [string, string][] =
1554    [['green', '#00CB87'], ['blue', '#007DFF'], ['yellow', '#FFBF00'], ['pink', '#E67C92']]
1555  @State currentIndex: number = 0
1556  @State animationDuration: number = 300
1557  @State indicatorLeftMargin: number = 0
1558  @State indicatorWidth: number = 0
1559  private tabsWidth: number = 0
1560  private textInfos: [number, number][] = []
1561  private isStartAnimateTo: boolean = false
1562
1563  aboutToAppear():void {
1564    for (let i = 0; i < this.colorArray.length; i++) {
1565      this.textInfos.push([0, 0]);
1566    }
1567  }
1568
1569  @Builder
1570  tabBuilder(index: number, name: string) {
1571    Column() {
1572      Text(name)
1573        .fontSize(16)
1574        .fontColor(this.currentIndex === index ? '#007DFF' : '#182431')
1575        .fontWeight(this.currentIndex === index ? 500 : 400)
1576        .id(index.toString())
1577        .onAreaChange((oldValue: Area, newValue: Area) => {
1578          this.textInfos[index] = [newValue.globalPosition.x as number, newValue.width as number]
1579          if (!this.isStartAnimateTo && this.currentIndex === index && this.tabsWidth > 0) {
1580            this.setIndicatorAttr(this.textInfos[this.currentIndex][0], this.textInfos[this.currentIndex][1])
1581          }
1582        })
1583    }.width('100%')
1584  }
1585
1586  build() {
1587    Stack({ alignContent: Alignment.TopStart }) {
1588      Tabs({ barPosition: BarPosition.Start }) {
1589        ForEach(this.colorArray, (item: [string, string], index:number) => {
1590          TabContent() {
1591            Column().width('100%').height('100%').backgroundColor(item[1])
1592          }.tabBar(this.tabBuilder(index, item[0]))
1593        })
1594      }
1595      .onAreaChange((oldValue: Area, newValue: Area)=> {
1596        this.tabsWidth = newValue.width as number
1597        if (!this.isStartAnimateTo) {
1598          this.setIndicatorAttr(this.textInfos[this.currentIndex][0], this.textInfos[this.currentIndex][1])
1599        }
1600      })
1601      .barWidth('100%')
1602      .barHeight(56)
1603      .width('100%')
1604      .height(296)
1605      .backgroundColor('#F1F3F5')
1606      .animationDuration(this.animationDuration)
1607      .onChange((index: number) => {
1608        this.currentIndex = index // Listen for index changes to switch between tab pages.
1609      })
1610      .onAnimationStart((index: number, targetIndex: number, event: TabsAnimationEvent) => {
1611        // Triggered when the tab switching animation starts. The underline moves with the active tab, along with a width gradient.
1612        this.currentIndex = targetIndex
1613        this.startAnimateTo(this.animationDuration, this.textInfos[targetIndex][0], this.textInfos[targetIndex][1])
1614      })
1615      .onAnimationEnd((index: number, event: TabsAnimationEvent) => {
1616        // Triggered when the tab switching animation ends. The underline animation stops.
1617        let currentIndicatorInfo = this.getCurrentIndicatorInfo(index, event)
1618        this.startAnimateTo(0, currentIndicatorInfo.left, currentIndicatorInfo.width)
1619      })
1620      .onGestureSwipe((index: number, event: TabsAnimationEvent) => {
1621        // Triggered on a frame-by-frame basis when the tab is switched by a swipe.
1622        let currentIndicatorInfo = this.getCurrentIndicatorInfo(index, event)
1623        this.currentIndex = currentIndicatorInfo.index
1624        this.setIndicatorAttr(currentIndicatorInfo.left, currentIndicatorInfo.width)
1625      })
1626
1627      Column()
1628        .height(2)
1629        .width(this.indicatorWidth)
1630        .margin({ start: LengthMetrics.vp(this.indicatorLeftMargin), top: LengthMetrics.vp(48) })
1631        .backgroundColor('#007DFF')
1632    }.width('100%')
1633  }
1634
1635  private getCurrentIndicatorInfo(index: number, event: TabsAnimationEvent): Record<string, number> {
1636    let nextIndex = index
1637    if (index > 0 && (CommonUtil.getIsRTL() ? event.currentOffset < 0 : event.currentOffset > 0)) {
1638      nextIndex--
1639    } else if (index < this.textInfos.length - 1 &&
1640        (CommonUtil.getIsRTL() ? event.currentOffset > 0 : event.currentOffset < 0)) {
1641      nextIndex++
1642    }
1643    let indexInfo = this.textInfos[index]
1644    let nextIndexInfo = this.textInfos[nextIndex]
1645    let swipeRatio = Math.abs(event.currentOffset / this.tabsWidth)
1646    let currentIndex = swipeRatio > 0.5 ? nextIndex : index // If the swipe distance exceeds half the page width, the tab bar switches to the next page.
1647    let currentLeft = indexInfo[0] + (nextIndexInfo[0] - indexInfo[0]) * swipeRatio
1648    let currentWidth = indexInfo[1] + (nextIndexInfo[1] - indexInfo[1]) * swipeRatio
1649    return { 'index': currentIndex, 'left': currentLeft, 'width': currentWidth }
1650  }
1651
1652  private startAnimateTo(duration: number, leftMargin: number, width: number) {
1653    this.isStartAnimateTo = true
1654    animateTo({
1655      duration: duration, // Animation duration.
1656      curve: Curve.Linear, // Animation curve.
1657      iterations: 1, // Number of playback times.
1658      playMode: PlayMode.Normal, // Animation playback mode.
1659      onFinish: () => {
1660        this.isStartAnimateTo = false
1661        console.info('play end')
1662      }
1663    }, () => {
1664      this.setIndicatorAttr(leftMargin, width)
1665    })
1666  }
1667
1668  private setIndicatorAttr(leftMargin: number, width: number) {
1669    this.indicatorWidth = width
1670    if (CommonUtil.getIsRTL()) {
1671      this.indicatorLeftMargin = this.tabsWidth - leftMargin - width
1672    } else {
1673      this.indicatorLeftMargin = leftMargin
1674    }
1675  }
1676}
1677```
1678
1679![tabs10](figures/tabs10.gif)
1680
1681### Example 10: Preloading Child Nodes
1682
1683In this example, the **preloadItems** API is used to preload specified child nodes.
1684
1685```ts
1686// xxx.ets
1687import { BusinessError } from '@kit.BasicServicesKit'
1688
1689@Entry
1690@Component
1691struct TabsPreloadItems {
1692  @State currentIndex: number = 1
1693  private tabsController: TabsController = new TabsController()
1694
1695  build() {
1696    Column() {
1697      Tabs({ index: this.currentIndex, controller: this.tabsController }) {
1698        TabContent() {
1699          MyComponent({ color: '#00CB87' })
1700        }.tabBar(SubTabBarStyle.of('green'))
1701
1702        TabContent() {
1703          MyComponent({ color: '#007DFF' })
1704        }.tabBar(SubTabBarStyle.of('blue'))
1705
1706        TabContent() {
1707          MyComponent({ color: '#FFBF00' })
1708        }.tabBar(SubTabBarStyle.of('yellow'))
1709
1710        TabContent() {
1711          MyComponent({ color: '#E67C92' })
1712        }.tabBar(SubTabBarStyle.of('pink'))
1713      }
1714      .width(360)
1715      .height(296)
1716      .backgroundColor('#F1F3F5')
1717      .onChange((index: number) => {
1718        this.currentIndex = index
1719      })
1720
1721      Button('preload items: [0, 2, 3]')
1722        .margin(5)
1723        .onClick(() => {
1724          // Preload child nodes 0, 2, and 3 to improve the performance when users swipe or click to switch to these nodes.
1725          this.tabsController.preloadItems([0, 2, 3])
1726            .then(() => {
1727              console.info('preloadItems success.')
1728            })
1729            .catch((error: BusinessError) => {
1730              console.error('preloadItems failed, error code: ' + error.code + ', error message: ' + error.message)
1731            })
1732        })
1733    }
1734  }
1735}
1736
1737@Component
1738struct MyComponent {
1739  private color: string = ""
1740
1741  aboutToAppear(): void {
1742    console.info('aboutToAppear backgroundColor:' + this.color)
1743  }
1744
1745  aboutToDisappear(): void {
1746    console.info('aboutToDisappear backgroundColor:' + this.color)
1747  }
1748
1749  build() {
1750    Column()
1751      .width('100%')
1752      .height('100%')
1753      .backgroundColor(this.color)
1754  }
1755}
1756```
1757
1758### Example 11: Setting Tab Bar Translation and Opacity
1759
1760This example demonstrates how to set the translation distance and opacity of the tab bar using the **setTabBarTranslate** and **setTabBarOpacity** APIs.
1761
1762```ts
1763// xxx.ets
1764@Entry
1765@Component
1766struct TabsExample {
1767  private controller: TabsController = new TabsController()
1768
1769  build() {
1770    Column() {
1771      Button('Set TabBar Translation Distance').margin({ top: 20 })
1772        .onClick(() => {
1773          this.controller.setTabBarTranslate({ x: -20, y: -20 })
1774        })
1775
1776      Button('Set TabBar Opacity').margin({ top: 20 })
1777        .onClick(() => {
1778          this.controller.setTabBarOpacity(0.5)
1779        })
1780
1781      Tabs({ barPosition: BarPosition.End, controller: this.controller }) {
1782        TabContent() {
1783          Column().width('100%').height('100%').backgroundColor('#00CB87')
1784        }.tabBar(BottomTabBarStyle.of($r('app.media.startIcon'), 'green'))
1785
1786        TabContent() {
1787          Column().width('100%').height('100%').backgroundColor('#007DFF')
1788        }.tabBar(BottomTabBarStyle.of($r('app.media.startIcon'), 'blue'))
1789
1790        TabContent() {
1791          Column().width('100%').height('100%').backgroundColor('#FFBF00')
1792        }.tabBar(BottomTabBarStyle.of($r('app.media.startIcon'), 'yellow'))
1793
1794        TabContent() {
1795          Column().width('100%').height('100%').backgroundColor('#E67C92')
1796        }.tabBar(BottomTabBarStyle.of($r('app.media.startIcon'), 'pink'))
1797      }
1798      .width(360)
1799      .height(296)
1800      .margin({ top: 20 })
1801      .barBackgroundColor('#F1F3F5')
1802    }
1803    .width('100%')
1804  }
1805}
1806```
1807
1808![tabs11](figures/tabs11.gif)
1809
1810### Example 12: Implementing Lazy Loading and Resource Release of Pages
1811
1812This example demonstrates how to use a custom tab bar with the **Swiper** component and **LazyForEach** to implement lazy loading and resource release of pages.
1813
1814```ts
1815// xxx.ets
1816class MyDataSource implements IDataSource {
1817  private list: number[] = []
1818
1819  constructor(list: number[]) {
1820    this.list = list
1821  }
1822
1823  totalCount(): number {
1824    return this.list.length
1825  }
1826
1827  getData(index: number): number {
1828    return this.list[index]
1829  }
1830
1831  registerDataChangeListener(listener: DataChangeListener): void {
1832  }
1833
1834  unregisterDataChangeListener() {
1835  }
1836}
1837
1838@Entry
1839@Component
1840struct TabsSwiperExample {
1841  @State fontColor: string = '#182431'
1842  @State selectedFontColor: string = '#007DFF'
1843  @State currentIndex: number = 0
1844  private list: number[] = []
1845  private tabsController: TabsController = new TabsController()
1846  private swiperController: SwiperController = new SwiperController()
1847  private swiperData: MyDataSource = new MyDataSource([])
1848
1849  aboutToAppear(): void {
1850    for (let i = 0; i <= 9; i++) {
1851      this.list.push(i);
1852    }
1853    this.swiperData = new MyDataSource(this.list)
1854  }
1855
1856  @Builder tabBuilder(index: number, name: string) {
1857    Column() {
1858      Text(name)
1859        .fontColor(this.currentIndex === index ? this.selectedFontColor : this.fontColor)
1860        .fontSize(16)
1861        .fontWeight(this.currentIndex === index ? 500 : 400)
1862        .lineHeight(22)
1863        .margin({ top: 17, bottom: 7 })
1864      Divider()
1865        .strokeWidth(2)
1866        .color('#007DFF')
1867        .opacity(this.currentIndex === index ? 1 : 0)
1868    }.width('20%')
1869  }
1870
1871  build() {
1872    Column() {
1873      Tabs({ barPosition: BarPosition.Start, controller: this.tabsController }) {
1874        ForEach(this.list, (item: number) => {
1875          TabContent().tabBar(this.tabBuilder(item, 'Tab ' + this.list[item]))
1876        })
1877      }
1878      .onTabBarClick((index: number) => {
1879        this.currentIndex = index
1880        this.swiperController.changeIndex(index, true)
1881      })
1882      .barMode(BarMode.Scrollable)
1883      .backgroundColor('#F1F3F5')
1884      .height(56)
1885      .width('100%')
1886
1887      Swiper(this.swiperController) {
1888        LazyForEach(this.swiperData, (item: string) => {
1889          Text(item.toString())
1890            .onAppear(()=>{
1891              console.info('onAppear ' + item.toString())
1892            })
1893            .onDisAppear(()=>{
1894              console.info('onDisAppear ' + item.toString())
1895            })
1896            .width('100%')
1897            .height('100%')
1898            .backgroundColor(0xAFEEEE)
1899            .textAlign(TextAlign.Center)
1900            .fontSize(30)
1901        }, (item: string) => item)
1902      }
1903      .loop(false)
1904      .onChange((index: number) => {
1905        this.currentIndex = index
1906      })
1907      .onAnimationStart((index: number, targetIndex: number, extraInfo: SwiperAnimationEvent) => {
1908        this.currentIndex = targetIndex
1909        this.tabsController.changeIndex(targetIndex)
1910      })
1911    }
1912  }
1913}
1914```
1915
1916![tabs12](figures/tabs12.gif)
1917