HarmonyOS鸿蒙Next中通过自定义布局实现瀑布流效果
HarmonyOS鸿蒙Next中通过自定义布局实现瀑布流效果 在开发中,我们常使用系统预置的布局组件来快速构建界面。但是在实际开发中我们可能经常会遇到不规则、动态变化的布局需求,例如Pinterest风格的瀑布流布局。这种布局中,子元素的高度不一,但需要紧密排列,并自动填充到当前最短的列中,系统布局组件不太好直接实现。
那么我们如何实现一种这样的瀑布流效果呢?
实现思路
第一步:应用 measure 修饰符,在容器组件(如 Stack)上,首先应用 .measure() 修饰符。.measure() 接收一个回调函数,该函数的参数是 MeasureResult 数组(每个子组件的测量结果)和父容器的 Constraint。
在这个回调中,我们遍历所有子组件,调用 child.measure() 来获取它们期望的尺寸。
根据瀑布流逻辑(找最短列、计算坐标),我们为每个子组件确定其位置和尺寸,并存储到 itemInfos 数组中。最后,返回整个容器的总尺寸。
第二步:应用 layout 修饰符,在 .measure() 之后,链式调用 .layout() 修饰符。.layout() 接收一个回调函数,其参数是 PlaceResult 数组(由 measure 阶段产生)。在这个回调中,我们遍历所有子组件,调用 child.place(),并从 itemInfos 中取出之前计算好的坐标,将子组件放置到正确的位置。
使用场景
瀑布流相关的场景,如图片瀑布流,文章瀑布流等。
实现效果

完整代码
interface ImageItem {
id:number;
url: Resource;
height:number;
}
@Entry
@Component
struct WaterFlowWithLazyLoadPage {
// 图片数据
@State images: ImageItem[] = [];
// 加载状态
@State isLoading: boolean = true;
// 图片加载状态映射
@State imageLoadStatus: Record<number, 'loading' | 'loaded' | 'error'> = {};
// 页面加载时初始化数据
aboutToAppear() {
this.loadData();
}
// 加载数据
async loadData() {
try {
this.isLoading = true;
// 模拟图片数据
const list: ImageItem[] = []
for (let i = 0; i < 20; i++) {
list.push({
id: i + 1,
url: $r('app.media.4'),
height: Math.floor(Math.random() * 200) + 200
})
}
this.images = list
// 初始化图片加载状态
this.images.forEach(item => {
this.imageLoadStatus[item.id] = 'loading';
});
} catch (error) {
console.error('加载图片数据失败:', error);
} finally {
this.isLoading = false;
}
}
// 图片加载完成
onImageLoad(id: number) {
this.imageLoadStatus[id] = 'loaded';
}
// 图片加载失败
onImageError(id: number) {
this.imageLoadStatus[id] = 'error';
}
build() {
Column() {
if (this.isLoading) {
Column() {
LoadingProgress()
.color('#007DFF')
.size({ width: 40, height: 40 })
Text('加载中...')
.fontSize(14)
.margin({ top: 12 })
}
.height('100%')
.justifyContent(FlexAlign.Center)
} else {
WaterFlow() {
ForEach(this.images, (item: ImageItem) => {
FlowItem() {
Stack() {
Image(item.url)
.width('100%')
.height(item.height)
.objectFit(ImageFit.Cover)
.borderRadius(12)
.margin(8)
.onComplete(() => this.onImageLoad(item.id)) // 加载完成回调
.onError(() => this.onImageError(item.id)) // 加载失败回调
}
}
}, (item: ImageItem) => item.id.toString())
}
.columnsTemplate('1fr 1fr')
.columnsGap(16)
.rowsGap(16)
.padding(16)
}
}
.width('100%')
.height('100%')
}
}
更多关于HarmonyOS鸿蒙Next中通过自定义布局实现瀑布流效果的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
很好
真不错的
学到了
在HarmonyOS Next中,可通过ArkUI的Grid组件结合自定义布局管理器实现瀑布流。使用WaterFlowLayout类继承自GridItemLayoutManager,重写onMeasure和onLayout方法,动态计算每个子组件的位置与高度。通过自定义组件尺寸和排列逻辑,实现不等高内容的错位排列效果。
在HarmonyOS Next中,可以通过自定义FlexLayoutManager或GridLayoutManager来实现瀑布流效果。核心思路是自定义LayoutManager,在onLayoutChildren方法中计算每个子组件的位置。
具体实现步骤:
- 创建自定义
WaterfallLayoutManager继承自GridLayoutManager - 重写
onLayoutChildren方法,计算每列当前高度 - 遍历每个子组件,将其放置在最短的列中
- 根据子组件实际高度更新列高
关键代码示例:
class WaterfallLayoutManager extends GridLayoutManager {
private columnHeights: number[] = [];
private columnCount = 2; // 列数
onLayoutChildren(recycler: Recycler, state: State) {
// 初始化列高
this.columnHeights = new Array(this.columnCount).fill(0);
// 遍历所有子组件
for (let i = 0; i < getItemCount(); i++) {
// 找到最短列
const shortestColumn = this.findShortestColumn();
// 测量子组件
const child = recycler.getViewForPosition(i);
measureChildWithMargins(child);
// 计算位置
const left = shortestColumn * columnWidth;
const top = this.columnHeights[shortestColumn];
const right = left + getDecoratedMeasuredWidth(child);
const bottom = top + getDecoratedMeasuredHeight(child);
// 布局子组件
layoutDecorated(child, left, top, right, bottom);
// 更新列高
this.columnHeights[shortestColumn] += getDecoratedMeasuredHeight(child);
}
}
private findShortestColumn(): number {
return this.columnHeights.indexOf(Math.min(...this.columnHeights));
}
}
这种方法可以高效实现动态高度的瀑布流布局,支持任意列数和动态数据更新。

