1# WaterFlow 2 3 4The **WaterFlow** component is a water flow container that consists of cells formed by rows and columns and arranges items of different sizes from top to bottom according to the preset rules. 5 6 7> **NOTE** 8> 9> This component is supported since API version 9. Updates will be marked with a superscript to indicate their earliest API version. 10 11 12## Child Components 13 14 15Only the [FlowItem](ts-container-flowitem.md) child component is allowed, with support for [if/else](../../../quick-start/arkts-rendering-control-ifelse.md), [ForEach](../../../quick-start/arkts-rendering-control-foreach.md), [LazyForEach](../../../quick-start/arkts-rendering-control-lazyforeach.md), and [Repeat](../../../quick-start/arkts-new-rendering-control-repeat.md) rendering control. 16 17> **NOTE** 18> 19> When its **visibility** attribute is set to **None**, a **FlowItem** is not displayed in the container, but its **columnsGap**, **rowsGap**, and **margin** settings are still effective. 20 21## APIs 22 23WaterFlow(options?: WaterFlowOptions) 24 25**Atomic service API**: This API can be used in atomic services since API version 11. 26 27**System capability**: SystemCapability.ArkUI.ArkUI.Full 28 29**Parameters** 30 31| Name| Type| Mandatory| Description| 32| -------- | -------- | -------- | -------- | 33| options | [WaterFlowOptions](#waterflowoptions)| No| Parameters of the **WaterFlow** component.| 34 35 36## WaterFlowOptions 37 38**System capability**: SystemCapability.ArkUI.ArkUI.Full 39 40| Name | Type | Mandatory| Description | 41| ---------- | ----------------------------------------------- | ------ | -------------------------------------------- | 42| footer | [CustomBuilder](ts-types.md#custombuilder8) | No | Footer of the **WaterFlow** component.<br>**Atomic service API**: This API can be used in atomic services since API version 11.| 43| scroller | [Scroller](ts-container-scroll.md#scroller) | No | Controller of the scrollable component, bound to the scrollable component.<br>**NOTE**<br>The same scroller cannot be bound to other scrollable components, such as [List](ts-container-list.md), [Grid](ts-container-grid.md), or [Scroll](ts-container-scroll.md).<br>**Atomic service API**: This API can be used in atomic services since API version 11.| 44| sections<sup>12+</sup> | [WaterFlowSections](#waterflowsections12) | No | Water flow item sections. Different sections can have different numbers of columns.<br>**NOTE**<br>1. When **sections** is used, the **columnsTemplate** and **rowsTemplate** attributes are ignored.<br>2. When **sections** is used, the footer cannot be set separately. The last section can function as the footer.<br>**Atomic service API**: This API can be used in atomic services since API version 12. | 45| layoutMode<sup>12+</sup> |[WaterFlowLayoutMode](#waterflowlayoutmode12)| No| Layout mode of the **WaterFlow** component.<br>**NOTE**<br>Default value: [ALWAYS_TOP_DOWN](#waterflowlayoutmode12)<br>**Atomic service API**: This API can be used in atomic services since API version 12. 46 47 48## WaterFlowSections<sup>12+</sup> 49 50Describes the water flow item sections. 51 52### constructor 53 54constructor() 55 56A constructor used to create a **WaterFlowSections** object. 57 58**Atomic service API**: This API can be used in atomic services since API version 12. 59 60**System capability**: SystemCapability.ArkUI.ArkUI.Full 61 62### splice<sup>12+</sup> 63 64splice(start: number, deleteCount?: number, sections?: Array\<SectionOptions\>): boolean 65 66Changes sections by removing or replacing an existing section and/or adding a section. 67 68**Atomic service API**: This API can be used in atomic services since API version 12. 69 70**System capability**: SystemCapability.ArkUI.ArkUI.Full 71 72**Parameters** 73 74| Name | Type | Mandatory | Description | 75| ---- | ----------------------------- | ---- | -------------------- | 76| start | number | Yes | Zero-based index at which the changing starts. The value is converted to an integer.<br>**NOTE**<br>1. A negative index counts back from the end of the section list. If -**WaterFlowSections.length()** <= **start** < **0**, **start** + **array.length** is used.<br>2. If **start** < -**WaterFlowSections.length()**, **0** is used.<br>3. If **start** >= **WaterFlowSections.length()**, a new section is added at the end.| 77| deleteCount | number | No | Number of sections to be deleted from the position specified by **start**.<br>**NOTE**<br>1. If **deleteCount** is omitted, or if its value is greater than or equal to the number of sections from the position specified by **start** to the end of the **WaterFlowSections**, then all sections from the position specified by **start** to the end of the **WaterFlowSections** will be deleted.<br>2. If **deleteCount** is **0** or a negative number, no sections are deleted.| 78| sections | Array<[SectionOptions](#sectionoptions12)> | No | Sections to add to the section list, beginning from the position specified by **start**. If no section is specified, **splice()** will only delete sections from the **WaterFlow** component.| 79 80**Return value** 81 82| Type | Description | 83| ------------------------------------------------------------ | ------------------------------------------------------------ | 84| boolean | Whether the changing is successful. If the value of **itemsCount** in any section to add is not a positive integer, **false** is returned.| 85 86 87### push<sup>12+</sup> 88 89push(section: SectionOptions): boolean 90 91Adds the specified sections to the end of the **WaterFlow** component. 92 93**Atomic service API**: This API can be used in atomic services since API version 12. 94 95**System capability**: SystemCapability.ArkUI.ArkUI.Full 96 97**Parameters** 98 99| Name | Type | Mandatory | Description | 100| ---- | ----------------------------- | ---- | -------------------- | 101| section | [SectionOptions](#sectionoptions12) | Yes | Sections to add to the end of the **WaterFlow** component.| 102 103**Return value** 104 105| Type | Description | 106| ------------------------------------------------------------ | ------------------------------------------------------------ | 107| boolean | Whether the adding is successful. If the value of **itemsCount** in any section to add is not a positive integer, **false** is returned.| 108 109### update<sup>12+</sup> 110 111update(sectionIndex: number, section: SectionOptions): boolean 112 113Updates the configuration of a specified water flow item section. 114 115**Atomic service API**: This API can be used in atomic services since API version 12. 116 117**System capability**: SystemCapability.ArkUI.ArkUI.Full 118 119**Parameters** 120 121| Name | Type | Mandatory | Description | 122| ---- | ----------------------------- | ---- | -------------------- | 123| sectionIndex | number | Yes | Zero-based index of the water flow item section to update. The value is converted to an integer.<br>**NOTE**<br>1. A negative index counts back from the end of the section list. If -**WaterFlowSections.length()** <= **sectionIndex** < **0**, **sectionIndex** + **array.length** is used.<br>2. If **sectionIndex** < -**WaterFlowSections.length()**, **0** is used.<br>3. If **sectionIndex** >= **WaterFlowSections.length()**, a new section is added at the end.| 124| section | [SectionOptions](#sectionoptions12) | Yes | New section configuration.| 125 126**Return value** 127 128| Type | Description | 129| ------------------------------------------------------------ | ------------------------------------------------------------ | 130| boolean | Whether the update is successful. If the value of **itemsCount** in any section to add is not a positive integer, **false** is returned.| 131 132### values<sup>12+</sup> 133 134values(): Array\<SectionOptions\> 135 136Obtains the configuration of all sections in the **WaterFlow** component. 137 138**Atomic service API**: This API can be used in atomic services since API version 12. 139 140**System capability**: SystemCapability.ArkUI.ArkUI.Full 141 142**Return value** 143 144| Type | Description | 145| ------------------------------------------------------------ | ------------------------------------------------------------ | 146| Array<[SectionOptions](#sectionoptions12)> | Configuration of all sections in the **WaterFlow** component.| 147 148### length<sup>12+</sup> 149 150length(): number 151 152Obtains the number of sections in the **WaterFlow** component. 153 154**Atomic service API**: This API can be used in atomic services since API version 12. 155 156**System capability**: SystemCapability.ArkUI.ArkUI.Full 157 158**Return value** 159 160| Type | Description | 161| ------------------------------------------------------------ | ------------------------------------------------------------ | 162| number | Number of sections in the **WaterFlow** component.| 163 164## SectionOptions<sup>12+</sup> 165 166Describes the configuration of the water flow item section. 167 168**Atomic service API**: This API can be used in atomic services since API version 12. 169 170**System capability**: SystemCapability.ArkUI.ArkUI.Full 171 172**Parameters** 173 174| Name| Type| Mandatory| Description| 175|------|-----|-----|-----| 176| itemsCount | number | Yes| Number of water flow items in the section. The value must be a positive integer.| 177| crossCount | number | No| Number of columns (in vertical layout) or rows (in horizontal layout).<br>Default value: **1**| 178| columnsGap | [Dimension](ts-types.md#dimension10) | No| Gap between columns. If this parameter is not set, the value of **columnsGap** for the water flow is used. If this parameter is set to an invalid value, 0 vp is used.| 179| rowsGap | [Dimension](ts-types.md#dimension10) | No| Gap between rows. If this parameter is not set, the value of **rowsGap** for the water flow is used. If this parameter is set to an invalid value, 0 vp is used.| 180| margin | [Margin](ts-types.md#margin) \| [Dimension](ts-types.md#dimension10) | No| Padding of the section. A value of the Length type specifies the margin for all the four sides.<br>Default value: **0**<br>Unit: vp<br>When **margin** is set to a percentage, the width of the **WaterFlow** component is used as the base value for the top, bottom, left, and right margins.| 181| onGetItemMainSizeByIndex | [GetItemMainSizeByIndex](#getitemmainsizebyindex12) | No| Callback used to obtain the main axis size, in vp, of the water flow item at a specified index during the layout process of the **WaterFlow** component. For a vertical **WaterFlow** component, this size refers to the height, and for a horizontal **WaterFlow** component, it refers to the width.<br>**NOTE**<br>1. When both **onGetItemMainSizeByIndex** and the width or height attribute of the water flow item are used, the main axis size is determined by the return value of **onGetItemMainSizeByIndex**, which will override the main axis length of water flow item.<br>2. Using **onGetItemMainSizeByIndex** can improve the efficiency of jumping to a specific position or index in the **WaterFlow** component. Avoid mixing the use of **onGetItemMainSizeByIndex** with sections that do not have it set, as this can cause layout exceptions.<br>3. If **onGetItemMainSizeByIndex** returns a negative number, the height of the water flow item is 0.| 182 183 184## GetItemMainSizeByIndex<sup>12+</sup> 185 186type GetItemMainSizeByIndex = (index: number) => number 187 188Obtains the main axis size of a specified water flow item based on its index. 189 190**Atomic service API**: This API can be used in atomic services since API version 12. 191 192**System capability**: SystemCapability.ArkUI.ArkUI.Full 193 194**Parameters** 195 196| Name | Type | Mandatory | Description | 197| ---- | ----------------------------- | ---- | -------------------- | 198| index | number | Yes | Index of the target water flow item.| 199 200**Return value** 201 202| Type | Description | 203| ------------------------------------------------------------ | ------------------------------------------------------------ | 204| number | Main axis size, in vp, of the water flow item at the specified index, which is the height for a vertical **WaterFlow** component and the width for a horizontal **WaterFlow** component.| 205 206## WaterFlowLayoutMode<sup>12+</sup> 207 208**Atomic service API**: This API can be used in atomic services since API version 12. 209 210**System capability**: SystemCapability.ArkUI.ArkUI.Full 211 212| Name| Value| Description| 213| ------ | ------ | -------------------- | 214| ALWAYS_TOP_DOWN | 0 | Default layout mode from top to bottom. The water flow item within the viewport depends on the layout information of all water flow items above it. As such, in cases of redirection or changing the number of columns, it is necessary to calculate the layout information for all water flow items above.| 215| SLIDING_WINDOW | 1 | Sliding window layout mode. It only takes into account the layout information within the viewport and has no dependency on water flow items above the viewport. As such, in cases of redirection backward or switching the number of columns, only the layout of water flow items within the viewport needs to be handled. This mode is recommended for applications that frequently switch between different numbers of columns.<br>**NOTE**<br>1. During a non-animated redirection to a distant location, the layout of water flow items is based on the target position, either before or after it. If you then swipe back to the position prior to the redirection, the layout of the content may not be consistent with its previous state. This can lead to misalignment of the top nodes when you swipe back to the top after the redirection. To counteract this issue, in this layout mode, an automatic adjustment of the layout is made after reaching the top of the viewport to ensure that the top is aligned. In cases with multiple sections, the section at the top of the viewport will be adjusted once the swipe action is completed.<br> 2. The mode does not support the use of a scrollbar; even if a scrollbar is set, it will not be displayed.<br> 3. This mode does not support the [scrollTo](ts-container-scroll.md#scrollto) API of [scroller](#waterflowoptions).<br> 4. If a jump action (for example, by calling a non-animated [scrollToIndex](ts-container-scroll.md#scrolltoindex) or [scrollEdge](ts-container-scroll.md#scrolledge)) and an input offset (such as from a swipe gesture or a scrolling animation) are both initiated within the same frame, both will be executed.| 216 217 218## Attributes 219 220In addition to [universal attributes](ts-universal-attributes-size.md) and [scrollable component common attributes](ts-container-scrollable-common.md#attributes), the following attributes are also supported. 221 222### columnsTemplate 223 224columnsTemplate(value: string) 225 226Sets the number of columns in the layout. If this attribute is not set, one column is used by default. 227 228For example, **'1fr 1fr 2fr'** indicates three columns, with the first column taking up 1/4 of the parent component's full width, the second column 1/4, and the third column 2/4. 229 230You can use **columnsTemplate('repeat(auto-fill,track-size)')** to automatically calculate the number of columns based on the specified column width **track-size**. **repeat** and **auto-fill** are keywords. The units for **track-size** can be px, vp (default), %, or a valid number. For details, see Example 2. 231 232**Atomic service API**: This API can be used in atomic services since API version 11. 233 234**System capability**: SystemCapability.ArkUI.ArkUI.Full 235 236**Parameters** 237 238| Name| Type | Mandatory| Description | 239| ------ | ------ | ---- | ---------------------------------------------- | 240| value | string | Yes | Number of columns in the layout.<br>Default value: **'1fr'**| 241 242### rowsTemplate 243 244rowsTemplate(value: string) 245 246Sets the number of rows in the layout. If this attribute is not set, one row is used by default. 247 248For example, **'1fr 1fr 2fr'** indicates three rows, with the first row taking up 1/4 of the parent component's full height, the second row 1/4, and the third row 2/4. 249 250You can use **rowsTemplate('repeat(auto-fill,track-size)')** to automatically calculate the number of rows based on the specified row height **track-size**. **repeat** and **auto-fill** are keywords. The units for **track-size** can be px, vp (default), %, or a valid number. 251 252**Atomic service API**: This API can be used in atomic services since API version 11. 253 254**System capability**: SystemCapability.ArkUI.ArkUI.Full 255 256**Parameters** 257 258| Name| Type | Mandatory| Description | 259| ------ | ------ | ---- | ---------------------------------------------- | 260| value | string | Yes | Number of rows in the layout.<br>Default value: **'1fr'**| 261 262### itemConstraintSize 263 264itemConstraintSize(value: ConstraintSizeOptions) 265 266Sets the size constraints of the child components during layout. 267 268**Atomic service API**: This API can be used in atomic services since API version 11. 269 270**System capability**: SystemCapability.ArkUI.ArkUI.Full 271 272**Parameters** 273 274| Name| Type | Mandatory| Description | 275| ------ | ---------------------------------------------------------- | ---- | ---------- | 276| value | [ConstraintSizeOptions](ts-types.md#constraintsizeoptions) | Yes | Size constraints of the child components during layout. If the value specified is less than 0, this parameter does not take effect.<br>**NOTE**<br>1. If both **itemConstraintSize** and the [constraintSize](ts-universal-attributes-size.md#constraintsize) attribute of the **FlowItem** are set, the **minWidth** (or **minHeight**) will be the larger of the two values, and the **maxWidth** (or **maxHeight**) will be the smaller of the two values. The resulting values will then be used as the **constraintSize** for the **FlowItem**. 2. When only **itemConstraintSize** is set, it effectively applies a uniform size constraint to all child components in the **WaterFlow**. 3. The **itemConstraintSize** attribute, once converted to the **constraintSize** attribute of the **FlowItem** through the two methods mentioned above, follows the same rules for taking effect as the universal attribute [constraintSize](./ts-universal-attributes-size.md#constraintsize).| 277 278### columnsGap 279 280columnsGap(value: Length) 281 282Sets the gap between columns. 283 284**Atomic service API**: This API can be used in atomic services since API version 11. 285 286**System capability**: SystemCapability.ArkUI.ArkUI.Full 287 288**Parameters** 289 290| Name| Type | Mandatory| Description | 291| ------ | ---------------------------- | ---- | ----------------------------- | 292| value | [Length](ts-types.md#length) | Yes | Gap between columns.<br>Default value: **0**| 293 294### rowsGap 295 296rowsGap(value: Length) 297 298Sets the gap between rows. 299 300**Atomic service API**: This API can be used in atomic services since API version 11. 301 302**System capability**: SystemCapability.ArkUI.ArkUI.Full 303 304**Parameters** 305 306| Name| Type | Mandatory| Description | 307| ------ | ---------------------------- | ---- | ----------------------------- | 308| value | [Length](ts-types.md#length) | Yes | Gap between rows.<br>Default value: **0**| 309 310### layoutDirection 311 312layoutDirection(value: FlexDirection) 313 314Sets the main axis direction of the layout. 315 316**Atomic service API**: This API can be used in atomic services since API version 11. 317 318**System capability**: SystemCapability.ArkUI.ArkUI.Full 319 320**Parameters** 321 322| Name| Type | Mandatory| Description | 323| ------ | --------------------------------------------------- | ---- | ------------------------------------------------- | 324| value | [FlexDirection](ts-appendix-enums.md#flexdirection) | Yes | Main axis direction of the layout.<br>Default value: **FlexDirection.Column**| 325 326The priority of **layoutDirection** is higher than that of **rowsTemplate** and **columnsTemplate**. Depending on the **layoutDirection** settings, there are three layout modes: 327 328- **layoutDirection** is set to **FlexDirection.Column** or **FlexDirection.ColumnReverse** 329 330 In this case, **columnsTemplate** is valid. If it is not set, the default value is used. For example, if **columnsTemplate** is set to **"1fr 1fr"** and **rowsTemplate** **"1fr 1fr 1fr"**, child components are arranged in vertical layout, with the cross axis equally divided into two columns. 331 332- **layoutDirection** set to **FlexDirection.Row** or **FlexDirection.RowReverse** 333 334 In this case, **rowsTemplate** is valid. If it is not set, the default value is used. For example, if **columnsTemplate** is set to **"1fr 1fr"** and **rowsTemplate** **"1fr 1fr 1fr"**, child components are arranged in horizontal layout, with the cross axis equally divided into three columns. 335 336- **layoutDirection** is not set 337 338 In this case, the default value of **layoutDirection** is used, which is **FlexDirection.Column**, and **columnsTemplate** is valid. For example, if **columnsTemplate** is set to **"1fr 1fr"** and **rowsTemplate** **"1fr 1fr 1fr"**, child components are arranged in vertical layout, with the cross axis equally divided into two columns. 339 340### enableScrollInteraction<sup>10+</sup> 341 342enableScrollInteraction(value: boolean) 343 344Sets whether to support scroll gestures. When this attribute is set to **false**, scrolling by finger or mouse is not supported, but the scrolling controller API is not affected. 345 346**Atomic service API**: This API can be used in atomic services since API version 11. 347 348**System capability**: SystemCapability.ArkUI.ArkUI.Full 349 350**Parameters** 351 352| Name| Type | Mandatory| Description | 353| ------ | ------- | ---- | ----------------------------------- | 354| value | boolean | Yes | Whether to support scroll gestures.<br>Default value: **true**| 355 356### nestedScroll<sup>10+</sup> 357 358nestedScroll(value: NestedScrollOptions) 359 360Sets the nested scrolling mode in the forward and backward directions to implement scrolling linkage with the parent component. 361 362**Atomic service API**: This API can be used in atomic services since API version 11. 363 364**System capability**: SystemCapability.ArkUI.ArkUI.Full 365 366**Parameters** 367 368| Name| Type | Mandatory| Description | 369| ------ | ------------------------------------------------------------ | ---- | -------------- | 370| value | [NestedScrollOptions](ts-container-scrollable-common.md#nestedscrolloptions10) | Yes | Nested scrolling options.| 371 372### friction<sup>10+</sup> 373 374friction(value: number | Resource) 375 376Sets the friction coefficient. It applies only to gestures in the scrolling area, and it affects only indirectly the scroll chaining during the inertial scrolling process. If this attribute is set to a value less than or equal to 0, the default value is used. 377 378**Atomic service API**: This API can be used in atomic services since API version 11. 379 380**System capability**: SystemCapability.ArkUI.ArkUI.Full 381 382**Parameters** 383 384| Name| Type | Mandatory| Description | 385| ------ | ---------------------------------------------------- | ---- | --------------------------------------------------------- | 386| value | number \| [Resource](ts-types.md#resource) | Yes | Friction coefficient.<br>Default value: **0.9** for wearable devices and **0.6** for non-wearable devices.<br>Since API version 11, the default value for non-wearable devices is **0.7**.<br>Since API version 12, the default value for non-wearable devices is **0.75**.| 387 388### cachedCount<sup>11+</sup> 389 390cachedCount(value: number) 391 392Number of items to be cached. This attribute is effective only in [LazyForEach](../../../quick-start/arkts-rendering-control-lazyforeach.md). Items that exceed the display and cache range are released. A value less than 0 evaluates to the default value. 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**Parameters** 399 400| Name| Type | Mandatory| Description | 401| ------ | ------ | ---- | ---------------------------------------- | 402| value | number | Yes | Number of water flow items to be preloaded (cached).<br> Default value: **1**| 403 404## Events 405 406In addition to [universal events](ts-universal-events-click.md) and [scrollable component common events](ts-container-scrollable-common.md#events), the following events are also supported. 407 408### onReachStart 409 410onReachStart(event: () => void) 411 412Triggered when the component reaches the start. 413 414**Atomic service API**: This API can be used in atomic services since API version 11. 415 416**System capability**: SystemCapability.ArkUI.ArkUI.Full 417 418### onReachEnd 419 420onReachEnd(event: () => void) 421 422Triggered when the component reaches the end position. 423 424**Atomic service API**: This API can be used in atomic services since API version 11. 425 426**System capability**: SystemCapability.ArkUI.ArkUI.Full 427 428### onScrollFrameBegin<sup>10+</sup> 429 430onScrollFrameBegin(event: (offset: number, state: ScrollState) => { offsetRemain: number; }) 431 432Triggered when the component starts to scroll. The input parameters indicate the amount by which the component will scroll. The event handler then works out the amount by which the component needs to scroll based on the real-world situation and returns the result. 433 434This event is triggered when the user starts dragging the component or the component starts inertial scrolling. It is not triggered when the component rebounds, the scrolling controller is used, or the scrollbar is dragged. 435 436**Atomic service API**: This API can be used in atomic services since API version 11. 437 438**System capability**: SystemCapability.ArkUI.ArkUI.Full 439 440**Parameters** 441 442| Name| Type | Mandatory| Description | 443| ------ | ------------------------------------------------------- | ---- | -------------------------- | 444| offset | number | Yes | Amount to scroll by, in vp.| 445| state | [ScrollState](ts-container-list.md#scrollstate) | Yes | Current scroll state. | 446 447**Return value** 448 449| Type | Description | 450| ------------------------ | -------------------- | 451| { offsetRemain: number } | Actual amount by which the component scrolls, in vp.| 452 453### onScrollIndex<sup>11+</sup> 454 455onScrollIndex(event: (first: number, last: number) => void) 456 457Triggered when the first or last item displayed in the component changes. It is triggered once when the component is initialized. 458 459This event is triggered when either of the preceding indexes changes. 460 461**Atomic service API**: This API can be used in atomic services since API version 11. 462 463**System capability**: SystemCapability.ArkUI.ArkUI.Full 464 465**Parameters** 466 467| Name| Type | Mandatory| Description | 468| ------ | ------ | ---- | ------------------------------------- | 469| first | number | Yes | Index of the first item of the component.| 470| last | number | Yes | Index of the last item of the component. | 471 472## Example 473 474### Example 1 475This example shows the basic usage of **WaterFlow**: 476```ts 477// WaterFlowDataSource.ets 478 479// Object that implements the IDataSource API, which is used by the WaterFlow component to load data. 480export class WaterFlowDataSource implements IDataSource { 481 private dataArray: number[] = [] 482 private listeners: DataChangeListener[] = [] 483 484 constructor() { 485 for (let i = 0; i < 100; i++) { 486 this.dataArray.push(i) 487 } 488 } 489 490 // Obtain the data corresponding to the specified index. 491 public getData(index: number): number { 492 return this.dataArray[index] 493 } 494 495 // Notify the controller of data reloading. 496 notifyDataReload(): void { 497 this.listeners.forEach(listener => { 498 listener.onDataReloaded() 499 }) 500 } 501 502 // Notify the controller of data addition. 503 notifyDataAdd(index: number): void { 504 this.listeners.forEach(listener => { 505 listener.onDataAdd(index) 506 }) 507 } 508 509 // Notify the controller of data changes. 510 notifyDataChange(index: number): void { 511 this.listeners.forEach(listener => { 512 listener.onDataChange(index) 513 }) 514 } 515 516 // Notify the controller of data deletion. 517 notifyDataDelete(index: number): void { 518 this.listeners.forEach(listener => { 519 listener.onDataDelete(index) 520 }) 521 } 522 523 // Notify the controller of the data location change. 524 notifyDataMove(from: number, to: number): void { 525 this.listeners.forEach(listener => { 526 listener.onDataMove(from, to) 527 }) 528 } 529 530 // Notify the controller of batch data modification. 531 notifyDatasetChange(operations: DataOperation[]): void { 532 this.listeners.forEach(listener => { 533 listener.onDatasetChange(operations); 534 }) 535 } 536 537 // Obtain the total number of data records. 538 public totalCount(): number { 539 return this.dataArray.length 540 } 541 542 // Register the data change listener. 543 registerDataChangeListener(listener: DataChangeListener): void { 544 if (this.listeners.indexOf(listener) < 0) { 545 this.listeners.push(listener) 546 } 547 } 548 549 // Unregister the data change listener. 550 unregisterDataChangeListener(listener: DataChangeListener): void { 551 const pos = this.listeners.indexOf(listener) 552 if (pos >= 0) { 553 this.listeners.splice(pos, 1) 554 } 555 } 556 557 // Add data. 558 public add1stItem(): void { 559 this.dataArray.splice(0, 0, this.dataArray.length) 560 this.notifyDataAdd(0) 561 } 562 563 // Add an item to the end of the data. 564 public addLastItem(): void { 565 this.dataArray.splice(this.dataArray.length, 0, this.dataArray.length) 566 this.notifyDataAdd(this.dataArray.length - 1) 567 } 568 569 // Add an item to the position corresponding to the specified index. 570 public addItem(index: number): void { 571 this.dataArray.splice(index, 0, this.dataArray.length) 572 this.notifyDataAdd(index) 573 } 574 575 // Delete the first item. 576 public delete1stItem(): void { 577 this.dataArray.splice(0, 1) 578 this.notifyDataDelete(0) 579 } 580 581 // Delete the second item. 582 public delete2ndItem(): void { 583 this.dataArray.splice(1, 1) 584 this.notifyDataDelete(1) 585 } 586 587 // Delete the last item. 588 public deleteLastItem(): void { 589 this.dataArray.splice(-1, 1) 590 this.notifyDataDelete(this.dataArray.length) 591 } 592 593 // Delete an item at the specified index position. 594 public deleteItem(index: number): void { 595 this.dataArray.splice(index, 1) 596 this.notifyDataDelete(index) 597 } 598 599 // Reload data. 600 public reload(): void { 601 this.dataArray.splice(1, 1) 602 this.dataArray.splice(3, 2) 603 this.notifyDataReload() 604 } 605} 606``` 607 608```ts 609// Index.ets 610import { WaterFlowDataSource } from './WaterFlowDataSource' 611 612@Entry 613@Component 614struct WaterFlowDemo { 615 @State minSize: number = 80 616 @State maxSize: number = 180 617 @State fontSize: number = 24 618 @State colors: number[] = [0xFFC0CB, 0xDA70D6, 0x6B8E23, 0x6A5ACD, 0x00FFFF, 0x00FF7F] 619 scroller: Scroller = new Scroller() 620 dataSource: WaterFlowDataSource = new WaterFlowDataSource() 621 private itemWidthArray: number[] = [] 622 private itemHeightArray: number[] = [] 623 624 // Calculate the width and height of a water flow item. 625 getSize() { 626 let ret = Math.floor(Math.random() * this.maxSize) 627 return (ret > this.minSize ? ret : this.minSize) 628 } 629 630 // Set the width and height array of the water flow item. 631 setItemSizeArray() { 632 for (let i = 0; i < 100; i++) { 633 this.itemWidthArray.push(this.getSize()) 634 this.itemHeightArray.push(this.getSize()) 635 } 636 } 637 638 aboutToAppear() { 639 this.setItemSizeArray() 640 } 641 642 @Builder 643 itemFoot() { 644 Column() { 645 Text(`Footer`) 646 .fontSize(10) 647 .backgroundColor(Color.Red) 648 .width(50) 649 .height(50) 650 .align(Alignment.Center) 651 .margin({ top: 2 }) 652 } 653 } 654 655 build() { 656 Column({ space: 2 }) { 657 WaterFlow() { 658 LazyForEach(this.dataSource, (item: number) => { 659 FlowItem() { 660 Column() { 661 Text("N" + item).fontSize(12).height('16') 662 // The image is displayed only when there is a corresponding JPG file. 663 Image('res/waterFlowTest(' + item % 5 + ').jpg') 664 .objectFit(ImageFit.Fill) 665 .width('100%') 666 .layoutWeight(1) 667 } 668 } 669 .onAppear(() => { 670 // Add data in advance when scrolling is about to end. 671 if (item + 20 == this.dataSource.totalCount()) { 672 for (let i = 0; i < 100; i++) { 673 this.dataSource.addLastItem() 674 } 675 } 676 }) 677 .width('100%') 678 .height(this.itemHeightArray[item % 100]) 679 .backgroundColor(this.colors[item % 5]) 680 }, (item: string) => item) 681 } 682 .columnsTemplate("1fr 1fr") 683 .columnsGap(10) 684 .rowsGap(5) 685 .backgroundColor(0xFAEEE0) 686 .width('100%') 687 .height('100%') 688 .onReachStart(() => { 689 console.info('waterFlow reach start') 690 }) 691 .onScrollStart(() => { 692 console.info('waterFlow scroll start') 693 }) 694 .onScrollStop(() => { 695 console.info('waterFlow scroll stop') 696 }) 697 .onScrollFrameBegin((offset: number, state: ScrollState) => { 698 console.info('waterFlow scrollFrameBegin offset: ' + offset + ' state: ' + state.toString()) 699 return { offsetRemain: offset } 700 }) 701 } 702 } 703} 704``` 705 706 707 708### Example 2 709This example shows the usage of **auto-fill**: 710```ts 711//index.ets 712import { WaterFlowDataSource } from './WaterFlowDataSource' 713 714@Entry 715@Component 716struct WaterFlowDemo { 717 @State minSize: number = 80 718 @State maxSize: number = 180 719 @State colors: number[] = [0xFFC0CB, 0xDA70D6, 0x6B8E23, 0x6A5ACD, 0x00FFFF, 0x00FF7F] 720 dataSource: WaterFlowDataSource = new WaterFlowDataSource() 721 private itemWidthArray: number[] = [] 722 private itemHeightArray: number[] = [] 723 724 // Calculate the width and height of a water flow item. 725 getSize() { 726 let ret = Math.floor(Math.random() * this.maxSize) 727 return (ret > this.minSize ? ret : this.minSize) 728 } 729 730 // Set the width and height array of the water flow item. 731 setItemSizeArray() { 732 for (let i = 0; i < 100; i++) { 733 this.itemWidthArray.push(this.getSize()) 734 this.itemHeightArray.push(this.getSize()) 735 } 736 } 737 738 aboutToAppear() { 739 this.setItemSizeArray() 740 } 741 742 build() { 743 Column({ space: 2 }) { 744 WaterFlow() { 745 LazyForEach(this.dataSource, (item: number) => { 746 FlowItem() { 747 Column() { 748 Text("N" + item).fontSize(12).height('16') 749 Image('res/waterFlowTest(' + item % 5 + ').jpg') 750 } 751 } 752 .width('100%') 753 .height(this.itemHeightArray[item % 100]) 754 .backgroundColor(this.colors[item % 5]) 755 }, (item: string) => item) 756 } 757 .columnsTemplate('repeat(auto-fill,80)') 758 .columnsGap(10) 759 .rowsGap(5) 760 .padding({left:5}) 761 .backgroundColor(0xFAEEE0) 762 .width('100%') 763 .height('100%') 764 } 765 } 766} 767``` 768 769 770 771 772### Example 3 773This example shows how to use **WaterFlowSections**. 774```ts 775// Index.ets 776import { WaterFlowDataSource } from './WaterFlowDataSource' 777 778@Reusable 779@Component 780struct ReusableFlowItem { 781 @State item: number = 0 782 783 // Invoked when a reusable custom component is re-added to the component tree from the reuse cache. The component state variable can be updated here to display the correct content. 784 aboutToReuse(params: Record<string, number>) { 785 this.item = params.item; 786 console.info('Reuse item:' + this.item) 787 } 788 789 aboutToAppear() { 790 console.info('new item:' + this.item) 791 } 792 793 build() { 794 Image('res/waterFlowTest(' + this.item % 5 + ').jpg') 795 .overlay('N' + this.item, { align: Alignment.Top }) 796 .objectFit(ImageFit.Fill) 797 .width('100%') 798 .layoutWeight(1) 799 } 800} 801 802@Entry 803@Component 804struct WaterFlowDemo { 805 minSize: number = 80 806 maxSize: number = 180 807 fontSize: number = 24 808 colors: number[] = [0xFFC0CB, 0xDA70D6, 0x6B8E23, 0x6A5ACD, 0x00FFFF, 0x00FF7F] 809 scroller: Scroller = new Scroller() 810 dataSource: WaterFlowDataSource = new WaterFlowDataSource() 811 dataCount: number = this.dataSource.totalCount() 812 private itemHeightArray: number[] = [] 813 @State sections: WaterFlowSections = new WaterFlowSections() 814 sectionMargin: Margin = { top: 10, left: 5, bottom: 10, right: 5 } 815 oneColumnSection: SectionOptions = { 816 itemsCount: 4, 817 crossCount: 1, 818 columnsGap: '5vp', 819 rowsGap: 10, 820 margin: this.sectionMargin, 821 onGetItemMainSizeByIndex: (index: number) => { 822 return this.itemHeightArray[index % 100] 823 } 824 } 825 twoColumnSection: SectionOptions = { 826 itemsCount: 2, 827 crossCount: 2, 828 onGetItemMainSizeByIndex: (index: number) => { 829 return 100 830 } 831 } 832 lastSection: SectionOptions = { 833 itemsCount: 20, 834 crossCount: 2, 835 onGetItemMainSizeByIndex: (index: number) => { 836 return this.itemHeightArray[index % 100] 837 } 838 } 839 840 // Calculate the FlowItem height. 841 getSize() { 842 let ret = Math.floor(Math.random() * this.maxSize) 843 return (ret > this.minSize ? ret : this.minSize) 844 } 845 846 // Set the height array for FlowItems. 847 setItemSizeArray() { 848 for (let i = 0; i < 100; i++) { 849 this.itemHeightArray.push(this.getSize()) 850 } 851 } 852 853 aboutToAppear() { 854 this.setItemSizeArray() 855 // Initialize the water flow section information. 856 let sectionOptions: SectionOptions[] = [] 857 let count = 0 858 let oneOrTwo = 0 859 while (count < this.dataCount) { 860 if (this.dataCount - count < 20) { 861 this.lastSection.itemsCount = this.dataCount - count 862 sectionOptions.push(this.lastSection) 863 break; 864 } 865 if (oneOrTwo++ % 2 == 0) { 866 sectionOptions.push(this.oneColumnSection) 867 count += this.oneColumnSection.itemsCount 868 } else { 869 sectionOptions.push(this.twoColumnSection) 870 count += this.twoColumnSection.itemsCount 871 } 872 } 873 this.sections.splice(0, 0, sectionOptions) 874 } 875 876 build() { 877 Column({ space: 2 }) { 878 Row() { 879 Button('splice') 880 .height('5%') 881 .onClick(() => { 882 // Replace all sections with a new section. Ensure that the number of data array items in LazyForEach is the same as the value of itemsCount of the new section. 883 let totalCount: number = this.dataSource.totalCount() 884 let newSection: SectionOptions = { 885 itemsCount: totalCount, 886 crossCount: 2, 887 onGetItemMainSizeByIndex: (index: number) => { 888 return this.itemHeightArray[index % 100] 889 } 890 } 891 let oldLength: number = this.sections.length() 892 this.sections.splice(0, oldLength, [newSection]) 893 }) 894 .margin({ top: 10, left: 20 }) 895 Button('update') 896 .height('5%') 897 .onClick(() => { 898 // Add four FlowItems to the second section. Ensure that the number of data array items in LazyForEach is the same as the sum of itemsCount values of all sections. 899 let newSection: SectionOptions = { 900 itemsCount: 6, 901 crossCount: 3, 902 columnsGap: 5, 903 rowsGap: 10, 904 margin: this.sectionMargin, 905 onGetItemMainSizeByIndex: (index: number) => { 906 return this.itemHeightArray[index % 100] 907 } 908 } 909 this.dataSource.addItem(this.oneColumnSection.itemsCount) 910 this.dataSource.addItem(this.oneColumnSection.itemsCount + 1) 911 this.dataSource.addItem(this.oneColumnSection.itemsCount + 2) 912 this.dataSource.addItem(this.oneColumnSection.itemsCount + 3) 913 const result: boolean = this.sections.update(1, newSection) 914 console.info('update:' + result) 915 }) 916 .margin({ top: 10, left: 20 }) 917 Button('delete') 918 .height('5%') 919 .onClick(() => { 920 // Click Update and then Delete. 921 let newSection: SectionOptions = { 922 itemsCount: 2, 923 crossCount: 2, 924 columnsGap: 5, 925 rowsGap: 10, 926 margin: this.sectionMargin, 927 onGetItemMainSizeByIndex: (index: number) => { 928 return this.itemHeightArray[index % 100] 929 } 930 } 931 this.dataSource.deleteItem(this.oneColumnSection.itemsCount) 932 this.dataSource.deleteItem(this.oneColumnSection.itemsCount) 933 this.dataSource.deleteItem(this.oneColumnSection.itemsCount) 934 this.dataSource.deleteItem(this.oneColumnSection.itemsCount) 935 this.sections.update(1, newSection) 936 }) 937 .margin({ top: 10, left: 20 }) 938 Button('values') 939 .height('5%') 940 .onClick(() => { 941 const sections: Array<SectionOptions> = this.sections.values(); 942 for (const value of sections) { 943 console.log(JSON.stringify(value)); 944 } 945 console.info('count:' + this.sections.length()) 946 }) 947 .margin({ top: 10, left: 20 }) 948 }.margin({ bottom: 20 }) 949 950 WaterFlow({ scroller: this.scroller, sections: this.sections }) { 951 LazyForEach(this.dataSource, (item: number) => { 952 FlowItem() { 953 ReusableFlowItem({ item: item }) 954 } 955 .width('100%') 956 // The value of onGetItemMainSizeByIndex is used. 957 // .height(this.itemHeightArray[item % 100]) 958 .backgroundColor(this.colors[item % 5]) 959 }, (item: string) => item) 960 } 961 .columnsTemplate('1fr 1fr') // This attribute is ineffective when the sections parameter is used. 962 .columnsGap(10) 963 .rowsGap(5) 964 .backgroundColor(0xFAEEE0) 965 .width('100%') 966 .height('100%') 967 .layoutWeight(1) 968 .onScrollIndex((first: number, last: number) => { 969 // Add data in advance when scrolling is about to end. 970 if (last + 20 >= this.dataSource.totalCount()) { 971 for (let i = 0; i < 100; i++) { 972 this.dataSource.addLastItem() 973 } 974 // After the data source is updated, update sections synchronously and change the number of FlowItems in the last section. 975 const sections: Array<SectionOptions> = this.sections.values(); 976 let newSection: SectionOptions = sections[this.sections.length() - 1]; 977 newSection.itemsCount += 100; 978 this.sections.update(-1, newSection); 979 } 980 }) 981 } 982 } 983} 984``` 985 986 987 988### Example 4 989This example implements the feature of using the pinch gesture to change the number of columns. 990 991```ts 992// Index.ets 993import { WaterFlowDataSource } from './WaterFlowDataSource' 994 995@Reusable 996@Component 997struct ReusableFlowItem { 998 @State item: number = 0 999 1000 // Invoked when a reusable custom component is re-added to the component tree from the reuse cache. The component state variable can be updated here to display the correct content. 1001 aboutToReuse(params: Record<string, number>) { 1002 this.item = params.item; 1003 console.info('Reuse item:' + this.item) 1004 } 1005 1006 aboutToAppear() { 1007 console.info('item:' + this.item) 1008 } 1009 1010 build() { 1011 Column() { 1012 Text("N" + this.item).fontSize(12).height('16') 1013 Image('res/waterFlow (' + this.item % 5 + ').JPG') 1014 .objectFit(ImageFit.Fill) 1015 .width('100%') 1016 .layoutWeight(1) 1017 } 1018 } 1019} 1020 1021@Entry 1022@Component 1023struct WaterFlowDemo { 1024 minSize: number = 80 1025 maxSize: number = 180 1026 colors: number[] = [0xFFC0CB, 0xDA70D6, 0x6B8E23, 0x6A5ACD, 0x00FFFF, 0x00FF7F] 1027 @State columns: number = 2 1028 dataSource: WaterFlowDataSource = new WaterFlowDataSource() 1029 private itemWidthArray: number[] = [] 1030 private itemHeightArray: number[] = [] 1031 1032 // Calculate the width and height of a water flow item. 1033 getSize() { 1034 let ret = Math.floor(Math.random() * this.maxSize) 1035 return (ret > this.minSize ? ret : this.minSize) 1036 } 1037 1038 // Set the width and height array of the water flow item. 1039 setItemSizeArray() { 1040 for (let i = 0; i < 100; i++) { 1041 this.itemWidthArray.push(this.getSize()) 1042 this.itemHeightArray.push(this.getSize()) 1043 } 1044 } 1045 1046 aboutToAppear() { 1047 let lastCount = AppStorage.get<number>('columnsCount') 1048 if (typeof lastCount != 'undefined') { 1049 this.columns = lastCount 1050 } 1051 this.setItemSizeArray() 1052 } 1053 1054 build() { 1055 Column({ space: 2 }) { 1056 Row() { 1057 Text('Pinch to change the number of columns') 1058 .height('5%') 1059 .margin({ top: 10, left: 20 }) 1060 } 1061 1062 WaterFlow() { 1063 LazyForEach(this.dataSource, (item: number) => { 1064 FlowItem() { 1065 ReusableFlowItem({ item: item }) 1066 } 1067 .width('100%') 1068 .height(this.itemHeightArray[item % 100]) 1069 .backgroundColor(this.colors[item % 5]) 1070 }, (item: string) => item) 1071 } 1072 .columnsTemplate('1fr '.repeat(this.columns)) 1073 .columnsGap(10) 1074 .rowsGap(5) 1075 .backgroundColor(0xFAEEE0) 1076 .width('100%') 1077 .height('100%') 1078 .layoutWeight(1) 1079 // Switching the number of columns triggers a reordering animation for the item positions. 1080 .animation({ 1081 duration: 300, 1082 curve: Curve.Smooth 1083 }) 1084 .priorityGesture( 1085 PinchGesture() 1086 .onActionEnd((event: GestureEvent) => { 1087 console.info('end scale:' + event.scale) 1088 // When a user performs a pinch-to-zoom gesture by moving their fingers apart, and the number of columns decreases to a certain threshold (in this case, 2), it will cause the items to enlarge. 1089 if (event.scale > 2) { 1090 this.columns-- 1091 } else if (event.scale < 0.6) { 1092 this.columns++ 1093 } 1094 // You can set the maximum and minimum number of columns based on the device screen width. Here, the minimum number of columns is 1, and the maximum number of columns is 4. 1095 this.columns = Math.min(4, Math.max(1, this.columns)); 1096 AppStorage.setOrCreate<number>('columnsCount', this.columns) 1097 }) 1098 ) 1099 } 1100 } 1101} 1102``` 1103 1104 1105