鸿蒙学习实战之路-Grid 网格布局组件全攻略
Grid&GridItem 组件真的是鸿蒙布局中的"瑞士军刀",无论是规则的网格布局还是复杂的不规则布局,都能轻松应对!
鸿蒙学习实战之路-Grid 网格布局组件全攻略
最近在写鸿蒙页面时,发现好多布局用传统的 Flex 和 Column/Row 组合起来特别麻烦,尤其是那种网格状的布局,比如淘宝首页的功能入口、小米有品的分类导航,简直想抠脑壳 o(╯□╰)o
今天这篇,我就手把手带你搞定 Grid&GridItem 网格布局组件,从基础到进阶,保证你看完就能上手!
一、Grid 是什么?
Grid 组件就像是一个布局神器,专门用来创建网格状的 UI 界面。简单来说,就是把屏幕分成若干行和列,然后把内容放在这些格子里。
举个例子,你看淘宝首页的这些功能入口,是不是整整齐齐的网格?用 Grid 实现简直不要太轻松!

🥦 西兰花小贴士
Grid 和 Flex 的区别:Flex 是一维布局(要么行要么列),Grid 是二维布局(同时控制行和列)。就像搭积木,Flex 是一列一列摆,Grid 是一块一块铺!
二、Grid 基础用法
2.1 固定行列布局
最基础的 Grid 用法就是创建固定行列数的网格,比如 3 行 2 列的布局。
组件结构
Grid() {
GridItem() {
// 展示的内容放在这里
Text('1')
.fontColor(Color.White)
.fontSize(30)
}
.backgroundColor(Color.Blue)
GridItem() {
Text('2')
.fontColor(Color.White)
.fontSize(30)
}
.backgroundColor(Color.Blue)
}
🥦 西兰花警告:
- Grid 的子组件必须是 GridItem,不能直接放其他组件!
- GridItem 只能有一个子组件,要放多个内容记得用容器包起来!
- Grid 如果不设置宽高,会默认继承父组件的尺寸。
基础属性
| 名称 | 参数类型 | 描述 |
|---|---|---|
| columnsTemplate | string | 设置列数和宽度比例,如’1fr 1fr 2fr’表示 3 列,宽度比例 1:1:2 |
| rowsTemplate | string | 设置行数和高度比例,如’1fr 1fr’表示 2 行,高度各占一半 |
| columnsGap | Length | 设置列间距,默认 0 |
| rowsGap | Length | 设置行间距,默认 0 |
实战练习
我们来创建一个 3 列 2 行的网格,列宽比例 1:2:1,行高各占一半,带 10px 间距:
@Entry
@Component
struct GridBasicExample {
build() {
Column() {
Text('固定行列布局')
.fontSize(20)
.fontWeight(900)
.padding(10)
Grid() {
// 第1行
GridItem() {
Text('1')
.fontColor(Color.White)
.fontSize(30)
}
.backgroundColor(Color.Blue)
GridItem() {
Text('2')
.fontColor(Color.White)
.fontSize(30)
}
.backgroundColor(Color.Blue)
GridItem() {
Text('3')
.fontColor(Color.White)
.fontSize(30)
}
.backgroundColor(Color.Blue)
// 第2行
GridItem() {
Text('4')
.fontColor(Color.White)
.fontSize(30)
}
.backgroundColor(Color.Blue)
GridItem() {
Text('5')
.fontColor(Color.White)
.fontSize(30)
}
.backgroundColor(Color.Blue)
GridItem() {
Text('6')
.fontColor(Color.White)
.fontSize(30)
}
.backgroundColor(Color.Blue)
}
.border({ width: 1 })
.columnsTemplate('1fr 2fr 1fr')
.rowsTemplate('1fr 1fr')
.width('100%')
.height(360)
.columnsGap(10)
.rowsGap(10)
}
.width('100%')
.height('100%')
}
}
三、实战案例:淘宝二楼效果
现在咱们来整个实战案例,实现淘宝二楼的功能入口布局!
注代码中的图片可以自行替换成 其他图片,保证代码逻辑正确即可
效果展示

需求分析
- 实现 5x5 的网格布局
- 每个网格包含图标和文字
- 整体使用渐变色背景
参考代码
interface TaoBaoItemContent {
title: string
icon: ResourceStr // $r('图片名')返回的是Resource类型,ResourceStr是联合类型Resource|string
}
@Entry
@Component
struct TaoBaoSecondFloor {
contentList: TaoBaoItemContent[] = [
{ title: '淘金币', icon: $r('app.media.ic_taobao_01') },
{ title: '一起摇现金', icon: $r('app.media.ic_taobao_02') },
{ title: '闲鱼', icon: $r('app.media.ic_taobao_03') },
{ title: '中通快递', icon: $r('app.media.ic_taobao_04') },
{ title: '芭芭农场', icon: $r('app.media.ic_taobao_05') },
{ title: '淘宝珍库', icon: $r('app.media.ic_taobao_06') },
{ title: '阿里拍卖', icon: $r('app.media.ic_taobao_07') },
{ title: '阿里药房', icon: $r('app.media.ic_taobao_08') },
{ title: '小黑盒', icon: $r('app.media.ic_taobao_09') },
{ title: '菜鸟', icon: $r('app.media.ic_taobao_10') },
{ title: 'U先试用', icon: $r('app.media.ic_taobao_11') },
{ title: '有好价', icon: $r('app.media.ic_taobao_12') },
{ title: '极有家', icon: $r('app.media.ic_taobao_13') },
{ title: '天猫榜单', icon: $r('app.media.ic_taobao_14') },
{ title: '天天特卖', icon: $r('app.media.ic_taobao_15') },
{ title: '每日好店', icon: $r('app.media.ic_taobao_16') },
{ title: '全球购', icon: $r('app.media.ic_taobao_17') },
{ title: '我的爱车', icon: $r('app.media.ic_taobao_18') },
{ title: '造点新货', icon: $r('app.media.ic_taobao_19') },
{ title: '首单优惠', icon: $r('app.media.ic_taobao_20') },
{ title: '潮Woo', icon: $r('app.media.ic_taobao_21') },
{ title: '亲宝贝', icon: $r('app.media.ic_taobao_22') },
{ title: '领券中心', icon: $r('app.media.ic_taobao_23') },
{ title: '天猫奢品', icon: $r('app.media.ic_taobao_24') },
{ title: 'iFashion', icon: $r('app.media.ic_taobao_25') }
]
build() {
Column() {
Column() {
// 顶部返回区域
this.backBuilder()
// 搜索框区域
this.searchBuilder()
// Grid区域
this.gridBuilder()
}
}
.width('100%')
.height('100%')
.linearGradient({
colors: [
['#271b41', 0],
['#481736', 1],
]
})
}
@Builder
backBuilder() {
// 顶部返回区域
Row() {
Image($r('app.media.ic_taobao_back'))
.fillColor(Color.White)
.width(30)
Text('最近使用')
.fontSize(20)
.fontColor(Color.White)
}
.width('100%')
.padding({ top: 40 })
}
@Builder
searchBuilder() {
// 搜索框区域
Stack() {
Text()
.width('100%')
.height(40)
.backgroundColor(Color.White)
.opacity(.3)
.borderRadius(20)
Row({ space: 10 }) {
Image($r('app.media.ic_taobao_search'))
.width(25)
.fillColor(Color.White)
Text('搜索')
.fontSize(15)
.fontColor(Color.White)
}
.padding({ left: 10 })
.width('100%')
}
.padding(10)
}
@Builder
gridBuilder() {
// Grid区域
Grid() {
ForEach(this.contentList, (item: TaoBaoItemContent, index: number) => {
GridItem() {
Column({ space: 10 }) {
Image(item.icon)
.width(40)
Text(item.title)
.fontColor(Color.White)
.fontSize(14)
}
}
})
}
.columnsTemplate('1fr 1fr 1fr 1fr 1fr')
.rowsTemplate('1fr 1fr 1fr 1fr 1fr')
.width('100%')
.height(360)
}
}
🥦 西兰花小贴士
这里用了@Builder 装饰器把布局拆分成几个小模块,代码看起来更清晰!这就像是把一个大蛋糕切成小块,吃起来更方便~
四、合并行列(不规则网格)
有时候咱们需要实现不规则的网格布局,比如有的格子跨两行,有的跨两列。这时候就需要用到 GridItem 的合并属性了!
合并属性
| 名称 | 参数类型 | 描述 |
|---|---|---|
| rowStart | number | 指定当前元素起始行号 |
| rowEnd | number | 指定当前元素终点行号 |
| columnStart | number | 指定当前元素起始列号 |
| columnEnd | number | 指定当前元素终点列号 |
实战练习:实现不规则网格
我们来把一个 4 列 3 行的规则网格改造成不规则布局:
@Entry
@Component
struct GridMergeExample {
// 快速生成12个元素的数组
nums: number[] = Array.from({ length: 12 })
build() {
Column() {
Text('合并行列')
.fontSize(20)
.fontWeight(900)
.padding(10)
Grid() {
ForEach(this.nums, (item: number, index: number) => {
if (index === 2) {
GridItem() {
Text(index + '')
.fontColor(Color.White)
.fontSize(30)
}
.backgroundColor('#9dc3e6')
.columnStart(3)
.columnEnd(4)
} else if (index === 3) {
GridItem() {
Text(index + '')
.fontColor(Color.White)
.fontSize(30)
}
.backgroundColor('#9dc3e6')
.rowStart(2)
.rowEnd(3)
} else {
GridItem() {
Text(index + '')
.fontColor(Color.White)
.fontSize(30)
}
.backgroundColor('#9dc3e6')
}
})
}
.columnsTemplate('1fr 1fr 1fr 1fr')
.rowsTemplate('1fr 1fr 1fr')
.width('100%')
.height(260)
.rowsGap(10)
.columnsGap(10)
.padding(10)
}
.width('100%')
.height('100%')
}
}
🥦 西兰花小贴士
快速生成指定长度的数组小技巧:
Array.from({ length: 12 }),想要多长就把 length 设为多少!
五、滚动网格
在实际开发中,经常会遇到内容超出屏幕的情况,这时候就需要用到滚动网格了。
设置滚动方向
- 水平滚动:设置
rowsTemplate,Grid 的滚动方向为水平方向 - 垂直滚动:设置
columnsTemplate,Grid 的滚动方向为垂直方向
实战练习:垂直滚动网格
// 为Text扩展属性newExtend
@Extend(Text)
function newExtend() {
.width('100%')
.height('100%')
.fontSize(30)
.fontColor(Color.White)
.textAlign(TextAlign.Center)
}
@Entry
@Component
struct GridScrollExample {
// 生成30个元素的数组
list: string[] = Array.from({ length: 30 })
build() {
Column() {
Text('垂直滚动网格')
.fontSize(20)
.fontWeight(900)
.padding(10)
Grid() {
ForEach(this.list, (item: string, index) => {
GridItem() {
Text((index + 1).toString())
.newExtend()
}
.padding(5)
.backgroundColor('#0094ff')
.height('30%') // 竖向滚动-通过height设置高度
})
}
.columnsTemplate('1fr 1fr 1fr') // 竖向滚动 固定列数
.rowsGap(10)
.columnsGap(10)
.width('100%')
.height(300)
.border({ width: 1 })
.padding(5)
}
.width('100%')
.height('100%')
}
}
六、实战案例:小米有品横向滚动导航
咱们来实现小米有品的横向滚动导航效果:
注代码中的图片可以自行替换成 其他图片,保证代码逻辑正确即可
效果展示

参考代码
interface NavItem {
title: string
icon: ResourceStr // 联合属性 Resource | string
}
@Entry
@Component
struct XiaomiYoupinNav {
// 数据
navList: NavItem[] = [
{ title: '上新精选', icon: $r('app.media.ic_xiaomi_nav_01') },
{ title: '智能家电', icon: $r('app.media.ic_xiaomi_nav_02') },
{ title: '小米众筹', icon: $r('app.media.ic_xiaomi_nav_03') },
{ title: '有品会员', icon: $r('app.media.ic_xiaomi_nav_04') },
{ title: '有品秒杀', icon: $r('app.media.ic_xiaomi_nav_05') },
{ title: '原产地', icon: $r('app.media.ic_xiaomi_nav_06') },
{ title: '生活优选', icon: $r('app.media.ic_xiaomi_nav_07') },
{ title: '手机', icon: $r('app.media.ic_xiaomi_nav_08') },
{ title: '小米自营', icon: $r('app.media.ic_xiaomi_nav_09') },
{ title: '茅台酒饮', icon: $r('app.media.ic_xiaomi_nav_10') },
{ title: '鞋服饰品', icon: $r('app.media.ic_xiaomi_nav_11') },
{ title: '家纺餐厨', icon: $r('app.media.ic_xiaomi_nav_12') },
{ title: '食品生鲜', icon: $r('app.media.ic_xiaomi_nav_13') },
{ title: '好惠买', icon: $r('app.media.ic_xiaomi_nav_14') },
{ title: '家具家装', icon: $r('app.media.ic_xiaomi_nav_15') },
{ title: '健康养生', icon: $r('app.media.ic_xiaomi_nav_16') },
{ title: '有品海购', icon: $r('app.media.ic_xiaomi_nav_17') },
{ title: '个护清洁', icon: $r('app.media.ic_xiaomi_nav_18') },
{ title: '户外运动', icon: $r('app.media.ic_xiaomi_nav_19') },
{ title: '3C数码', icon: $r('app.media.ic_xiaomi_nav_20') }
]
build() {
Column() {
Text('小米有品')
.fontSize(20)
.fontWeight(900)
.padding(10)
Grid() {
ForEach(this.navList, (item: NavItem) => {
GridItem() {
Column() {
Image(item.icon)
.width('80%')
Text(item.title)
.fontSize(12)
}
.height('100%')
}
.width('20%')
})
}
.rowsTemplate('1fr 1fr')
.height(160)
.width('100%')
.backgroundColor(Color.White)
.borderRadius(5)
.padding({ bottom: 10 })
.scrollBar(BarState.Off) // 关闭滚动条
}
.width('100%')
.height('100%')
.padding(10)
.backgroundColor('#f5f5f5')
}
}
七、代码控制滚动
有时候我们需要通过代码来控制 Grid 的滚动,比如实现上一页/下一页的按钮。这时候就需要用到 Scroller 控制器了!
核心步骤
- 创建 Scroller 对象
- 设置给 Grid
- 调用 Scroller 对象的 scrollPage 方法
参考代码
@Entry
@Component
struct GridControllerExample {
nums: number[] = Array.from({ length: 200 })
// 控制器对象,不是状态属性,不需要添加任何修饰符
scroller: Scroller = new Scroller()
build() {
Column() {
Text('控制器-代码控制滚动')
.fontSize(20)
.fontWeight(900)
.padding(10)
Grid(this.scroller) {
ForEach(this.nums, (item: number, index: number) => {
GridItem() {
Text(index + 1 + '')
.fontColor(Color.White)
.fontSize(20)
.width('100%')
.height('100%')
.textAlign(TextAlign.Center)
}
.backgroundColor('#0094ff')
.width('25%')
})
}
.padding(10)
.height(450)
.rowsGap(10)
.columnsGap(10)
.rowsTemplate('1fr 1fr 1fr 1fr')
Row() {
Button('上一页')
.width(100)
.onClick(() => {
// 上一页
this.scroller.scrollPage({ next: false })
})
Button('下一页')
.width(100)
.onClick(() => {
// 下一页
this.scroller.scrollPage({ next: true })
})
}
.width('100%')
.justifyContent(FlexAlign.SpaceAround)
}
}
}
八、总结
Grid&GridItem 组件真的是鸿蒙布局中的"瑞士军刀",无论是规则的网格布局还是复杂的不规则布局,都能轻松应对!
核心知识点回顾
- 基础用法:固定行列布局,使用 columnsTemplate 和 rowsTemplate 设置行列数和比例
- 合并行列:使用 rowStart/rowEnd 和 columnStart/columnEnd 实现不规则网格
- 滚动网格:设置 rowsTemplate 或 columnsTemplate 实现水平/垂直滚动≈
- 代码控制:使用 Scroller 控制器实现代码控制滚动
实战技巧
- 使用@Builder 装饰器拆分复杂布局,提高代码可读性
- 利用 Array.from({ length: n })快速生成测试数据
- 合理设置 GridItem 的宽高,优化滚动性能
📚 推荐资料
- 官方文档:Grid 组件开发指南
- 组件参考:Grid API 文档
我是盐焗西兰花,≈
不教理论,只给你能跑的代码和避坑指南。≈
下期见!🥦
更多推荐



所有评论(0)