加入下方官方班级,得鸿蒙礼盒
本期活动时间:2025年8月1日-12月31日
问题现象
在开发过程中,Grid布局是构建页面结构的强大工具。特别是在复杂界面设计中,开发者常需利用Grid布局实现单元格的灵活合并,以确保界面元素的精准对齐和响应式调整,从而提升用户体验。
效果预览

背景知识
- 创建网格 (Grid/GridItem):网格布局是由“行”和“列”分割的单元格所组成,通过指定“项目”所在的单元格做出各种各样的布局。网格布局具有较强的页面均分能力和子组件占比控制能力,是一种重要自适应布局,其使用场景有九宫格图片展示、日历、计算器等。
- GridItem:网格容器中单项内容容器,具有若干设置容器的属性。
- 设置子组件所占行列数:在Grid组件中,可以通过创建Grid时传入合适的GridLayoutOptions实现如图所示的单个网格横跨多行或多列的场景。
解决方案
方案一:通过GridItem的行列跨度属性合并单元格。
- 适用场景。
场景
举例说明
简单跨行/跨列
适用于需要手动控制少量单元格合并的场景,例如:
- 表格标题栏跨多列(如合并首行前3列)。
- 九宫格首项占据整行(如电商首页Banner)。
- 日历中的周末日期合并(如周六、周日跨列显示)。
固定布局需求
当布局结构固定且无需频繁调整时,直接设置行列号更直观,例如:
- 固定导航栏按钮排列。
- 商品分类标签的固定组合。
- 实现方式。
从背景知识中可知,若要实现问题现象中的预期效果,可以对GridItem进行设置。GridItem有属性如下:
GridItem的属性
说明
rowStart
设置当前元素起始行号。
rowEnd
设置当前元素终点行号。
columnStart
设置当前元素起始列号。
columnEnd
设置当前元素终点列号。
对于预期效果图进行分析:

这是一个五行四列的网格布局,其中编号1、2、3的网格占了整行(合并列),它们的元素起始列号为0,元素终点列号为3,如果设置了rowStart/rowEnd/columnStart/columnEnd,GridItem会占据指定的行数(rowEnd-rowStart+1)或列数(columnEnd-columnStart+1)。
编号9的网格元素起始列号为1,元素终点列号为3,但若将其列号设置的与1、2、3号网格一样,则9号会和8号产生位置冲突。在设置行列号布局时,若该区域位置存在GridItem布局,则会产生避让,因此9号按照1、2、3号网格进行相同设置,同样能实现预期效果。
interface listItem { type: string value: string } @Entry @Component struct Index { @State text: string = 'drag' @State titleIndex: number = 0 @State numbers: listItem[] = [{ type: 'title', value: '1' }, { type: 'title', value: '2' }, { type: 'title', value: '3' }, { type: 'item', value: '4' }, { type: 'item', value: '5' }, { type: 'item', value: '6' }, { type: 'item', value: '7' }, { type: 'item', value: '8' }, { type: 'title', value: '9' },] build() { Column({ space: 4 }) { Grid() { ForEach(this.numbers, (item: listItem) => { GridItem() { Text(item.value) .fontSize(16) .backgroundColor(0x0A59F7) .width('100%') .height('100%') .textAlign(TextAlign.Center) } .columnStart(item.type == 'title' ? 0 : undefined) .columnEnd(item.type == 'title' ? 3 : undefined) .height('100%') .width('100%') }, (item: listItem, index: number) => item.value) } .columnsTemplate('1fr 1fr 1fr 1fr') .rowsTemplate('1fr 1fr 1fr 1fr 1fr') .columnsGap(10) .rowsGap(10) .width('90%') .backgroundColor(0xFAEEE0) .height(300) .animation({ duration: 300, curve: Curve.Smooth }) } .width('100%') .margin({ top: 5 }) } }
方案二:通过layoutOptions配置不规则布局。
- 适用场景。
场景
举例说明
复杂不规则布局场景
适用于需要稍微复杂的计算单元格尺寸或位置的场景。例如:
- 计算器按键布局(如“0”键跨2列,“=”键跨2行)。
- 促销广告栏横向跨多列(如“买一送一”横跨3列)。
- 可滚动的跨行跨列场景。
频繁合并单元格场景
当网格项数量较多时,layoutOptions通过预计算布局减少渲染开销,避免因频繁操作行列号导致的性能问题。
- 实现方式。
制作不均匀的网格布局也可以通过onGetRectByIndex返回的rowStart、columnStart、rowSpan、columnSpan来实现跨行跨列布局。
onGetRectByIndex的属性
说明
rowStart
当前元素起始行号。
columnStart
当前元素起始列号。
rowSpan
指定当前元素的占用行数。
columnSpan
指定当前元素的占用列数。
对于预期效果图进行分析:

为了实现预期效果,可以对网格进行起始位置以及占用列数的设置,如编号1号的网格起始行号为0,起始列号也为0,占用行数为1,占用列数为4;编号9号的网格起始行号为4,起始列号为1,占用行数为1,占用列数为3。其余网格依次推理,在此不再赘述。更多示例详见:固定行列Grid。
layoutOptions: GridLayoutOptions = { regularSize: [1, 1], onGetRectByIndex: (index: number) => { if (index == 0) { return [0, 0, 1, 4] } else if (index == 8) { return [1, 4, 1, 3] } else { // 布局其他item的位置 // ... } } } Grid(undefined, this.layoutOptions) { // ... } .columnsTemplate('1fr 1fr 1fr 1fr') .rowsTemplate('1fr 1fr 1fr 1fr 1fr')
总结
在HarmonyOS的Grid布局中,合并单元格的两种方法(行列跨度属性和layoutOptions配置)各有其适用场景和优缺点。以下是详细对比及场景建议:
| 维度 | 行列跨度属性(rowStart/rowEnd等) | layoutOptions配置 |
|---|---|---|
| 实现方式 | 手动指定单元格的起始和结束行列号。 | 通过配置对象动态定义常规项与不规则项的尺寸和位置。 |
| 代码复杂度 | 简单直观,适合少量单元格操作。 | 需理解配置逻辑,适合复杂布局。 |
| 动态响应 | 需手动更新行列号,动态场景维护成本高。 | 支持响应式断点,自动适配屏幕变化。 |
| 性能表现 | 大量单元格时可能影响渲染效率。 | 通过预计算优化布局,性能更优。 |
| 适用布局类型 | 规则网格中的局部合并(如标题栏、首图)。 | 高度不规则布局(如计算器按键、促销广告位)。 |
658

被折叠的 条评论
为什么被折叠?



