HarmonyOS6 - WaterFlow瀑布流容器案例
开发环境为:
开发工具:DevEco Studio 6.0.1 Release
API版本是:API21
本文所有代码都已使用模拟器测试成功!
1. WaterFlow介绍
瀑布流容器,由"行"和"列"分割的单元格所组成,通过容器自身的排列规则,将不同大小的"项目"自上而下,如瀑布般紧密布局。
注意:
- 仅支持FlowItem子组件和自定义组件。
- 自定义组件在WaterFlow下使用时,建议使用FlowItem作为自定组件的顶层组件,不建议给自定义组件设置属性和事件方法。
- 支持通过渲染控制类型(if/else、ForEach、LazyForEach和Repeat)动态生成子组件,更推荐使用LazyForEach或Repeat以优化性能。
- WaterFlow子组件的visibility属性设置为None时不显示,但该子组件周围的columnsGap、rowsGap、margin仍会生效。
- 在涉及大量子组件的情况下,建议采用懒加载、缓存数据、组件复用、固定宽高以及布局优化等方法,以提升性能和减少内存占用。
- 纵向布局时,WaterFlow会计算每一列中已放置子组件的累计高度,并将新子组件放入累计高度最小的那一列,以保持整体布局紧凑。
- 若多个列的高度相同,优先放入最左边的列。在RTL模式下,优先放入最右边的列。
- 从API version 21开始,WaterFlow单个子组件的宽高最大为16777216px;API version 20及之前,WaterFlow单个子组件的宽高最大为1000000px。子组件超出该大小可能导致滚动或显示异常。
官网文档:https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-container-waterflow
2. 案例
1. 效果
效果图如下:

2. 代码
代码如下:
js
import CommonConstants from '../common/CommonConstants'
import { CardModel } from '../model/CardModel';
import { WaterFlowDataSource } from '../model/WaterFlowDataSource';
/**
* Desc: 社区
* Author: 波波老师(weixin: javabobo0513)
*/
@Entry
@Component
export struct Page002 {
@StorageProp('bottomRectHeight')
bottomRectHeight: number = 0;
@StorageProp('topRectHeight')
topRectHeight: number = 0;
@State listData: Array<CardModel> = [
new CardModel($r('app.media.c1'), '社区老年人活动游戏', $r('app.media.user_1003'), '刘小太太', '4586'),
new CardModel($r('app.media.c2'), '社区组织的剪纸活动', $r('app.media.user_1004'), '一颗红心', '2635'),
new CardModel($r('app.media.c3'), '社区走访独居老人', $r('app.media.user_1006'), '牡丹花心', '2369'),
new CardModel($r('app.media.c4'), '人间群像「空巢老人的聚会」', $r('app.media.user_1007'), '奔跑的玫瑰', '3018'),
new CardModel($r('app.media.c1'), '社区老年人活动游戏', $r('app.media.user_1003'), '刘小太太', '4586'),
new CardModel($r('app.media.c2'), '社区组织的剪纸活动', $r('app.media.user_1004'), '一颗红心', '2635'),
new CardModel($r('app.media.c3'), '社区走访独居老人', $r('app.media.user_1006'), '牡丹花心', '2369'),
new CardModel($r('app.media.c4'), '人间群像「空巢老人的聚会」', $r('app.media.user_1007'), '奔跑的玫瑰', '3018'),
]
@State dataSource: WaterFlowDataSource = new WaterFlowDataSource();
scroller: Scroller = new Scroller();
aboutToAppear(): void {
this.dataSource.addDataItem(this.listData);
}
build() {
Column() {
//头部标题
Row() {
Text('社区')
.fontSize(20)
.fontWeight(800)
}
.width('100%')
.justifyContent(FlexAlign.Center)
.margin({ bottom: 5 })
//内容区域
Scroll() {
Column() {
WaterFlow({ scroller: this.scroller }) {
LazyForEach(this.dataSource, (item: CardModel) => {
FlowItem() {
Column({ space: 7 }) {
Image(item.img)
.borderRadius(8)
.width('100%')
Text(item.title)
.fontSize(15)
Row() {
Row({ space: 3 }) {
Image(item.userImg)
.borderRadius(50)
.width(18)
Text(item.userName)
.fontSize(11)
}
Row({ space: 3 }) {
Image($r('app.media.svg_like'))
.fillColor('#ff8d8d8d')
.width(18)
Text(item.likeCount)
.fontSize(11)
}
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
}
.width('100%')
.alignItems(HorizontalAlign.Start)
}
.width('100%')
.backgroundColor('#ffffff')
})
}
.columnsTemplate('1fr 1fr') //设置当前瀑布流组件布局列的数量,不设置时默认1列
.columnsGap(12) //设置列与列的间距。
.rowsGap(15) //设置行与行的间距。
.height('90%')
// 单边边缘效果:设置弹簧效果,仅在顶部生效
// EdgeEffect.Spring:弹簧回弹效果,滑动到边界时会有弹性回弹
// alwaysEnabled: true:始终启用边缘效果,即使内容不足以滚动
// effectEdge: EffectEdge.START:仅在起始边缘(顶部)生效
// 效果:只有向上滑动到顶部时才会有弹簧回弹效果,向下滑动到底部不会有效果
.edgeEffect(EdgeEffect.Spring, { alwaysEnabled: true, effectEdge: EffectEdge.START })
}
.width('100%')
}
.scrollable(ScrollDirection.Vertical) // 滚动方向纵向
.scrollBar(BarState.Off) // 设置滚动条隐藏
.edgeEffect(EdgeEffect.Fade) //设置滑动效果
.align(Alignment.Top) //内容顶部对齐
.height('100%') //设置高度
}
.width('100%')
.height('100%')
// top数值与状态栏区域高度保持一致;bottom数值与导航区域高度保持一致
.padding({
top: this.getUIContext().px2vp(this.topRectHeight),
bottom: this.getUIContext().px2vp(this.bottomRectHeight),
left: CommonConstants.LEFT_PADDING,
right: CommonConstants.RIGHT_PADDING
})
}
}
WaterFlowDataSource代码如下:
js
import { CardModel } from "./CardModel";
/**
* 实现IDataSource接口的对象,用于瀑布流组件加载数据
*/
export class WaterFlowDataSource implements IDataSource {
private dataArray: CardModel[] = [];
private listeners: DataChangeListener[] = [];
// 获取索引对应的数据
public getData(index: number): CardModel {
return this.dataArray[index];
}
// 通知控制器数据重新加载
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);
})
}
// 获取数据总数
public totalCount(): number {
return this.dataArray.length;
}
// 注册改变数据的控制器
registerDataChangeListener(listener: DataChangeListener): void {
if (this.listeners.indexOf(listener) < 0) {
this.listeners.push(listener);
}
}
// 注销改变数据的控制器
unregisterDataChangeListener(listener: DataChangeListener): void {
const pos = this.listeners.indexOf(listener);
if (pos >= 0) {
this.listeners.splice(pos, 1);
}
}
// 增加数据
public addDataItem(data: Array<CardModel>): void {
if (data.length > 0) {
for (let index = 0; index < data.length; index++) {
this.dataArray.push(data[index]);
}
this.notifyDataAdd(0);
}
}
// 删除第一个元素
public delete1stItem(): void {
this.dataArray.splice(0, 1);
this.notifyDataDelete(0);
}
// 删除第二个元素
public delete2ndItem(): void {
this.dataArray.splice(1, 1);
this.notifyDataDelete(1);
}
// 删除最后一个元素
public deleteLastItem(): void {
this.dataArray.splice(-1, 1);
this.notifyDataDelete(this.dataArray.length);
}
// 在指定索引位置删除一个元素
public deleteItem(index: number): void {
this.dataArray.splice(index, 1);
this.notifyDataDelete(index);
}
// 重新加载数据
public reload(): void {
this.dataArray.splice(1, 1);
this.dataArray.splice(3, 2);
this.notifyDataReload();
}
}
CardModel代码如下:
js
/**
* 小卡片模型
*/
export class CardModel {
img: ResourceStr; //封面
title: string; //标题
userImg: ResourceStr; //用户头像
userName: string; //用户昵称
likeCount: string; //点赞量
constructor(img: ResourceStr, title: string, userImg: ResourceStr, userName: string, likeCount: string) {
this.img = img;
this.title = title;
this.userImg = userImg;
this.userName = userName;
this.likeCount = likeCount;
}
}
最后
- 希望本文对你有所帮助!
- 本人如有任何错误或不当之处,请留言指出,谢谢!