• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# WaterFlow
2
3
4瀑布流容器,由“行”和“列”分割的单元格所组成,通过容器自身的排列规则,将不同大小的“项目”自上而下,如瀑布般紧密布局。
5
6
7> **说明:**
8>
9> 该组件从API version 9 开始支持。后续版本如有新增内容,则采用上角标单独标记该内容的起始版本。
10
11
12## 子组件
13
14
15仅支持[FlowItem](ts-container-flowitem.md)子组件,支持渲染控制类型([if/else](../../../ui/state-management/arkts-rendering-control-ifelse.md)、[ForEach](../../../ui/state-management/arkts-rendering-control-foreach.md)、[LazyForEach](../../../ui/state-management/arkts-rendering-control-lazyforeach.md)和[Repeat](../../../ui/state-management/arkts-new-rendering-control-repeat.md))。
16
17>  **说明:**
18>
19>  WaterFlow子组件的visibility属性设置为None时不显示,但该子组件周围的columnsGap、rowsGap、margin仍会生效。
20
21## 接口
22
23
24WaterFlow(options?:  WaterFlowOptions)
25
26创建瀑布流容器。
27
28**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。
29
30**系统能力:** SystemCapability.ArkUI.ArkUI.Full
31
32**参数:**
33
34| 参数名 | 类型 | 必填 | 说明 |
35| -------- | -------- | -------- | -------- |
36| options |  [WaterFlowOptions](#waterflowoptions对象说明)| 否 | 瀑布流组件参数。 |
37
38
39## WaterFlowOptions对象说明
40
41瀑布流组件参数对象。
42
43**系统能力:** SystemCapability.ArkUI.ArkUI.Full
44
45| 名称     | 类型                                        | 必填 | 说明                                     |
46| ---------- | ----------------------------------------------- | ------ | -------------------------------------------- |
47| footer |  [CustomBuilder](ts-types.md#custombuilder8) | 否   | 设置WaterFlow尾部组件。<br/>**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。 |
48| footerContent<sup>18+</sup> | [ComponentContent](../js-apis-arkui-ComponentContent.md) | 否 | 设置WaterFlow尾部组件。<br/>该参数的优先级高于参数footer,即同时设置footer和footerContent时,以footerContent设置的组件为准。<br/>**原子化服务API:** 从API version 18开始,该接口支持在原子化服务中使用。 |
49| scroller | [Scroller](ts-container-scroll.md#scroller) | 否   | 可滚动组件的控制器,与可滚动组件绑定。<br/>**说明:** <br/>不允许和其他滚动类组件,如:[ArcList](ts-container-arclist.md)、[List](ts-container-list.md)、[Grid](ts-container-grid.md)、[Scroll](ts-container-scroll.md)和[WaterFlow](ts-container-waterflow.md)绑定同一个滚动控制对象。<br/>**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。 |
50| sections<sup>12+</sup> |  [WaterFlowSections](#waterflowsections12) | 否   | 设置FlowItem分组,实现同一个瀑布流组件内部各分组使用不同列数混合布局。<br/>**说明:** <br/>1. 使用分组混合布局时会忽略columnsTemplate和rowsTemplate属性。<br/>2. 使用分组混合布局时不支持单独设置footer,可以使用最后一个分组作为尾部组件。<br/>**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。  |
51| layoutMode<sup>12+</sup> |[WaterFlowLayoutMode](#waterflowlayoutmode12枚举说明) | 否 | 设置WaterFlow的布局模式,根据使用场景选择更切合的模式。<br/>**说明:** <br/>默认值:[ALWAYS_TOP_DOWN](#waterflowlayoutmode12枚举说明)。<br/>**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
52
53
54## WaterFlowSections<sup>12+</sup>
55
56瀑布流分组信息。
57
58> **说明:**
59>
60> 使用splice、push、update修改分组信息后需要保证所有分组子节点总数与瀑布流实际子节点总数一致,否则会出现瀑布流因为不能正常布局而无法滑动的问题。
61
62### constructor
63
64constructor()
65
66创建一个瀑布流分组。
67
68**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
69
70**系统能力:** SystemCapability.ArkUI.ArkUI.Full
71
72### splice<sup>12+</sup>
73
74splice(start: number, deleteCount?: number, sections?: Array\<SectionOptions\>): boolean
75
76移除或者替换已存在的分组和/或添加新分组。
77
78**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
79
80**系统能力:** SystemCapability.ArkUI.ArkUI.Full
81
82**参数:**
83
84| 参数名   | 类型                            | 必填   | 说明                   |
85| ---- | ----------------------------- | ---- | -------------------- |
86| start | number | 是    | 从0开始计算的索引,会转换为整数,表示要开始改变分组的位置。<br/>**说明:** <br/>1. 如果索引是负数,则从末尾开始计算,使用`start + WaterFlowSections.length()`。<br/>2. 如果 `start < -WaterFlowSections.length()`,则使用0。<br/>3. 如果 `start >= WaterFlowSections.length()`,则在最后添加新分组。 |
87| deleteCount | number | 否    | 表示要从start开始删除的分组数量。<br/>**说明:** <br/>1. 如果省略了deleteCount,或者其值大于或等于由start指定的位置到WaterFlowSections末尾的分组数量,那么从start到WaterFlowSections末尾的所有分组将被删除。<br/>2. 如果deleteCount是0或者负数,则不会删除任何分组。 |
88| sections | Array<[SectionOptions](#sectionoptions12对象说明)> | 否    | 表示要从start开始加入的分组。如果不指定,`splice()`将只从瀑布流中删除分组。 |
89
90**返回值:**
91
92| 类型                                                         | 说明                                                         |
93| ------------------------------------------------------------ | ------------------------------------------------------------ |
94| boolean | 分组修改成功返回true;修改失败(要加入的分组中有任意分组的itemsCount不是正整数)返回false。 |
95
96
97### push<sup>12+</sup>
98
99push(section: SectionOptions): boolean
100
101将指定分组添加到瀑布流末尾。
102
103**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
104
105**系统能力:** SystemCapability.ArkUI.ArkUI.Full
106
107**参数:**
108
109| 参数名   | 类型                            | 必填   | 说明                   |
110| ---- | ----------------------------- | ---- | -------------------- |
111| section | [SectionOptions](#sectionoptions12对象说明) | 是    | 添加到瀑布流末尾的分组。 |
112
113**返回值:**
114
115| 类型                                                         | 说明                                                         |
116| ------------------------------------------------------------ | ------------------------------------------------------------ |
117| boolean | 分组添加成功返回true,添加失败(新分组的itemsCount不是正整数)返回false。 |
118
119### update<sup>12+</sup>
120
121update(sectionIndex: number, section: SectionOptions): boolean
122
123修改指定索引分组的配置信息。
124
125**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
126
127**系统能力:** SystemCapability.ArkUI.ArkUI.Full
128
129**参数:**
130
131| 参数名   | 类型                            | 必填   | 说明                   |
132| ---- | ----------------------------- | ---- | -------------------- |
133| sectionIndex | number | 是    | 从0开始计算的索引,会转换为整数,表示要修改的分组的位置。<br/>**说明:** <br/>1. 如果索引是负数,则从末尾开始计算,使用`sectionIndex + WaterFlowSections.length()`。<br/>2. 如果`sectionIndex < -WaterFlowSections.length()`,则使用0。<br/>3. 如果`sectionIndex >= WaterFlowSections.length()`,则在最后添加新分组。 |
134| section | [SectionOptions](#sectionoptions12对象说明) | 是    | 新的分组信息。 |
135
136**返回值:**
137
138| 类型                                                         | 说明                                                         |
139| ------------------------------------------------------------ | ------------------------------------------------------------ |
140| boolean | 分组是否更新成功,新分组的itemsCount不是正整数时返回false。 |
141
142### values<sup>12+</sup>
143
144values(): Array\<SectionOptions\>
145
146获取瀑布流中所有分组配置信息。
147
148**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
149
150**系统能力:** SystemCapability.ArkUI.ArkUI.Full
151
152**返回值:**
153
154| 类型                                                         | 说明                                                         |
155| ------------------------------------------------------------ | ------------------------------------------------------------ |
156| Array<[SectionOptions](#sectionoptions12对象说明)> | 瀑布流中所有分组配置信息。 |
157
158### length<sup>12+</sup>
159
160length(): number
161
162获取瀑布流中分组数量。
163
164**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
165
166**系统能力:** SystemCapability.ArkUI.ArkUI.Full
167
168**返回值:**
169
170| 类型                                                         | 说明                                                         |
171| ------------------------------------------------------------ | ------------------------------------------------------------ |
172| number | 瀑布流中分组数量。 |
173
174## SectionOptions<sup>12+</sup>对象说明
175
176FlowItem分组配置信息。
177
178**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
179
180**系统能力:** SystemCapability.ArkUI.ArkUI.Full
181
182| 名称 | 类型 | 必填 | 说明 |
183|------|-----|-----|-----|
184| itemsCount | number | 是 | 分组中FlowItem数量,必须是正整数。若splice、push、update方法收到的分组中有分组的itemsCount小于0,则不会执行该方法。 |
185| crossCount | number | 否 | 纵向布局时为列数,横向布局时为行数,默认值:1。小于1的按默认值处理。 |
186| columnsGap | [Dimension](ts-types.md#dimension10) | 否 | 该分组的列间距,不设置时使用瀑布流的columnsGap,设置非法值时使用0vp。 |
187| rowsGap | [Dimension](ts-types.md#dimension10) | 否 | 该分组的行间距,不设置时使用瀑布流的rowsGap,设置非法值时使用0vp。 |
188| margin | [Margin](ts-types.md#margin) \| [Dimension](ts-types.md#dimension10) | 否 | 该分组的外边距参数为Length类型时,四个方向外边距同时生效。<br>默认值:0<br>单位:vp<br>margin设置百分比时,上下左右外边距均以瀑布流的width作为基础值。 |
189| onGetItemMainSizeByIndex | [GetItemMainSizeByIndex](#getitemmainsizebyindex12) | 否 | 瀑布流组件布局过程中获取指定index的FlowItem的主轴大小,纵向瀑布流时为高度,横向瀑布流时为宽度,单位vp。<br/>**说明:** <br/>1. 同时使用onGetItemMainSizeByIndex和FlowItem的宽高属性时,主轴大小以onGetItemMainSizeByIndex返回结果为准,onGetItemMainSizeByIndex会覆盖FlowItem的主轴长度。<br/>2. 使用onGetItemMainSizeByIndex可以提高瀑布流跳转到指定位置或index时的效率,避免混用设置onGetItemMainSizeByIndex和未设置的分组,会导致布局异常。<br/>3. onGetItemMainSizeByIndex返回负数时FlowItem高度为0。 |
190
191
192## GetItemMainSizeByIndex<sup>12+</sup>
193
194type GetItemMainSizeByIndex = (index: number) => number
195
196根据index获取指定Item的主轴大小。
197
198**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
199
200**系统能力:** SystemCapability.ArkUI.ArkUI.Full
201
202**参数:**
203
204| 参数名   | 类型                            | 必填   | 说明                   |
205| ---- | ----------------------------- | ---- | -------------------- |
206| index | number | 是    | FlowItem在WaterFlow中的索引。<br/>取值范围:[0, 子节点总数-1] |
207
208**返回值:**
209
210| 类型                                                         | 说明                                                         |
211| ------------------------------------------------------------ | ------------------------------------------------------------ |
212| number | 指定index的FlowItem的主轴大小,纵向瀑布流时为高度,横向瀑布流时为宽度,单位vp。 |
213
214## WaterFlowLayoutMode<sup>12+</sup>枚举说明
215
216瀑布流组件布局模式枚举。
217
218**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
219
220**系统能力:** SystemCapability.ArkUI.ArkUI.Full
221
222| 名称 | 值 | 说明 |
223| ------ | ------ | -------------------- |
224| ALWAYS_TOP_DOWN | 0 | 默认的从上到下的布局模式。视窗内的FlowItem依赖视窗上方所有FlowItem的布局信息。因此跳转或切换列数时,需要计算出上方所有的FlowItem的布局信息。 |
225| SLIDING_WINDOW | 1 | 移动窗口式的布局模式。只考虑视窗内的布局信息,对视窗上方的FlowItem没有依赖关系,因此向后跳转或切换列数时只需要布局视窗内的FlowItem。建议优先使用该模式。 <br/>**说明:** <br/>1. 无动画跳转到较远的位置时,会以目标位置为基准,向前或向后布局FlowItem。这之后如果滑回跳转前的位置,内容的布局效果可能和之前不一致。 这个效果会导致跳转后回滑到顶部时,顶部节点可能不对齐。所以该布局模式下会在滑动到顶部后自动调整布局,保证顶部对齐。在有多个分组的情况下,会在滑动结束时调整在视窗内的分组。<br/> 2. [scroller](#waterflowoptions对象说明)的[currentOffset](ts-container-scroll.md#currentoffset)接口返回的总偏移量在触发跳转或数据更新后不准确,在回滑到顶部时会重新校准。 <br/> 3. 如果在同一帧内调用跳转(如无动画的[scrollToIndex](ts-container-scroll.md#scrolltoindex)、[scrollEdge](ts-container-scroll.md#scrolledge))和输入偏移量(如滑动手势或滚动动画),两者都会生效。 <br/> 4. 调用无动画的[scrollToIndex](ts-container-scroll.md#scrolltoindex)进行跳转,如果跳转到较远位置(超过视窗内的FlowItem数量的位置)时,移动窗口模式对总偏移量进行估算。 |
226
227
228## 属性
229
230除支持[通用属性](ts-component-general-attributes.md)和[滚动组件通用属性](ts-container-scrollable-common.md#属性)外,还支持以下属性:
231
232### columnsTemplate
233
234columnsTemplate(value: string)
235
236设置当前瀑布流组件布局列的数量,不设置时默认1列。
237
238例如,'1fr 1fr 2fr' 是将父组件分3列,将父组件允许的宽分为4等份,第一列占1份,第二列占1份,第三列占2份。
239
240可使用columnsTemplate('repeat(auto-fill,track-size)')根据给定的列宽track-size自动计算列数,其中repeat、auto-fill为关键字,track-size为可设置的宽度,支持的单位包括px、vp、%或有效数字,默认单位为vp,使用方法参见示例2。
241
242**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。
243
244**系统能力:** SystemCapability.ArkUI.ArkUI.Full
245
246**参数:**
247
248| 参数名 | 类型   | 必填 | 说明                                           |
249| ------ | ------ | ---- | ---------------------------------------------- |
250| value  | string | 是   | 当前瀑布流组件布局列的数量。<br/>默认值:'1fr' |
251
252### rowsTemplate
253
254rowsTemplate(value: string)
255
256设置当前瀑布流组件布局行的数量,不设置时默认1行。
257
258例如,'1fr 1fr 2fr'是将父组件分三行,将父组件允许的高分为4等份,第一行占1份,第二行占一份,第三行占2份。
259
260可使用rowsTemplate('repeat(auto-fill,track-size)')根据给定的行高track-size自动计算行数,其中repeat、auto-fill为关键字,track-size为可设置的高度,支持的单位包括px、vp、%或有效数字,默认单位为vp。
261
262**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。
263
264**系统能力:** SystemCapability.ArkUI.ArkUI.Full
265
266**参数:**
267
268| 参数名 | 类型   | 必填 | 说明                                           |
269| ------ | ------ | ---- | ---------------------------------------------- |
270| value  | string | 是   | 当前瀑布流组件布局行的数量。<br/>默认值:'1fr' |
271
272### itemConstraintSize
273
274itemConstraintSize(value: ConstraintSizeOptions)
275
276设置约束尺寸,子组件布局时,进行尺寸范围限制。
277
278**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。
279
280**系统能力:** SystemCapability.ArkUI.ArkUI.Full
281
282**参数:**
283
284| 参数名 | 类型                                                       | 必填 | 说明       |
285| ------ | ---------------------------------------------------------- | ---- | ---------- |
286| value  | [ConstraintSizeOptions](ts-types.md#constraintsizeoptions) | 是   | 约束尺寸。设置小于0的值,参数不生效。 <br/>**说明:**<br/>1.同时设置itemConstraintSize和FlowItem的[constraintSize](ts-universal-attributes-size.md#constraintsize)属性时,minWidth/minHeight会取其中的最大值,maxWidth/maxHeight会取其中的最小值,调整后的值作为FlowItem的constraintSize处理。2.只设置itemConstraintSize时,相当于对WaterFlow所有子组件设置了相同的constraintSize。3.itemConstraintSize通过以上两种方式转换成FlowItem的constraintSize后的生效规则与通用属性[constraintSize](./ts-universal-attributes-size.md#constraintsize)相同。|
287
288### columnsGap
289
290columnsGap(value: Length)
291
292设置列与列的间距。
293
294**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。
295
296**系统能力:** SystemCapability.ArkUI.ArkUI.Full
297
298**参数:**
299
300| 参数名 | 类型                         | 必填 | 说明                          |
301| ------ | ---------------------------- | ---- | ----------------------------- |
302| value  | [Length](ts-types.md#length) | 是   | 列与列的间距。 <br/>默认值:0<br/>取值范围:[0, +∞) |
303
304### rowsGap
305
306rowsGap(value: Length)
307
308设置行与行的间距。
309
310**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。
311
312**系统能力:** SystemCapability.ArkUI.ArkUI.Full
313
314**参数:**
315
316| 参数名 | 类型                         | 必填 | 说明                          |
317| ------ | ---------------------------- | ---- | ----------------------------- |
318| value  | [Length](ts-types.md#length) | 是   | 行与行的间距。 <br/>默认值:0<br/>取值范围:[0, +∞) |
319
320### layoutDirection
321
322layoutDirection(value: FlexDirection)
323
324设置布局的主轴方向。
325
326**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。
327
328**系统能力:** SystemCapability.ArkUI.ArkUI.Full
329
330**参数:**
331
332| 参数名 | 类型                                                | 必填 | 说明                                              |
333| ------ | --------------------------------------------------- | ---- | ------------------------------------------------- |
334| value  | [FlexDirection](ts-appendix-enums.md#flexdirection) | 是   | 布局的主轴方向。<br/>默认值:FlexDirection.Column |
335
336layoutDirection优先级高于rowsTemplate和columnsTemplate。根据layoutDirection设置情况,分为以下三种设置模式:
337
338- layoutDirection设置纵向布局(FlexDirection.ColumnFlexDirection.ColumnReverse339
340  此时columnsTemplate有效(如果未设置,取默认值)。例如columnsTemplate设置为"1fr 1fr"、rowsTemplate设置为"1fr 1fr 1fr"时,瀑布流组件纵向布局,辅轴均分成横向2列。
341
342- layoutDirection设置横向布局(FlexDirection.RowFlexDirection.RowReverse343
344  此时rowsTemplate有效(如果未设置,取默认值)。例如columnsTemplate设置为"1fr 1fr"、rowsTemplate设置为"1fr 1fr 1fr"时,瀑布流组件横向布局,辅轴均分成纵向3列。
345
346- layoutDirection未设置布局方向
347
348  布局方向为layoutDirection的默认值:FlexDirection.Column,此时columnsTemplate有效。例如columnsTemplate设置为"1fr 1fr"、rowsTemplate设置为"1fr 1fr 1fr"时,瀑布流组件纵向布局,辅轴均分成横向2列。
349
350### enableScrollInteraction<sup>10+</sup>
351
352enableScrollInteraction(value: boolean)
353
354设置是否支持滚动手势。
355
356**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。
357
358**系统能力:** SystemCapability.ArkUI.ArkUI.Full
359
360**参数:**
361
362| 参数名 | 类型    | 必填 | 说明                                |
363| ------ | ------- | ---- | ----------------------------------- |
364| value  | boolean | 是   | 是否支持滚动手势。设置为true时可以通过手指或者鼠标滚动,设置为false时无法通过手指或者鼠标滚动,但不影响控制器[Scroller](ts-container-scroll.md#scroller)的滚动接口。<br/>默认值:true |
365
366### nestedScroll<sup>10+</sup>
367
368nestedScroll(value: NestedScrollOptions)
369
370设置前后两个方向的嵌套滚动模式,实现与父组件的滚动联动。
371
372**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。
373
374**系统能力:** SystemCapability.ArkUI.ArkUI.Full
375
376**参数:**
377
378| 参数名 | 类型                                                         | 必填 | 说明           |
379| ------ | ------------------------------------------------------------ | ---- | -------------- |
380| value  | [NestedScrollOptions](ts-container-scrollable-common.md#nestedscrolloptions10对象说明) | 是   | 嵌套滚动选项。 |
381
382### friction<sup>10+</sup>
383
384friction(value: number | Resource)
385
386设置摩擦系数,手动划动滚动区域时生效,仅影响惯性滚动过程,对惯性滚动过程中的链式效果有间接影响。
387
388**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。
389
390**系统能力:** SystemCapability.ArkUI.ArkUI.Full
391
392**参数:**
393
394| 参数名 | 类型                                                 | 必填 | 说明                                                      |
395| ------ | ---------------------------------------------------- | ---- | --------------------------------------------------------- |
396| value  | number&nbsp;\|&nbsp;[Resource](ts-types.md#resource) | 是   | 摩擦系数。<br/>默认值:非可穿戴设备为0.6,可穿戴设备为0.9。<br/>从API version 11开始,非可穿戴设备默认值为0.7。<br/>从API version 12开始,非可穿戴设备默认值为0.75。<br/>取值范围:(0, +∞),设置为小于等于0的值时,按默认值处理。 |
397
398### cachedCount<sup>11+</sup>
399
400cachedCount(value: number)
401
402设置预加载的FlowItem的数量,只在LazyForEach中生效。设置该属性后会缓存cachedCount个FlowItem。[LazyForEach](../../../ui/state-management/arkts-rendering-control-lazyforeach.md)超出显示和缓存范围的FlowItem会被释放。
403
404**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
405
406**系统能力:** SystemCapability.ArkUI.ArkUI.Full
407
408**参数:**
409
410| 参数名 | 类型   | 必填 | 说明                                                         |
411| ------ | ------ | ---- | ------------------------------------------------------------ |
412| value  | number | 是   | 预加载的FlowItem的数量。 <br/>默认值:根据屏幕内显示的节点个数设置,最大值为16。<br/>取值范围:[0, +∞),设置为小于0的值时,按1处理。 |
413
414### cachedCount<sup>14+</sup>
415
416cachedCount(count: number, show: boolean)
417
418设置预加载的FlowItem数量,并配置是否显示预加载节点。
419
420配合[裁剪](ts-universal-attributes-sharp-clipping.md#clip12)或[内容裁剪](ts-container-scrollable-common.md#clipcontent14)属性可以显示出预加载节点。
421
422[LazyForEach](../../../ui/state-management/arkts-rendering-control-lazyforeach.md)和开启了virtualScroll开关的[Repeat](../../../ui/state-management/arkts-new-rendering-control-repeat.md)超出显示和缓存范围的FlowItem会被释放。
423
424**原子化服务API:** 从API version 14开始,该接口支持在原子化服务中使用。
425
426**系统能力:** SystemCapability.ArkUI.ArkUI.Full
427
428**参数:**
429
430| 参数名 | 类型   | 必填 | 说明                                     |
431| ------ | ------ | ---- | ---------------------------------------- |
432| count | number | 是   | 预加载的FlowItem的数量。 <br/>默认值:根据屏幕内显示的节点个数设置,最大值为16。<br/>取值范围:[0, +∞),设置为小于0的值时,按1处理。 |
433| show  | boolean | 是   | 被预加载的FlowItem是否需要显示。设置为true时显示预加载的FlowItem,设置为false时不显示预加载的FlowItem。 <br/> 默认值:false |
434
435## 事件
436
437除支持[通用事件](ts-component-general-events.md)和[滚动组件通用事件](ts-container-scrollable-common.md#事件)外,还支持以下事件:
438
439### onReachStart
440
441onReachStart(event: () => void)
442
443瀑布流内容到达起始位置时触发。
444
445**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。
446
447**系统能力:** SystemCapability.ArkUI.ArkUI.Full
448
449### onReachEnd
450
451onReachEnd(event: () => void)
452
453瀑布流内容到达末尾位置时触发。
454
455**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。
456
457**系统能力:** SystemCapability.ArkUI.ArkUI.Full
458
459### onScrollFrameBegin<sup>10+</sup>
460
461onScrollFrameBegin(event: (offset: number, state: ScrollState) => { offsetRemain: number; })
462
463瀑布流开始滑动时触发,事件参数传入即将发生的滑动量,事件处理函数中可根据应用场景计算实际需要的滑动量并作为事件处理函数的返回值返回,瀑布流将按照返回值的实际滑动量进行滑动。
464
465触发该事件的条件:手指拖动WaterFlow、WaterFlow惯性划动时每帧开始时触发;WaterFlow超出边缘回弹、使用滚动控制器和拖动滚动条的滚动不会触发。
466
467**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。
468
469**系统能力:** SystemCapability.ArkUI.ArkUI.Full
470
471**参数:**
472
473| 参数名 | 类型                                                    | 必填 | 说明                       |
474| ------ | ------------------------------------------------------- | ---- | -------------------------- |
475| offset | number                                                  | 是   | 即将发生的滑动量,单位vp。 |
476| state  | [ScrollState](ts-container-list.md#scrollstate枚举说明) | 是   | 当前滑动状态。             |
477
478**返回值:**
479
480| 类型                     | 说明                 |
481| ------------------------ | -------------------- |
482| { offsetRemain: number } | 实际滑动量,单位vp。 |
483
484### onScrollIndex<sup>11+</sup>
485
486onScrollIndex(event: (first: number, last: number) => void)
487
488当前瀑布流显示的起始位置/终止位置的子组件发生变化时触发。瀑布流初始化时会触发一次。
489
490瀑布流显示区域上第一个子组件/最后一个组件的索引值有变化就会触发。
491
492**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。
493
494**系统能力:** SystemCapability.ArkUI.ArkUI.Full
495
496**参数:**
497
498| 参数名 | 类型   | 必填 | 说明                                  |
499| ------ | ------ | ---- | ------------------------------------- |
500| first  | number | 是   | 当前显示的瀑布流起始位置的索引值。<br/>取值范围:[0, 子节点总数-1] |
501| last   | number | 是   | 当前显示的瀑布流终止位置的索引值。<br/>取值范围:[0, 子节点总数-1] |
502
503## 示例
504
505### 示例1(使用基本瀑布流)
506该示例展示了WaterFlow组件数据加载处理、属性设置和事件回调等基本使用场景。
507
508<!--code_no_check-->
509```ts
510// WaterFlowDataSource.ets
511
512// 实现IDataSource接口的对象,用于瀑布流组件加载数据
513export class WaterFlowDataSource implements IDataSource {
514  private dataArray: number[] = [];
515  private listeners: DataChangeListener[] = [];
516
517  constructor() {
518    for (let i = 0; i < 100; i++) {
519      this.dataArray.push(i);
520    }
521  }
522
523  // 获取索引对应的数据
524  public getData(index: number): number {
525    return this.dataArray[index];
526  }
527
528  // 通知控制器数据重新加载
529  notifyDataReload(): void {
530    this.listeners.forEach(listener => {
531      listener.onDataReloaded();
532    })
533  }
534
535  // 通知控制器数据增加
536  notifyDataAdd(index: number): void {
537    this.listeners.forEach(listener => {
538      listener.onDataAdd(index);
539    })
540  }
541
542  // 通知控制器数据变化
543  notifyDataChange(index: number): void {
544    this.listeners.forEach(listener => {
545      listener.onDataChange(index);
546    })
547  }
548
549  // 通知控制器数据删除
550  notifyDataDelete(index: number): void {
551    this.listeners.forEach(listener => {
552      listener.onDataDelete(index);
553    })
554  }
555
556  // 通知控制器数据位置变化
557  notifyDataMove(from: number, to: number): void {
558    this.listeners.forEach(listener => {
559      listener.onDataMove(from, to);
560    })
561  }
562
563  //通知控制器数据批量修改
564  notifyDatasetChange(operations: DataOperation[]): void {
565    this.listeners.forEach(listener => {
566      listener.onDatasetChange(operations);
567    })
568  }
569
570  // 获取数据总数
571  public totalCount(): number {
572    return this.dataArray.length;
573  }
574
575  // 注册改变数据的控制器
576  registerDataChangeListener(listener: DataChangeListener): void {
577    if (this.listeners.indexOf(listener) < 0) {
578      this.listeners.push(listener);
579    }
580  }
581
582  // 注销改变数据的控制器
583  unregisterDataChangeListener(listener: DataChangeListener): void {
584    const pos = this.listeners.indexOf(listener);
585    if (pos >= 0) {
586      this.listeners.splice(pos, 1);
587    }
588  }
589
590  // 增加数据
591  public add1stItem(): void {
592    this.dataArray.splice(0, 0, this.dataArray.length);
593    this.notifyDataAdd(0);
594  }
595
596  // 在数据尾部增加一个元素
597  public addLastItem(): void {
598    this.dataArray.splice(this.dataArray.length, 0, this.dataArray.length);
599    this.notifyDataAdd(this.dataArray.length - 1);
600  }
601
602  // 在指定索引位置增加一个元素
603  public addItem(index: number): void {
604    this.dataArray.splice(index, 0, this.dataArray.length);
605    this.notifyDataAdd(index);
606  }
607
608  // 删除第一个元素
609  public delete1stItem(): void {
610    this.dataArray.splice(0, 1);
611    this.notifyDataDelete(0);
612  }
613
614  // 删除第二个元素
615  public delete2ndItem(): void {
616    this.dataArray.splice(1, 1);
617    this.notifyDataDelete(1);
618  }
619
620  // 删除最后一个元素
621  public deleteLastItem(): void {
622    this.dataArray.splice(-1, 1);
623    this.notifyDataDelete(this.dataArray.length);
624  }
625
626  // 在指定索引位置删除一个元素
627  public deleteItem(index: number): void {
628    this.dataArray.splice(index, 1);
629    this.notifyDataDelete(index);
630  }
631
632  // 重新加载数据
633  public reload(): void {
634    this.dataArray.splice(1, 1);
635    this.dataArray.splice(3, 2);
636    this.notifyDataReload();
637  }
638}
639```
640
641<!--code_no_check-->
642```ts
643// Index.ets
644import { WaterFlowDataSource } from './WaterFlowDataSource';
645
646@Entry
647@Component
648struct WaterFlowDemo {
649  @State minSize: number = 80;
650  @State maxSize: number = 180;
651  @State fontSize: number = 24;
652  @State colors: number[] = [0xFFC0CB, 0xDA70D6, 0x6B8E23, 0x6A5ACD, 0x00FFFF, 0x00FF7F];
653  scroller: Scroller = new Scroller();
654  dataSource: WaterFlowDataSource = new WaterFlowDataSource();
655  private itemWidthArray: number[] = [];
656  private itemHeightArray: number[] = [];
657
658  // 计算FlowItem宽/高
659  getSize() {
660    let ret = Math.floor(Math.random() * this.maxSize);
661    return (ret > this.minSize ? ret : this.minSize);
662  }
663
664  // 设置FlowItem的宽/高数组
665  setItemSizeArray() {
666    for (let i = 0; i < 100; i++) {
667      this.itemWidthArray.push(this.getSize());
668      this.itemHeightArray.push(this.getSize());
669    }
670  }
671
672  aboutToAppear() {
673    this.setItemSizeArray();
674  }
675
676  @Builder
677  itemFoot() {
678    Column() {
679      Text(`Footer`)
680        .fontSize(10)
681        .backgroundColor(Color.Red)
682        .width(50)
683        .height(50)
684        .align(Alignment.Center)
685        .margin({ top: 2 })
686    }
687  }
688
689  build() {
690    Column({ space: 2 }) {
691      WaterFlow() {
692        LazyForEach(this.dataSource, (item: number) => {
693          FlowItem() {
694            Column() {
695              Text("N" + item).fontSize(12).height('16')
696              // 存在对应的jpg文件才会显示图片
697              Image('res/waterFlowTest(' + item % 5 + ').jpg')
698                .objectFit(ImageFit.Fill)
699                .width('100%')
700                .layoutWeight(1)
701            }
702          }
703          .onAppear(() => {
704            // 即将触底时提前增加数据
705            if (item + 20 == this.dataSource.totalCount()) {
706              for (let i = 0; i < 100; i++) {
707                this.dataSource.addLastItem();
708              }
709            }
710          })
711          .width('100%')
712          .height(this.itemHeightArray[item % 100])
713          .backgroundColor(this.colors[item % 5])
714        }, (item: string) => item)
715      }
716      .columnsTemplate("1fr 1fr")
717      .columnsGap(10)
718      .rowsGap(5)
719      .backgroundColor(0xFAEEE0)
720      .width('100%')
721      .height('100%')
722      .onReachStart(() => {
723        console.info('waterFlow reach start');
724      })
725      .onScrollStart(() => {
726        console.info('waterFlow scroll start');
727      })
728      .onScrollStop(() => {
729        console.info('waterFlow scroll stop');
730      })
731      .onScrollFrameBegin((offset: number, state: ScrollState) => {
732        console.info('waterFlow scrollFrameBegin offset: ' + offset + ' state: ' + state.toString());
733        return { offsetRemain: offset };
734      })
735    }
736  }
737}
738```
739
740![zh-cn_image_WaterFlow.gif](figures/waterflow-perf-demo.gif)
741
742### 示例2(自动计算列数)
743该示例通过auto-fill实现了自动计算列数的效果。
744
745<!--code_no_check-->
746```ts
747// Index.ets
748import { WaterFlowDataSource } from './WaterFlowDataSource';
749
750@Entry
751@Component
752struct WaterFlowDemo {
753  @State minSize: number = 80;
754  @State maxSize: number = 180;
755  @State colors: number[] = [0xFFC0CB, 0xDA70D6, 0x6B8E23, 0x6A5ACD, 0x00FFFF, 0x00FF7F];
756  dataSource: WaterFlowDataSource = new WaterFlowDataSource();
757  private itemWidthArray: number[] = [];
758  private itemHeightArray: number[] = [];
759
760  // 计算FlowItem宽/高
761  getSize() {
762    let ret = Math.floor(Math.random() * this.maxSize);
763    return (ret > this.minSize ? ret : this.minSize);
764  }
765
766  // 设置FlowItem宽/高数组
767  setItemSizeArray() {
768    for (let i = 0; i < 100; i++) {
769      this.itemWidthArray.push(this.getSize());
770      this.itemHeightArray.push(this.getSize());
771    }
772  }
773
774  aboutToAppear() {
775    this.setItemSizeArray();
776  }
777
778  build() {
779    Column({ space: 2 }) {
780      WaterFlow() {
781        LazyForEach(this.dataSource, (item: number) => {
782          FlowItem() {
783            Column() {
784              Text("N" + item).fontSize(12).height('16')
785              Image('res/waterFlowTest(' + item % 5 + ').jpg')
786            }
787          }
788          .width('100%')
789          .height(this.itemHeightArray[item % 100])
790          .backgroundColor(this.colors[item % 5])
791        }, (item: string) => item)
792      }
793      .columnsTemplate('repeat(auto-fill,80)')
794      .columnsGap(10)
795      .rowsGap(5)
796      .padding({left:5})
797      .backgroundColor(0xFAEEE0)
798      .width('100%')
799      .height('100%')
800    }
801  }
802}
803```
804
805![waterflow_auto-fill.png](figures/waterflow_auto-fill.png)
806
807
808### 示例3(使用分组)
809该示例展示了分组的初始化以及splice、push、update、values、length等接口的不同效果。
810如果配合状态管理V2使用,详情见:[WaterFlow与makeObserved](../../../ui/state-management/arkts-v1-v2-migration.md#waterflow)。
811
812<!--code_no_check-->
813```ts
814// Index.ets
815import { WaterFlowDataSource } from './WaterFlowDataSource';
816
817@Reusable
818@Component
819struct ReusableFlowItem {
820  @State item: number = 0;
821
822  // 从复用缓存中加入到组件树之前调用,可在此处更新组件的状态变量以展示正确的内容
823  aboutToReuse(params: Record<string, number>) {
824    this.item = params.item;
825    console.info('Reuse item:' + this.item);
826  }
827
828  aboutToAppear() {
829    console.info('new item:' + this.item);
830  }
831
832  build() {
833    Image('res/waterFlowTest(' + this.item % 5 + ').jpg')
834        .overlay('N' + this.item, { align: Alignment.Top })
835        .objectFit(ImageFit.Fill)
836        .width('100%')
837        .layoutWeight(1)
838  }
839}
840
841@Entry
842@Component
843struct WaterFlowDemo {
844  minSize: number = 80;
845  maxSize: number = 180;
846  fontSize: number = 24;
847  colors: number[] = [0xFFC0CB, 0xDA70D6, 0x6B8E23, 0x6A5ACD, 0x00FFFF, 0x00FF7F];
848  scroller: Scroller = new Scroller();
849  dataSource: WaterFlowDataSource = new WaterFlowDataSource();
850  dataCount: number = this.dataSource.totalCount();
851  private itemHeightArray: number[] = [];
852  @State sections: WaterFlowSections = new WaterFlowSections();
853  sectionMargin: Margin = { top: 10, left: 5, bottom: 10, right: 5 };
854  oneColumnSection: SectionOptions = {
855    itemsCount: 4,
856    crossCount: 1,
857    columnsGap: '5vp',
858    rowsGap: 10,
859    margin: this.sectionMargin,
860    onGetItemMainSizeByIndex: (index: number) => {
861      return this.itemHeightArray[index % 100];
862    }
863  }
864  twoColumnSection: SectionOptions = {
865    itemsCount: 2,
866    crossCount: 2,
867    onGetItemMainSizeByIndex: (index: number) => {
868      return 100;
869    }
870  }
871  lastSection: SectionOptions = {
872    itemsCount: 20,
873    crossCount: 2,
874    onGetItemMainSizeByIndex: (index: number) => {
875      return this.itemHeightArray[index % 100];
876    }
877  }
878
879  // 计算FlowItem高度
880  getSize() {
881    let ret = Math.floor(Math.random() * this.maxSize);
882    return (ret > this.minSize ? ret : this.minSize);
883  }
884
885  // 设置FlowItem的高度数组
886  setItemSizeArray() {
887    for (let i = 0; i < 100; i++) {
888      this.itemHeightArray.push(this.getSize());
889    }
890  }
891
892  aboutToAppear() {
893    this.setItemSizeArray();
894    // 初始化瀑布流分组信息
895    let sectionOptions: SectionOptions[] = [];
896    let count = 0;
897    let oneOrTwo = 0;
898    while (count < this.dataCount) {
899      if (this.dataCount - count < 20) {
900        this.lastSection.itemsCount = this.dataCount - count;
901        sectionOptions.push(this.lastSection);
902        break;
903      }
904      if (oneOrTwo++ % 2 == 0) {
905        sectionOptions.push(this.oneColumnSection);
906        count += this.oneColumnSection.itemsCount;
907      } else {
908        sectionOptions.push(this.twoColumnSection);
909        count += this.twoColumnSection.itemsCount;
910      }
911    }
912    this.sections.splice(0, 0, sectionOptions);
913  }
914
915  build() {
916    Column({ space: 2 }) {
917      Row() {
918        Button('splice')
919          .height('5%')
920          .onClick(() => {
921            // 将所有分组替换成一个新分组,注意保证LazyForEach中数据数量和新分组itemsCount保持一致
922            let totalCount: number = this.dataSource.totalCount();
923            let newSection: SectionOptions = {
924              itemsCount: totalCount,
925              crossCount: 2,
926              onGetItemMainSizeByIndex: (index: number) => {
927                return this.itemHeightArray[index % 100];
928              }
929            }
930            let oldLength: number = this.sections.length();
931            this.sections.splice(0, oldLength, [newSection]);
932          })
933          .margin({ top: 10, left: 20 })
934        Button('update')
935          .height('5%')
936          .onClick(() => {
937            // 在第二个分组增加4个FlowItem,注意保证LazyForEach中数据数量和所有分组itemsCount的和保持一致
938            let newSection: SectionOptions = {
939              itemsCount: 6,
940              crossCount: 3,
941              columnsGap: 5,
942              rowsGap: 10,
943              margin: this.sectionMargin,
944              onGetItemMainSizeByIndex: (index: number) => {
945                return this.itemHeightArray[index % 100];
946              }
947            }
948            this.dataSource.addItem(this.oneColumnSection.itemsCount);
949            this.dataSource.addItem(this.oneColumnSection.itemsCount + 1);
950            this.dataSource.addItem(this.oneColumnSection.itemsCount + 2);
951            this.dataSource.addItem(this.oneColumnSection.itemsCount + 3);
952            const result: boolean = this.sections.update(1, newSection);
953            console.info('update:' + result);
954          })
955          .margin({ top: 10, left: 20 })
956        Button('delete')
957          .height('5%')
958          .onClick(() => {
959            // 先点击update再点击delete
960            let newSection: SectionOptions = {
961              itemsCount: 2,
962              crossCount: 2,
963              columnsGap: 5,
964              rowsGap: 10,
965              margin: this.sectionMargin,
966              onGetItemMainSizeByIndex: (index: number) => {
967                return this.itemHeightArray[index % 100];
968              }
969            }
970            this.dataSource.deleteItem(this.oneColumnSection.itemsCount);
971            this.dataSource.deleteItem(this.oneColumnSection.itemsCount);
972            this.dataSource.deleteItem(this.oneColumnSection.itemsCount);
973            this.dataSource.deleteItem(this.oneColumnSection.itemsCount);
974            this.sections.update(1, newSection);
975          })
976          .margin({ top: 10, left: 20 })
977        Button('values')
978          .height('5%')
979          .onClick(() => {
980            const sections: Array<SectionOptions> = this.sections.values();
981            for (const value of sections) {
982              console.log(JSON.stringify(value));
983            }
984            console.info('count:' + this.sections.length());
985          })
986          .margin({ top: 10, left: 20 })
987      }.margin({ bottom: 20 })
988
989      WaterFlow({ scroller: this.scroller, sections: this.sections }) {
990        LazyForEach(this.dataSource, (item: number) => {
991          FlowItem() {
992            ReusableFlowItem({ item: item })
993          }
994          .width('100%')
995          // 以onGetItemMainSizeByIndex为准
996          // .height(this.itemHeightArray[item % 100])
997          .backgroundColor(this.colors[item % 5])
998        }, (item: string) => item)
999      }
1000      .columnsTemplate('1fr 1fr') // 瀑布流使用sections参数时该属性无效
1001      .columnsGap(10)
1002      .rowsGap(5)
1003      .backgroundColor(0xFAEEE0)
1004      .width('100%')
1005      .height('100%')
1006      .layoutWeight(1)
1007      .onScrollIndex((first: number, last: number) => {
1008        // 即将触底时提前增加数据
1009        if (last + 20 >= this.dataSource.totalCount()) {
1010          for (let i = 0; i < 100; i++) {
1011            this.dataSource.addLastItem();
1012          }
1013          // 更新数据源后同步更新sections,修改最后一个section的FlowItem数量
1014          const sections: Array<SectionOptions> = this.sections.values();
1015          let newSection: SectionOptions = sections[this.sections.length() - 1];
1016          newSection.itemsCount += 100;
1017          this.sections.update(-1, newSection);
1018        }
1019      })
1020    }
1021  }
1022}
1023```
1024
1025![waterflowSections.png](figures/waterflowSections.png)
1026
1027### 示例4(双指缩放改变列数)
1028该示例通过[priorityGesture](ts-gesture-settings.md)和[PinchGesture](ts-basic-gestures-pinchgesture.md)实现了双指缩放改变列数效果。
1029
1030<!--code_no_check-->
1031```ts
1032// Index.ets
1033import { WaterFlowDataSource } from './WaterFlowDataSource';
1034import { image } from '@kit.ImageKit';
1035
1036@Reusable
1037@Component
1038struct ReusableFlowItem {
1039  @State item: number = 0;
1040
1041  // 从复用缓存中加入到组件树之前调用,可在此处更新组件的状态变量以展示正确的内容
1042  aboutToReuse(params: Record<string, number>) {
1043    this.item = params.item;
1044  }
1045
1046  build() {
1047    Column() {
1048      Text("N" + this.item).fontSize(12).height('16')
1049      Image('res/waterFlow(' + this.item % 5 + ').JPG')
1050        .objectFit(ImageFit.Fill)
1051        .width('100%')
1052        .layoutWeight(1)
1053    }
1054  }
1055}
1056
1057@Entry
1058@Component
1059struct WaterFlowDemo {
1060  minSize: number = 80;
1061  maxSize: number = 180;
1062  colors: number[] = [0xFFC0CB, 0xDA70D6, 0x6B8E23, 0x6A5ACD, 0x00FFFF, 0x00FF7F];
1063  dataSource: WaterFlowDataSource = new WaterFlowDataSource();
1064  private itemWidthArray: number[] = [];
1065  private itemHeightArray: number[] = [];
1066  @State columns: number = 2;
1067  @State waterFlowScale: number = 1;
1068  @State imageScale: number = 1;
1069  @State waterFlowOpacity: number = 1;
1070  @State waterFlowSnapshot: image.PixelMap | undefined = undefined;
1071  private columnChanged: boolean = false;
1072  private oldColumn: number = this.columns;
1073  private pinchTime: number = 0;
1074
1075  // 计算FlowItem宽/高
1076  getSize() {
1077    let ret = Math.floor(Math.random() * this.maxSize);
1078    return (ret > this.minSize ? ret : this.minSize);
1079  }
1080
1081  // 设置FlowItem的宽/高数组
1082  setItemSizeArray() {
1083    for (let i = 0; i < 100; i++) {
1084      this.itemWidthArray.push(this.getSize());
1085      this.itemHeightArray.push(this.getSize());
1086    }
1087  }
1088
1089  aboutToAppear() {
1090    // 读取上次最后切换到到列数
1091    let lastCount = AppStorage.get<number>('columnsCount');
1092    if (typeof lastCount != 'undefined') {
1093      this.columns = lastCount;
1094    }
1095    this.setItemSizeArray();
1096  }
1097
1098  // 根据缩放阈值改变列数,触发WaterFlow重新布局
1099  changeColumns(scale: number) {
1100    if (scale > (this.columns / (this.columns - 0.5)) && this.columns > 1) {
1101      this.columns--;
1102      this.columnChanged = true;
1103    } else if (scale < 1 && this.columns < 4) {
1104      this.columns++;
1105      this.columnChanged = true;
1106    }
1107  }
1108
1109  build() {
1110    Column({ space: 2 }) {
1111      Row() {
1112        Text('双指缩放改变列数')
1113          .height('5%')
1114          .margin({ top: 10, left: 20 })
1115      }
1116
1117      Stack() {
1118        // 用于展示缩放前的WaterFlow截图
1119        Image(this.waterFlowSnapshot)
1120          .width('100%')
1121          .height('100%')
1122          .scale({
1123            x: this.imageScale,
1124            y: this.imageScale,
1125            centerX: 0,
1126            centerY: 0
1127          })
1128
1129        WaterFlow() {
1130          LazyForEach(this.dataSource, (item: number) => {
1131            FlowItem() {
1132              ReusableFlowItem({ item: item })
1133            }
1134            .width('100%')
1135            .aspectRatio(this.itemHeightArray[item % 100] / this.itemWidthArray[item%100])
1136            .backgroundColor(this.colors[item % 5])
1137          }, (item: string) => item)
1138        }
1139        .id('waterflow') // 设置id用于截图
1140        .columnsTemplate('1fr '.repeat(this.columns))
1141        .backgroundColor(0xFAEEE0)
1142        .width('100%')
1143        .height('100%')
1144        .layoutWeight(1)
1145        .opacity(this.waterFlowOpacity)
1146        .scale({
1147          x: this.waterFlowScale,
1148          y: this.waterFlowScale,
1149          centerX: 0,
1150          centerY: 0
1151        })
1152        .priorityGesture(
1153          PinchGesture()
1154            .onActionStart((event: GestureEvent) => {
1155              // 双指捏合手势识别成功时截图
1156              this.pinchTime = event.timestamp;
1157              this.columnChanged = false;
1158              this.oldColumn = this.columns;
1159              this.getUIContext().getComponentSnapshot().get('waterflow', (error: Error, pixmap: image.PixelMap) => {
1160                if (error) {
1161                  console.info('error:' + JSON.stringify(error));
1162                  return;
1163                }
1164                this.waterFlowSnapshot = pixmap;
1165              })
1166            })
1167            .onActionUpdate((event: GestureEvent) => {
1168              // 缩放列数限制
1169              if ((this.oldColumn === 1 && event.scale > 1) || (this.oldColumn === 4 && event.scale < 1)) {
1170                return;
1171              }
1172              if (event.timestamp - this.pinchTime < 10000000) {
1173                return;
1174              }
1175              this.pinchTime = event.timestamp;
1176
1177              this.waterFlowScale = event.scale;
1178              this.imageScale = event.scale;
1179              // 根据缩放比例设置WaterFlow透明度
1180              this.waterFlowOpacity = (this.waterFlowScale > 1) ? (this.waterFlowScale - 1) : (1 - this.waterFlowScale);
1181              this.waterFlowOpacity *= 3;
1182              if (!this.columnChanged) {
1183                this.changeColumns(event.scale);
1184              }
1185              // 限制缩放比例避免出现空白
1186              if (this.columnChanged) {
1187                this.waterFlowScale = this.imageScale * this.columns / this.oldColumn;
1188                if (event.scale < 1) {
1189                  this.waterFlowScale = this.waterFlowScale > 1 ? this.waterFlowScale : 1;
1190                } else {
1191                  this.waterFlowScale = this.waterFlowScale < 1 ? this.waterFlowScale : 1;
1192                }
1193              }
1194            })
1195            .onActionEnd((event: GestureEvent) => {
1196              // 离手做动画归位
1197              this.getUIContext()?.animateTo({ duration: 300 }, () => {
1198                this.waterFlowScale = 1;
1199                this.waterFlowOpacity = 1;
1200              })
1201              // 记录当前列数
1202              AppStorage.setOrCreate<number>('columnsCount', this.columns);
1203            })
1204        )
1205      }
1206    }
1207  }
1208}
1209```
1210
1211![pinch](figures/waterflow-pinch.gif)
1212
1213### 示例5(设置边缘渐隐效果)
1214该示例通过[fadingEdge](ts-container-scrollable-common.md#fadingedge14)实现了WaterFlow组件开启边缘渐隐效果,并通过fadingEdgeLength参数设置边缘渐隐长度。
1215
1216<!--code_no_check-->
1217```ts
1218// Index.ets
1219import { LengthMetrics } from '@kit.ArkUI';
1220import { WaterFlowDataSource } from './WaterFlowDataSource';
1221
1222@Entry
1223@Component
1224struct WaterFlowDemo {
1225  @State minSize: number = 80;
1226  @State maxSize: number = 180;
1227  @State colors: number[] = [0xFFC0CB, 0xDA70D6, 0x6B8E23, 0x6A5ACD, 0x00FFFF, 0x00FF7F];
1228  dataSource: WaterFlowDataSource = new WaterFlowDataSource();
1229  scroller: Scroller = new Scroller();
1230  private itemWidthArray: number[] = [];
1231  private itemHeightArray: number[] = [];
1232
1233  // 计算FlowItem宽/高
1234  getSize() {
1235    let ret = Math.floor(Math.random() * this.maxSize);
1236    return (ret > this.minSize ? ret : this.minSize);
1237  }
1238
1239  // 设置FlowItem宽/高数组
1240  setItemSizeArray() {
1241    for (let i = 0; i < 100; i++) {
1242      this.itemWidthArray.push(this.getSize());
1243      this.itemHeightArray.push(this.getSize());
1244    }
1245  }
1246
1247  aboutToAppear() {
1248    this.setItemSizeArray();
1249  }
1250
1251  build() {
1252    Column({ space: 2 }) {
1253      WaterFlow({ scroller: this.scroller }) {
1254        LazyForEach(this.dataSource, (item: number) => {
1255          FlowItem() {
1256            Column() {
1257              Text("N" + item).fontSize(12).height('16')
1258            }
1259          }
1260          .width('100%')
1261          .height(this.itemHeightArray[item % 100])
1262          .backgroundColor(this.colors[item % 5])
1263        }, (item: string) => item)
1264      }
1265      .columnsTemplate('repeat(auto-fill,80)')
1266      .columnsGap(10)
1267      .rowsGap(5)
1268      .height('90%')
1269      .scrollBar(BarState.On)
1270      .fadingEdge(true, { fadingEdgeLength: LengthMetrics.vp(80) })
1271    }
1272  }
1273}
1274```
1275
1276![fadingEdge_waterFlow](figures/fadingEdge_waterFlow.gif)
1277
1278### 示例6(单边边缘效果)
1279
1280该示例通过edgeEffect接口,实现了WaterFlow组件设置单边边缘效果。
1281
1282<!--code_no_check-->
1283```ts
1284// Index.ets
1285import { WaterFlowDataSource } from './WaterFlowDataSource';
1286
1287@Entry
1288@Component
1289struct WaterFlowDemo {
1290  @State minSize: number = 80;
1291  @State maxSize: number = 180;
1292  @State colors: number[] = [0xFFC0CB, 0xDA70D6, 0x6B8E23, 0x6A5ACD, 0x00FFFF, 0x00FF7F];
1293  dataSource: WaterFlowDataSource = new WaterFlowDataSource();
1294  scroller: Scroller = new Scroller();
1295  private itemWidthArray: number[] = [];
1296  private itemHeightArray: number[] = [];
1297
1298  // 计算FlowItem宽/高
1299  getSize() {
1300    let ret = Math.floor(Math.random() * this.maxSize);
1301    return (ret > this.minSize ? ret : this.minSize);
1302  }
1303
1304  // 设置FlowItem宽/高数组
1305  setItemSizeArray() {
1306    for (let i = 0; i < 100; i++) {
1307      this.itemWidthArray.push(this.getSize());
1308      this.itemHeightArray.push(this.getSize());
1309    }
1310  }
1311
1312  aboutToAppear() {
1313    this.setItemSizeArray();
1314  }
1315
1316  build() {
1317    Column({ space: 2 }) {
1318      WaterFlow({ scroller: this.scroller }) {
1319        LazyForEach(this.dataSource, (item: number) => {
1320          FlowItem() {
1321            Column() {
1322              Text("N" + item).fontSize(12).height('16')
1323            }
1324          }
1325          .width('100%')
1326          .height(this.itemHeightArray[item % 100])
1327          .backgroundColor(this.colors[item % 5])
1328        }, (item: string) => item)
1329      }
1330      .columnsTemplate('repeat(auto-fill,80)')
1331      .columnsGap(10)
1332      .rowsGap(5)
1333      .height('90%')
1334      .edgeEffect(EdgeEffect.Spring, { alwaysEnabled: true, effectEdge: EffectEdge.START })
1335
1336    }
1337  }
1338}
1339```
1340
1341![edgeEffect_waterFlow](figures/edgeEffect_waterflow.gif)
1342
1343### 示例7(WaterFlow组件设置和改变尾部组件)
1344
1345该示例通过footerContent接口,实现了WaterFlow组件设置尾部组件。通过ComponentContent的update函数更新尾部组件。
1346
1347<!--code_no_check-->
1348```ts
1349// Index.ets
1350import { ComponentContent, UIContext } from "@kit.ArkUI";
1351import { WaterFlowDataSource } from './WaterFlowDataSource';
1352
1353class Params {
1354  text: string = "";
1355
1356  constructor(text: string) {
1357    this.text = text;
1358  }
1359}
1360
1361@Builder
1362function buildText(params: Params) {
1363  Column() {
1364    Text(params.text)
1365      .fontSize(20)
1366      .fontWeight(FontWeight.Bold)
1367      .margin(20)
1368  }
1369}
1370
1371@Entry
1372@Component
1373struct Index {
1374  @State message1: string = "已经到底了";
1375  @State message2: string = "加载更多";
1376  @State colors: number[] = [0xD5D5D5, 0x7F7F7F, 0xF7F7F7];
1377  @State minSize: number = 80;
1378  @State maxSize: number = 180;
1379  context: UIContext = this.getUIContext();
1380  footerContent: ComponentContent<Params> = new ComponentContent<Params>(this.context, wrapBuilder<[Params]>(buildText), new Params(this.message1));
1381  dataSource: WaterFlowDataSource = new WaterFlowDataSource();
1382  private itemWidthArray: number[] = [];
1383  private itemHeightArray: number[] = [];
1384
1385  // 计算FlowItem宽/高
1386  getSize() {
1387    let ret = Math.floor(Math.random() * this.maxSize);
1388    return (ret > this.minSize ? ret : this.minSize);
1389  }
1390
1391  // 设置FlowItem宽/高数组
1392  setItemSizeArray() {
1393    for (let i = 0; i < 100; i++) {
1394      this.itemWidthArray.push(this.getSize());
1395      this.itemHeightArray.push(this.getSize());
1396    }
1397  }
1398
1399  aboutToAppear() {
1400    this.setItemSizeArray();
1401  }
1402
1403  build() {
1404    Row() {
1405      Column() {
1406        Button("更新footer").width('90%').margin(20)
1407          .onClick((event?: ClickEvent) => {
1408            this.footerContent.update(new Params(this.message2));
1409          })
1410        WaterFlow({ footerContent: this.footerContent }) {
1411          LazyForEach(this.dataSource, (item: number) => {
1412            FlowItem() {
1413              Column() {
1414                Text("N" + item).fontSize(12).height('16')
1415              }
1416              .width('100%')
1417              .height(this.itemHeightArray[item % 100])
1418              .backgroundColor(this.colors[item % 3])
1419              .justifyContent(FlexAlign.Center)
1420              .alignItems(HorizontalAlign.Center)
1421            }
1422          }, (item: string) => item)
1423        }
1424        .columnsTemplate('1fr')
1425        .height("90%")
1426      }
1427      .width('100%')
1428      .height('100%')
1429    }
1430    .height('100%')
1431  }
1432}
1433```
1434
1435![waterFlow_footerContent](figures/waterFlow_footerContent.gif)