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.Column 或 FlexDirection.ColumnReverse) 339 340 此时columnsTemplate有效(如果未设置,取默认值)。例如columnsTemplate设置为"1fr 1fr"、rowsTemplate设置为"1fr 1fr 1fr"时,瀑布流组件纵向布局,辅轴均分成横向2列。 341 342- layoutDirection设置横向布局(FlexDirection.Row 或 FlexDirection.RowReverse) 343 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 \| [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 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 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 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 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 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 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