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