HarmonyOS7 水流布局适合内容卡片流:别把瀑布流当九宫格用

前言
图片社区、笔记流、菜谱卡片这类页面,用普通两列 Grid 经常会出现大块空白。原因很简单:每张卡片高度不一样,硬凑整齐反而浪费屏幕。
HarmonyOS7 里可以用 WaterFlow 承载这种内容流。我的判断是:内容高度天然不一致,用户又是连续浏览,就可以考虑水流布局。如果每个入口都是同等权重,比如功能菜单、分类入口,那就不要用它。
为什么这个问题经常被写乱
水流布局适合内容卡片流 这类内容很容易被写成“代码能跑就算讲完了”,但对初学者来说,这恰恰是最不够的地方。真正让人卡住的,往往不是某个组件名记不住,而是不知道这段代码为什么要这样拆、状态为什么要这样放、以后需求变化时应该从哪里改。
所以这篇文章不只想给你一个能跑的例子,更想把背后的判断过程讲清楚。你只要把这个判断过程吃透,后面自己改页面、补需求、查问题时,心里会稳很多。
适用场景要先分清
WaterFlow 不是“更高级的 Grid”。它解决的是不等高内容的浏览效率。

| 场景 | 是否推荐 | 原因 |
|---|---|---|
| 图片笔记流 | 推荐 | 图片比例和文字长度不同 |
| 商品瀑布流 | 推荐 | 卡片信息密度差异明显 |
| 设置菜单 | 不推荐 | 条目应该稳定、可预测 |
| 分类入口 | 不推荐 | 用户需要快速扫固定位置 |

如果用户的目标是“找到一个固定入口”,瀑布流会让位置变得不稳定。如果用户的目标是“连续浏览内容”,水流布局才有价值。
瀑布流的重点在数据准备
瀑布流最容易翻车的地方不是 API,而是图片高度。图片真实高度、标题行数、作者信息、互动数都会影响卡片最终高度。
| 数据字段 | 用途 |
|---|---|
id |
作为稳定 key |
imageHeight |
提前占位,减少加载跳动 |
title |
限制最多两行 |
topic |
用于图片占位或标签展示 |
likes |
作为轻量互动信息 |
真实项目里,最好让服务端返回图片宽高,前端按比例换算显示高度。示例里直接使用 imageHeight,就是为了把“先有占位高度”这件事讲清楚。
ArkUI/ArkTS 完整示例
interface FeedCard44 {
id: number
title: string
author: string
imageHeight: number
likes: number
topic: string
}
@Entry
@Component
struct ContentWaterFlowPage {
@State selectedTitle: string = '还没有打开卡片'
private cards: FeedCard44[] = [
{ id: 1, title: '周末徒步路线记录', author: '阿宁', imageHeight: 156, likes: 82, topic: '户外' },
{ id: 2, title: '三步整理桌面小技巧', author: '林木', imageHeight: 110, likes: 41, topic: '整理' },
{ id: 3, title: '咖啡豆手冲参数', author: '小北', imageHeight: 186, likes: 128, topic: '咖啡' },
{ id: 4, title: 'HarmonyOS7 卡片布局练习', author: '程同学', imageHeight: 132, likes: 66, topic: '开发' }
]
@Builder
FeedCard(item: FeedCard44) {
Column({ space: 8 }) {
Stack() {
Text(item.topic)
.fontSize(14)
.fontColor('#777777')
}
.height(item.imageHeight)
.width('100%')
.backgroundColor('#DDE7F8')
.borderRadius(8)
Text(item.title)
.fontSize(15)
.fontWeight(FontWeight.Medium)
.maxLines(2)
.textOverflow({ overflow: TextOverflow.Ellipsis })
Row() {
Text(item.author).fontSize(12).fontColor('#777777')
Blank()
Text(`${item.likes} 赞`).fontSize(12).fontColor('#777777')
}
}
.padding(10)
.backgroundColor(Color.White)
.borderRadius(8)
.onClick(() => {
this.selectedTitle = item.title
})
}
build() {
Column({ space: 12 }) {
Text('内容流')
.fontSize(24)
.fontWeight(FontWeight.Bold)
.width('100%')
Text(this.selectedTitle)
.fontSize(13)
.fontColor('#0A59F7')
.width('100%')
WaterFlow() {
ForEach(this.cards, (item: FeedCard44) => {
FlowItem() {
this.FeedCard(item)
}
}, (item: FeedCard44) => item.id.toString())
}
.columnsTemplate('1fr 1fr')
.columnsGap(12)
.rowsGap(12)
.layoutWeight(1)
}
.padding(16)
.height('100%')
.backgroundColor('#F5F7FA')
}
}
把关键代码一段段拆开
imageHeight 提前进入模型。 即使真实项目使用网络图片,也尽量提前拿到宽高。否则图片加载前后高度变化,会让用户正在看的卡片跳走。
FlowItem 只包一张卡片。 水流布局已经负责排布,卡片内部不要再塞复杂 Grid。结构越重,后续越难处理点击、曝光和懒加载。
.columnsTemplate('1fr 1fr') 是列策略入口。 手机两列比较常见,平板可以扩展成三列或四列,但不要在内容卡片里写死宽度。
列数和卡片高度怎么定
| 设备/内容 | 推荐列数 | 说明 |
|---|---|---|
| 手机图片笔记 | 2 列 | 信息密度和可读性比较平衡 |
| 折叠屏展开态 | 3 列 | 内容更多,但文字仍要限行 |
| 平板横屏 | 3 到 4 列 | 需要配合最大内容宽度 |
| 纯文字卡片 | 不一定用 WaterFlow | 高度差异不大时普通列表更稳 |
卡片标题最好限制行数。瀑布流允许高度不同,但不代表单张卡片可以无限拉长。标题、图片、互动区都要有边界,浏览节奏才不会散。
常见坑
- 图片加载后才决定高度。 页面会跳动,用户正在看的内容可能被挤走。
- 标题不限制行数。 长标题会把单张卡片拉得过长,破坏流式节奏。
- 把瀑布流当功能入口。 入口类页面需要固定位置,不适合自然流。
- 卡片里嵌复杂交互。 收藏、关注、长按菜单都可以做,但触摸区域要分清,避免误触详情。
小结
水流布局适合“内容自己决定高度”的页面。HarmonyOS7 里写 WaterFlow 时,别先纠结样式,先把数据高度、稳定 key、点击反馈处理好,页面就不会散。
更多推荐



所有评论(0)