最近被一个 HarmonyOS 6 + ArkTS 的改版需求缠住了:首屏金刚区要从 4 列改成 6 列,另一个运营位要做固定两行的横向滚动入口。听起来就是 Grid 固定行列的小事,columnsTemplate、rowsTemplate 两个字符串一填,最多再管下间距,应该半小时能收工。真正上手以后才发现,网格出来了,但多出来的 item 去哪了?行高为什么不受控?明明设了固定列,数据一多整个容器又为什么开始纵向滚?这个周末我把 Grid 固定行列的规则彻底跑了一遍,把踩过的坑、验证过的结论和各种可直接复制的写法整理成一篇偏实操的记录,新手能照着抄,写过几版的老手也能确认一些容易忽略的边界行为。
1. Grid不是普通布局容器:先看清它“滚动表格”的本质
很多开发者一开始把 ArkTS 里的 Grid 当成 CSS Grid 或者传统 UI 里的宫格控件来用,所以一上来就问:为什么我设置了 4 列,第 5 个 GridItem 没有换行排到第二行,它跑哪去了?要回答这个问题,必须先接受一个事实:在 ArkUI 里,Grid 的身份不是普通布局容器,而是“带网格能力的滚动容器”,它和 List 属于同一个家族。List 是单维滚动列表,Grid 是二维滚动网格,两者底层都围绕滚动、复用、缓存这些机制来运转。这一点决定了你琢磨固定行列的时候,不能只想着格子怎么排,还要想滚动的方向、容器的高度、数据超出后如何处理。
1.1 只设置 columnsTemplate:行数交给数据自己长
先看最常见场景:固定列数,行数随数据增加,显示不下时容器滚动。代码只需要设置 columnsTemplate,不需要碰 rowsTemplate。
typescript复制Grid() {
ForEach(this.demoList, (item: string) => {
GridItem() {
Text(item)
.width('100%')
.height(80)
.textAlign(TextAlign.Center)
.backgroundColor('#F1F3F5')
}
}, (item: string) => item)
}
.columnsTemplate('1fr 1fr 1fr 1fr')
.columnsGap(12)
.rowsGap(12)
.width('100%')
.height('100%')
这个写法下,1fr 1fr 1fr 1fr 把横向可用宽度切成了 4 份,每个 GridItem 默认占用一个单元格。数据有 8 个,就渲染成两行;数据有 100 个,就继续往下排,容器高度不够时整体竖向滚动。也就是说,“固定列数”只是固定了模板方向,另一个维度是由 GridItem 数量和容器高度共同决定的动态维度。这也是需求改动中最常出现的行为:我要 4 列,只设置 columnsTemplate 就够了,不需要在 rowsTemplate 里写死到底生成多少行。
1.2 只设置 rowsTemplate:列数从右侧悄悄延伸
反过来,只设置 rowsTemplate 就是固定行数,列由数据在水平方向上扩展。这个场景适合做高度固定、横向滑动的两行入口,很多应用的“宫格第二屏”就是这种结构。
typescript复制Grid() {
ForEach(this.horizontalList, (item: string) => {
GridItem() {
Text(item)
.width(100)
.height(64)
.textAlign(TextAlign.Center)
.backgroundColor('#E8F3FF')
.borderRadius(8)
}
}, (item: string) => item)
}
.rowsTemplate('1fr 1fr')
.columnsGap(10)
.rowsGap(10)
.width('100%')
.height(140)
设置 rowsTemplate: '1fr 1fr' 表示整个 Grid 的内容区域严格按两行来组织,所以 GridItem 会先填满第一列的两个位置,然后继续在右侧生成新列。数据多了以后,内容会超出屏幕宽度,此时 Grid 在水平方向变成可滚动状态。注意这里的高度我给了一个具体值:外层如果没有明确高度,Grid 拿不到确定的行高分配依据,布局表现就会变得奇怪。
1.3 两个模板都设置:二维单元格数量会被锁死
同时设置 columnsTemplate 和 rowsTemplate,这大概是需求文档里最字面意义上的“固定行列”。比如 2×2 宫格、3×3 宫格,模板就限定了 Grid 内部最多能显示 rows × columns 个单元格。
我在设备上验证时发现一个新手最容易懵的现象:如果同时设置了 rowsTemplate: '1fr 1fr' 和 columnsTemplate: '1fr 1fr',循环里却给 Grid 塞了 6 个 GridItem,最终渲染出来的往往不是正常的 3 行 2 列,也不是前 4 个保留后面隐藏,而是多出来的子项挤在同一个布局体系里导致行为不可预测,有的版本里后续子项会直接不显示,有的会把单元格的行列关系打乱。所以做这种固定棋盘式布局时,子项数量必须和模板行列数严格对应,不要指望 Grid 像 FlowLayout 那样自动换行并扩展隐式网格。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. columnsTemplate与rowsTemplate字段写法:fr单位、空格与固定尺寸怎么组合
固定行列的第二个大坑是模板字符串本身。columnsTemplate 和 rowsTemplate 接收的是一个字符模板,不是数组,不是对象,也不是逗号分隔的字符串,而是以空格分隔的一串轨道定义。我在代码评审里见过有人把 CSS 里的写法直接搬过来写 '1fr, 1fr, 1fr',结果运行时不报错但列数完全是乱的,调试了半天才发现是分隔符问题。
2.1 模板字符串的组成规则
模板里每一段代表一列(或者一行),常用的有几种:
1fr:按比例分配剩余空间。三个1fr就是三等分。- 固定数值 + 单位:比如
120vp、`100p
