LazyVWaterFlowLayout
该组件用于实现支持懒加载的瀑布流布局,其父组件仅限于List、Scroll、WaterFlow、FlowItem或LazyColumnLayout,并支持使用自定义组件或NodeContainer组件封装后应用在上述组件中。
说明:
- LazyVWaterFlowLayout组件高度默认自适应内容,不建议设置高度、高度约束或宽高比,设置后会导致显示异常。
- 当父组件设置主轴方向尺寸时,LazyVWaterFlowLayout按照父组件可视区域进行懒加载;当父组件未设置主轴方向尺寸时,LazyVWaterFlowLayout会被内容撑开,导致所有子组件都会被加载布局。
- 该组件在不同父组件下的懒加载支持条件如下:
- 在List组件下,要求List组件布局方向必须是竖直方向(即listDirection属性设置为Axis.Vertical),在非竖直方向的List中使用该组件会导致应用崩溃。当List设置了lanes、chainAnimation、scrollSnapAlign属性中的任意一个时,该组件的懒加载功能会失效。
- 在Scroll组件下,要求Scroll组件布局方向必须是竖直方向(即scrollable属性设置为ScrollDirection.Vertical),在非竖直方向的Scroll中使用该组件会导致应用崩溃。
- 在WaterFlow组件下,仅在WaterFlow组件的单列模式或分段布局中的单列分段,并且布局方向为FlexDirection.Column时支持懒加载。当WaterFlow为多列模式或布局方向为FlexDirection.Row、FlexDirection.RowReverse时,该组件的懒加载功能会失效。此外,在布局方向为FlexDirection.ColumnReverse的WaterFlow组件下使用该组件会导致显示异常。
- 当懒加载功能生效时,该组件仅加载父组件可视区域内的子组件,并在帧间空闲时隙预加载可视区域上方和下方各半屏的内容。
- 此处的父组件指最靠近当前组件的上层滚动组件,其他文档下的具体含义请参考对应内容。
起始版本: 26.0.0
导入模块
import { LazyVWaterFlowLayout } from '@kit.ArkUI';
接口
LazyVWaterFlowLayout()
创建垂直方向的LazyVWaterFlowLayout组件。
起始版本: 26.0.0
原子化服务API: 从API版本26.0.0开始,该接口支持在原子化服务中使用。
模型约束: 此接口仅可在Stage模型下使用。
系统能力: SystemCapability.ArkUI.ArkUI.Full
属性
除支持通用属性外,还支持以下属性:
columnsTemplate
columnsTemplate(value: string | ItemFillPolicy | undefined)
设置当前LazyVWaterFlowLayout的列数,不设置时默认1列。
当value设置为string类型时,可设置当前LazyVWaterFlowLayout的列数或最小列宽值。例如,columnsTemplate('1fr 1fr 2fr')可将LazyVWaterFlowLayout分为3列,LazyVWaterFlowLayout宽分为4等份,第1列占1份,第2列占1份,第3列占2份。
可使用columnsTemplate('repeat(auto-fill, track-size)')根据给定的列宽track-size自动计算列数,其中repeat、auto-fill为关键字,track-size为可设置的宽度,支持的单位包括px、vp、%或有效数字,默认单位为vp。
当value设置为ItemFillPolicy类型时,将根据LazyVWaterFlowLayout组件宽度对应断点类型确定列数。
例如,ItemFillPolicy.BREAKPOINT_DEFAULT在组件宽度相当于sm及更小的设备上显示2列,相当于md设备时显示3列,相当于lg及更大的设备时显示5列,且每列均为1fr(表示每列占用1等份可用宽度)。
起始版本: 26.0.0
原子化服务API: 从API版本26.0.0开始,该接口支持在原子化服务中使用。
模型约束: 此接口仅可在Stage模型下使用。
系统能力: SystemCapability.ArkUI.ArkUI.Full
参数:
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| value | string | ItemFillPolicy | undefined | 是 | 当前LazyVWaterFlowLayout的列数或最小列宽值。 方法入参为undefined时,恢复为默认值(1列)。 |
返回值:
| 类型 | 说明 |
|---|---|
| T | 返回当前组件。 |
columnsGap
columnsGap(value: LengthMetrics | undefined): T
设置列与列的间距。默认值为0vp,设置为小于0的值时,按默认值显示。
起始版本: 26.0.0
原子化服务API: 从API版本26.0.0开始,该接口支持在原子化服务中使用。
模型约束: 此接口仅可在Stage模型下使用。
系统能力: SystemCapability.ArkUI.ArkUI.Full
参数:
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| value | LengthMetrics | undefined | 是 | 列与列的间距。 方法入参为undefined时,恢复为默认值(0vp)。 |
返回值:
| 类型 | 说明 |
|---|---|
| T | 返回当前组件。 |
rowsGap
rowsGap(value: LengthMetrics | undefined): T
设置行与行的间距。默认值为0vp,设置为小于0的值时,按默认值显示。
起始版本: 26.0.0
原子化服务API: 从API版本26.0.0开始,该接口支持在原子化服务中使用。
模型约束: 此接口仅可在Stage模型下使用。
系统能力: SystemCapability.ArkUI.ArkUI.Full
参数:
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| value | LengthMetrics | undefined | 是 | 行与行的间距。 方法入参为undefined时,恢复为默认值(0vp)。 |
返回值:
| 类型 | 说明 |
|---|---|
| T | 返回当前组件。 |
header
header(builder: CustomBuilder | undefined)
设置当前LazyVWaterFlowLayout的头部组件。
说明:
头部组件位于容器顶部区域,通常用于展示标题、分组说明或其他固定在内容前方的元素。
当本组件随滚动容器滚动至可视区域内,且通过sticky设置了header吸顶模式时,header会吸附在滚动容器可视区域顶部。
起始版本: 26.0.0
原子化服务API: 从API版本26.0.0开始,该接口支持在原子化服务中使用。
模型约束: 此接口仅可在Stage模型下使用。
系统能力: SystemCapability.ArkUI.ArkUI.Full
参数:
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| builder | CustomBuilder | undefined | 是 | 头部组件构造函数。 方法入参为undefined时,当前LazyVWaterFlowLayout不设置头部组件,如果已有头部组件,也会被移除。 |
footer
footer(builder: CustomBuilder | undefined)
设置当前LazyVWaterFlowLayout的尾部组件。
说明:
尾部组件位于容器底部区域,通常用于展示补充信息、加载状态或其他固定在内容后方的元素。
当本组件随滚动容器滚动至可视区域内,且通过sticky设置了footer吸底模式时,footer会吸附在滚动容器可视区域底部。
起始版本: 26.0.0
原子化服务API: 从API版本26.0.0开始,该接口支持在原子化服务中使用。
模型约束: 此接口仅可在Stage模型下使用。
系统能力: SystemCapability.ArkUI.ArkUI.Full
参数:
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| builder | CustomBuilder | undefined | 是 | 尾部组件构造函数。 方法入参为undefined时,当前LazyVWaterFlowLayout不设置尾部组件,如果已有尾部组件,也会被移除。 |
sticky
sticky(sticky: StickyStyle | undefined)
当本组件随滚动容器滚动至可视区域内,且通过sticky设置header吸顶或footer吸底时,header会吸附在滚动容器可视区域顶部,footer会吸附在滚动容器可视区域底部。
说明:
由于浮点数计算精度,设置sticky后,在滚动过程中小概率产生缝隙,可以通过pixelRound指定当前组件向下像素取整解决该问题。
起始版本: 26.0.0
原子化服务API: 从API版本26.0.0开始,该接口支持在原子化服务中使用。
模型约束: 此接口仅可在Stage模型下使用。
系统能力: SystemCapability.ArkUI.ArkUI.Full
参数:
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| sticky | StickyStyle | undefined | 是 | 头部组件和尾部组件的吸附模式。sticky属性可以设置为StickyStyle.Header或StickyStyle.Footer,也可以设置为StickyStyle.BOTH,以同时支持头部组件吸顶和尾部组件吸底。 方法入参为undefined时,恢复为默认值StickyStyle.None。 未通过该接口设置时,默认头部组件不吸顶、尾部组件不吸底。 |
事件
除支持通用事件外,还支持以下事件:
onVisibleIndexesChange
onVisibleIndexesChange(callback: OnVisibleIndexesChangeCallback | undefined): T
设置onVisibleIndexesChange回调函数。当LazyVWaterFlowLayout在可视区域内的子组件的索引值发生变化时触发回调,返回可视区域内子组件的起始索引值和结束索引值。
说明:
当父组件设置主轴方向尺寸时,LazyVWaterFlowLayout按照父组件可视区域进行懒加载。此时onVisibleIndexesChange回调中start返回当前可视区域起始位置子组件的索引值,end返回当前可视区域结束位置子组件的索引值。
当父组件未设置主轴方向尺寸时,LazyVWaterFlowLayout会被内容撑开,导致所有子组件都会被加载布局。此时onVisibleIndexesChange回调中start返回0,end返回数据源最后一个子组件的索引值。
此处的父组件指最靠近当前组件的上层滚动组件,其他文档下的具体含义请参考对应内容。
起始版本: 26.0.0
原子化服务API: 从API版本26.0.0开始,该接口支持在原子化服务中使用。
模型约束: 此接口仅可在Stage模型下使用。
系统能力: SystemCapability.ArkUI.ArkUI.Full
参数:
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| callback | OnVisibleIndexesChangeCallback | undefined | 是 | 回调函数,当可见区域内的子组件索引发生变化时触发。 方法入参为undefined时,取消监听。 |
返回值:
| 类型 | 说明 |
|---|---|
| T | 返回当前组件。 |
示例
示例1(实现懒加载瀑布流布局)
通过Scroll和LazyVWaterFlowLayout组件实现懒加载瀑布流布局。
MyDataSource实现了LazyForEach数据源接口IDataSource,用于通过LazyForEach给LazyVWaterFlowLayout提供子组件。
从API版本26.0.0开始,新增支持LazyVWaterFlowLayout组件。
import { LengthMetrics, LazyVWaterFlowLayout, LazyVWaterFlowLayoutAttribute } from '@kit.ArkUI';
import { MyDataSource } from './MyDataSource';
@Entry
@Component
struct LazyVWaterFlowLayoutSample1 {
private arr : MyDataSource<number> = new MyDataSource<number>();
// 返回随机高度
private itemHeight(index: number): number {
return 80 + (index * 37 % 121)
}
private itemColor(index: number): string {
const colors: string[] = ['#FFE0B2', '#C8E6C9', '#BBDEFB', '#F8BBD0', '#D1C4E9', '#FFF9C4']
return colors[index % colors.length]
}
build() {
Column() {
Scroll() {
LazyVWaterFlowLayout() {
LazyForEach(this.arr, (item: number) => {
Column() {
Text('item ' + item.toString())
.fontSize(16)
.fontColor(Color.Black)
}
.height(this.itemHeight(item))
.width('100%')
.borderRadius(8)
.backgroundColor(this.itemColor(item))
.justifyContent(FlexAlign.Center)
})
}
.columnsTemplate('1fr 1fr')
.rowsGap(LengthMetrics.vp(10))
.columnsGap(LengthMetrics.vp(10))
.onVisibleIndexesChange((start: number, end: number) => {
// 滚动监听:即将触底时提前加载更多数据
if (end + 20 >= this.arr.totalCount()) {
// 添加100个新数据到数据源
let currentCount = this.arr.totalCount();
for (let i = currentCount; i < currentCount + 100; i++) {
this.arr.pushData(i);
}
}
})
}
.padding(10)
.layoutWeight(1)
}
.width('100%')
.height('100%')
.backgroundColor('#DCDCDC')
}
aboutToAppear(): void {
for (let i = 0; i < 100; i++) {
this.arr.pushData(i);
}
}
}
// MyDataSource.ets
export class BasicDataSource<T> implements IDataSource {
private listeners: DataChangeListener[] = [];
protected dataArray: T[] = [];
public totalCount(): number {
return this.dataArray.length;
}
public getData(index: number): T {
return this.dataArray[index];
}
registerDataChangeListener(listener: DataChangeListener): void {
if (this.listeners.indexOf(listener) < 0) {
console.info('add listener');
this.listeners.push(listener);
}
}
unregisterDataChangeListener(listener: DataChangeListener): void {
const pos = this.listeners.indexOf(listener);
if (pos >= 0) {
console.info('remove listener');
this.listeners.splice(pos, 1);
}
}
notifyDataReload(): void {
this.listeners.forEach(listener => {
listener.onDataReloaded();
})
}
notifyDataAdd(index: number): void {
this.listeners.forEach(listener => {
listener.onDataAdd(index);
})
}
notifyDataChange(index: number): void {
this.listeners.forEach(listener => {
listener.onDataChange(index);
})
}
notifyDataDelete(index: number): void {
this.listeners.forEach(listener => {
listener.onDataDelete(index);
})
}
notifyDataMove(from: number, to: number): void {
this.listeners.forEach(listener => {
listener.onDataMove(from, to);
})
}
notifyDatasetChange(operations: DataOperation[]): void {
this.listeners.forEach(listener => {
listener.onDatasetChange(operations);
})
}
}
export class MyDataSource<T> extends BasicDataSource<T> {
public shiftData(): void {
this.dataArray.shift();
this.notifyDataDelete(0);
}
public unshiftData(data: T): void {
this.dataArray.unshift(data);
this.notifyDataAdd(0);
}
public pushData(data: T): void {
this.dataArray.push(data);
this.notifyDataAdd(this.dataArray.length - 1);
}
public popData(): void {
this.dataArray.pop();
this.notifyDataDelete(this.dataArray.length);
}
public clearData(): void {
this.dataArray = [];
this.notifyDataReload();
}
}

示例2(设置头部组件或尾部组件及吸附效果)
该示例通过Scroll嵌套LazyVWaterFlowLayout,并通过header、footer、sticky实现瀑布流顶部和底部吸附效果。滚动过程中header吸附在可视区域顶部,footer吸附在可视区域底部。
从API版本26.0.0开始,新增支持header、footer和sticky属性。
import { LengthMetrics, LazyVWaterFlowLayout, LazyVWaterFlowLayoutAttribute } from '@kit.ArkUI';
import { MyDataSource } from './MyDataSource';
@Entry
@Component
struct LazyVWaterFlowLayoutStickyDemo {
private arr : MyDataSource<number> = new MyDataSource<number>();
// 返回随机高度
private itemHeight(index: number): number {
return 80 + (index * 37 % 121)
}
private itemColor(index: number): string {
const colors: string[] = ['#FFE0B2', '#C8E6C9', '#BBDEFB', '#F8BBD0', '#D1C4E9', '#FFF9C4']
return colors[index % colors.length]
}
@Builder
HeaderBuilder() {
Column() {
Text('Header')
.fontSize(16)
.fontColor(Color.Black)
}
.height(50)
.width('100%')
.borderRadius(8)
.backgroundColor('#BBDEFB')
.justifyContent(FlexAlign.Center)
}
@Builder
FooterBuilder() {
Column() {
Text('Footer')
.fontSize(16)
.fontColor(Color.Black)
}
.height(40)
.width('100%')
.borderRadius(8)
.backgroundColor('#D1C4E9')
.justifyContent(FlexAlign.Center)
}
build() {
Column() {
Scroll() {
LazyVWaterFlowLayout() {
LazyForEach(this.arr, (item: number) => {
Column() {
Text('item ' + item.toString())
.fontSize(16)
.fontColor(Color.Black)
}
.height(this.itemHeight(item))
.width('100%')
.borderRadius(8)
.backgroundColor(this.itemColor(item))
.justifyContent(FlexAlign.Center)
})
}
.columnsTemplate('1fr 1fr')
.rowsGap(LengthMetrics.vp(10))
.columnsGap(LengthMetrics.vp(10))
.header(this.HeaderBuilder)
.footer(this.FooterBuilder)
.sticky(StickyStyle.BOTH)
.onVisibleIndexesChange((start: number, end: number) => {
// 滚动监听:即将触底时提前加载更多数据
if (end + 20 >= this.arr.totalCount()) {
// 添加100个新数据到数据源
let currentCount = this.arr.totalCount();
for (let i = currentCount; i < currentCount + 100; i++) {
this.arr.pushData(i);
}
}
})
}
.padding(10)
.layoutWeight(1)
}
.width('100%')
.height('100%')
.backgroundColor('#DCDCDC')
}
aboutToAppear(): void {
for (let i = 0; i < 100; i++) {
this.arr.pushData(i);
}
}
}
