【共创季稿事节】鸿蒙ArkTS之GridItem跨列布局深度解析
鸿蒙 ArkTS 布局精粹:GridItem 跨列布局(columnStart / columnEnd)深度解析
适用版本:HarmonyOS NEXT API 24(6.2.0)
核心组件:Grid / GridItem / columnStart / columnEnd



一、引言
在 HarmonyOS NEXT 应用开发中,布局是一切 UI 表达的根基。ArkTS 作为鸿蒙原生声明式开发语言,提供了丰富而强大的布局容器,其中 Grid(网格布局) 是最常用的二维布局方案之一。
Grid 布局的核心魅力在于 规则中蕴藏变化——当所有 GridItem 整齐排列时,它是规整的表格;当你通过 columnStart / columnEnd 让某个单元格跨越多列时,它就能演化出灵活多样的视觉层次。这种「合并单元格」的能力,在仪表盘面板、商品展示网格、日历组件、数据报表等场景中几乎不可或缺。
本文将从一个完整的可运行示例出发,深入剖析 GridItem 跨列布局的实现原理、API 语义和实战要点。全文配套的示例代码已在 API 24 环境下编译通过,你可以直接运行观察效果。
二、Grid 布局基础回顾
2.1 什么是 Grid
Grid 是一个二维布局容器,它将可用空间划分为 行(Row) 和 列(Column) 的矩阵。开发者通过 columnsTemplate 和 rowsTemplate 两个属性定义网格的骨架,然后将子组件(GridItem)放入其中,组件会自动按文档流顺序在网格中排列。
Grid() {
// GridItem 放入此处
}
.columnsTemplate('1fr 1fr 1fr 1fr') // 4 列等宽
.rowsTemplate('80vp 80vp 80vp') // 3 行固定高度
.columnsGap(8) // 列间距
.rowsGap(8) // 行间距
2.2 Template 语法详解
columnsTemplate 和 rowsTemplate 接受一个字符串,用空格分隔每个轨道(Track)的尺寸。支持的尺寸单位包括:
| 单位 | 含义 | 示例 |
|---|---|---|
px |
物理像素 | 100px |
vp |
虚拟像素(推荐) | 80vp |
% |
父容器百分比 | 25% |
fr |
弹性剩余空间分配 | 1fr |
auto |
内容自适应 | auto |
fr 是最常用的弹性单位——它将可用空间按比例分配给各列/行。例如 '1fr 2fr 1fr' 表示把水平空间分成 4 份,第 1 列占 1/4,第 2 列占 1/2,第 3 列占 1/4。
2.3 GridItem 的默认行为
默认情况下,每个 GridItem 占据 1 列 1 行,按从上到下、从左到右的文档流顺序依次填充。这就像 HTML 中的 float: left 效果——多个尺寸一致的块级元素自动排成整齐的网格。
三、columnStart / columnEnd 跨列机制
3.1 核心 API
跨列布局的关键在于 GridItem 的两个属性方法:
GridItem()
.columnStart(startIndex: number) // 起始列索引,从 0 开始
.columnEnd(endIndex: number) // 结束列索引,半开区间 [start, end)
语义解读:
columnStart指定该单元格从第几列开始(包含)columnEnd指定该单元格到第几列结束(不包含)- 区间表示为
[columnStart, columnEnd),这是一个左闭右开区间
举例来说,columnStart(0).columnEnd(3) 表示跨列范围为列 0、列 1、列 2,共 3 列。
3.2 为什么设计为半开区间
半开区间 [start, end) 是计算机科学中非常经典的设计,鸿蒙选择这种设计有以下考量:
- 区间长度计算直观:
end - start = 跨列数,例如colEnd=4 - colStart=0 = 4列 - 相邻区间无缝拼接:区间A
[0, 2)+ 区间B[2, 4)恰好覆盖[0, 4)无重叠无缝隙 - 与编程习惯一致:数组切片、循环区间等均采用此约定,降低学习成本
3.3 不设置跨列时的行为
如果 GridItem 不调用 columnStart() 和 columnEnd(),它会按照文档流自动排列到下一个可用位置,占据 1 列。这与设置 columnStart 但跳过某些列不同——跳过列会导致空白区域,而自动排列则始终填充下一个空位。
3.4 跨行类比(rowStart / rowEnd)
作为补充,GridItem 同样支持 行跨越 的对应属性:
GridItem()
.rowStart(startIndex: number) // 起始行索引
.rowEnd(endIndex: number) // 结束行索引(不包含)
行列跨可以组合使用,形成一个矩形区域跨越任意行列范围。
四、实战示例:完整代码解析
4.1 示例概述
我们构建了一个 4 列 × 4 行的网格演示页,展示了多种跨列组合:
第1行:ItemA [0-2) | ItemB [2-4) ← 两个 2 列宽并排
第2行:ItemC [0-4) ← 横跨全部 4 列(全宽横幅)
第3行:ItemD [0-1) | ItemE [1-3) | ItemF [3-4) ← 1+2+1 混合
第4行:ItemG [0-3) | ItemH [3-4) ← 3 列宽 + 1 列宽
每个单元格用不同背景色区分,并标注了其 colStart / colEnd 配置值。
4.2 数据模型设计
@ObservedV2
class GridItemInfo {
label: string = ''; // 显示文本
color: string = '#FFFFFF'; // 背景色
colStart?: number; // 起始列(可选)
colEnd?: number; // 结束列(可选)
}
这里将 colStart 和 colEnd 设计为可选参数(?:),当未传入时 GridItem 自动按文档流排列——这与实际使用场景高度一致。
4.3 核心 Grid 布局代码
Grid() {
ForEach(this.gridItems, (item: GridItemInfo) => {
GridItem() {
Column() {
Text(item.label)
.fontSize(13)
.fontWeight(FontWeight.Medium)
.fontColor('#333333')
.textAlign(TextAlign.Center)
.width('100%')
.lineHeight(20);
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
.alignItems(HorizontalAlign.Center)
.backgroundColor(item.color)
.borderRadius(6)
.border({ width: 1, color: '#CCCCCC' })
}
.columnStart(item.colStart) // ★ 关键:设置起始列
.columnEnd(item.colEnd) // ★ 关键:设置结束列
},
(item: GridItemInfo, index?: number): string => {
return item.label + (index !== undefined ? index : 0);
})
}
.columnsTemplate('1fr 1fr 1fr 1fr')
.rowsTemplate('80vp 80vp 80vp 80vp')
.columnsGap(8)
.rowsGap(8)
.width('100%')
.backgroundColor('#F5F5F5')
.borderRadius(8)
.padding(8)
4.4 关键技术细节
1. ForEach 的 keyGenerator
ForEach 的第三个参数是 keyGenerator,用于为每个列表项生成稳定的唯一标识。这能帮助框架在数据变更时精确追踪每个节点,避免不必要的重复渲染。我们的实现是:
(item: GridItemInfo, index?: number): string => {
return item.label + (index !== undefined ? index : 0);
}
⚠ 注意:在 API 24 的 ArkTS 中,
??(nullish coalescing)运算符尚未支持,需要改用三元表达式?:。
2. Builder 封装图例
使用 @Builder 装饰器封装了图例组件,使代码更模块化:
@Builder
legendItem(color: string, label: string) {
Row() {
Column().width(16).height(16).backgroundColor(color).borderRadius(4);
Text(label).fontSize(13).fontColor('#555555');
}
}
3. 全局组件无需导入
在 ArkTS 中,Grid、GridItem、Text、Column、Row 以及 FlexAlign、FontWeight、TextAlign、HorizontalAlign 等枚举都是 内置全局 API,不需要也不能从 @kit.ArkUI 导入。这是初学者最容易犯的错误之一。
五、运行效果与视觉解读
当应用启动后,你将看到以下布局效果:
第1行:对称分割
两个红色(#FF6B6B)和青色(#4ECDC4)的色块各占 2 列,各显示其 colStart / colEnd 配置值。视觉上呈 50% : 50% 的分割。
第2行:全宽横幅
一个亮黄色(#FFE66D)的色条横跨全部 4 列,适合用作分类标题、广告横幅或状态提示条。在实际应用中,这通常用于分隔不同区块。
第3行:非对称布局
三个色块形成 1:2:1 的比例——左侧绿色(#95E1D3)占 1 列,中间粉色(#F38181)占 2 列,右侧紫色(#AA96DA)占 1 列。这种布局常见于仪表盘中的「中间突出,两侧辅助」的视觉结构。
第4行:主次分明
浅蓝色(#A8D8EA)区域占 3 列,右侧粉色(#FCBAD3)占 1 列。这种宽窄搭配适合展示主要内容 + 侧栏信息的场景。
页面底部还附带了文字结构解析和 API 使用提示,方便对照学习。
六、实际应用场景
6.1 商品展示网格
在电商应用中,往往需要突出展示某个商品(主推款、折扣商品)。通过跨列布局,可以让主推商品占据 2 列,普通商品占 1 列:
[ 主推商品 ][ 普通 ][ 普通 ]
[ 普通 ][ 普通 ][ 主推商品 ]
6.2 仪表盘面板
数据看板通常需要将不同重要程度的信息用不同的视觉权重展示:
[ 实时销售额 ][ 订单数 ]
[ 访问量 ][ 转化率 ][ 客单价 ]
6.3 日历组件
日历中日期占据固定网格,但跨月或特殊事件提示条可以跨列显示:
[ 1 ][ 2 ][ 3 ][ 4 ][ 5 ][ 6 ][ 7 ]
[ 8 ][ 9 ][ 休假通知 ][ 14 ]
6.4 表单布局
动态表单中,标签 + 输入框的组合有时需要根据内容长度调整宽度,跨列能力让布局更加灵活。
七、常见问题与避坑指南
7.1 跨列总和超过总列数
如果同一行中各 GridItem 的 columnEnd 之和超过了 columnsTemplate 定义的总列数,会导致布局溢出或自动换行。务必确保每行的列区间分配与总列数一致。
正确示例(4列网格):
Row 1: [0-2) + [2-4) = 覆盖 0,1,2,3 → 4列 ✓
Row 2: [0-4) = 覆盖 0,1,2,3 → 4列 ✓
7.2 跨列区间重叠
如果两个 GridItem 的 [colStart, colEnd) 区间在同一行中有重叠,后一个会覆盖前一个的显示区域。需要仔细规划每个单元格的列区间,确保它们互不重叠且连续。
7.3 columnStart 跳过列导致空白
如果某行的列区间不连续(例如 [0-1) 和 [3-4),跳过了列 2),则跳过的列会显示为空白区域。这有时是故意的设计(留白),但通常需要配合其他视觉元素填充。
7.4 动态数据中的跨列
当 GridItem 的数据来自动态列表时,需要确保跨列参数在运行时计算正确。建议在数据模型层面明确存储 colStart/colEnd,避免在渲染时动态计算以免出现逻辑错误。
7.5 与 rowStart / rowEnd 的配合
columnStart/columnEnd 控制水平跨度,rowStart/rowEnd 控制垂直跨度。两者可以独立或组合使用——例如 columnStart(0).columnEnd(3).rowStart(0).rowEnd(2) 会让 GridItem 跨越 3 列 2 行,形成一个 3×2 的大单元格。
八、性能优化建议
8.1 合理使用 ForEach 的 keyGenerator
当网格数据频繁更新时,提供稳定的 keyGenerator 能让框架精确追踪节点变化,避免全部重建:
ForEach(
this.gridItems,
(item) => { GridItem() { /* ... */ } },
(item) => item.id // 用稳定且唯一的 ID
)
8.2 避免过度嵌套
GridItem 内部尽量保持扁平结构。如果需要复杂布局,优先使用 Column/Row 组合,避免多层 Grid 嵌套导致测量开销成倍增长。
8.3 使用 LazyForEach 替代 ForEach
对于超长列表(数百个以上 GridItem),推荐使用 LazyForEach 实现按需渲染,大幅降低首屏加载时间和内存占用:
Grid() {
LazyForEach(this.dataSource, (item: ItemType) => {
GridItem() { /* ... */ }
.columnStart(item.colStart)
.columnEnd(item.colEnd)
})
}
九、总结
GridItem 的 columnStart / columnEnd 跨列能力是 HarmonyOS NEXT 网格布局中非常实用但又容易被忽视的特性。它的设计哲学可以概括为三个关键词:
- 精确:通过半开区间
[start, end)精确定义跨列范围 - 灵活:支持任意粒度的跨列组合,从 1 列到全部
- 可预测:文档流自动排列 + 显式跨列设置的行为清晰明确
结合 rowStart / rowEnd 的行跨能力,Grid 可以构建出从简单表格到复杂仪表盘的各类布局。在 API 24 版本中,布局引擎的稳定性和性能进一步提升,让开发者可以放心地将其应用于生产环境。
希望本文的示例和解析能帮助你更好地掌握 GridItem 跨列布局的技巧。如果你在实际开发中遇到布局相关的疑难问题,欢迎留言讨论。
附录:完整源码索引
项目位置:entry/src/main/ets/pages/Index.ets
文件结构:
entry/
├── src/
│ ├── main/
│ │ ├── ets/
│ │ │ └── pages/
│ │ │ └── Index.ets ← 本文完整示例(入口页)
│ │ ├── module.json5 ← 页面路由配置
│ │ └── resources/
│ └── ohosTest/
├── build-profile.json5
└── hvigor/
路由配置(main_pages.json):
{
"src": [
"pages/Index"
]
}
作者:AtomCode
版本:HarmonyOS NEXT API 24(6.2.0)
许可:本文档遵循 CC BY-NC 4.0 协议,转载请注明出处
更多推荐



所有评论(0)