news 2025/12/25 4:49:13

鸿蒙学习实战之路-Grid 网格布局组件全攻略

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
鸿蒙学习实战之路-Grid 网格布局组件全攻略

鸿蒙学习实战之路-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) }

🥦西兰花警告

  1. Grid 的子组件必须是 GridItem,不能直接放其他组件!
  2. GridItem 只能有一个子组件,要放多个内容记得用容器包起来!
  3. Grid 如果不设置宽高,会默认继承父组件的尺寸。
基础属性
名称参数类型描述
columnsTemplatestring设置列数和宽度比例,如’1fr 1fr 2fr’表示 3 列,宽度比例 1:1:2
rowsTemplatestring设置行数和高度比例,如’1fr 1fr’表示 2 行,高度各占一半
columnsGapLength设置列间距,默认 0
rowsGapLength设置行间距,默认 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%') } }

三、实战案例:淘宝二楼效果

现在咱们来整个实战案例,实现淘宝二楼的功能入口布局!

注代码中的图片可以自行替换成 其他图片,保证代码逻辑正确即可

效果展示

需求分析

  1. 实现 5x5 的网格布局
  2. 每个网格包含图标和文字
  3. 整体使用渐变色背景

参考代码

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 的合并属性了!

合并属性

名称参数类型描述
rowStartnumber指定当前元素起始行号
rowEndnumber指定当前元素终点行号
columnStartnumber指定当前元素起始列号
columnEndnumber指定当前元素终点列号

实战练习:实现不规则网格

我们来把一个 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 控制器了!

核心步骤

  1. 创建 Scroller 对象
  2. 设置给 Grid
  3. 调用 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 组件真的是鸿蒙布局中的"瑞士军刀",无论是规则的网格布局还是复杂的不规则布局,都能轻松应对!

核心知识点回顾

  1. 基础用法:固定行列布局,使用 columnsTemplate 和 rowsTemplate 设置行列数和比例
  2. 合并行列:使用 rowStart/rowEnd 和 columnStart/columnEnd 实现不规则网格
  3. 滚动网格:设置 rowsTemplate 或 columnsTemplate 实现水平/垂直滚动≈
  4. 代码控制:使用 Scroller 控制器实现代码控制滚动

实战技巧

  • 使用@Builder 装饰器拆分复杂布局,提高代码可读性
  • 利用 Array.from({ length: n })快速生成测试数据
  • 合理设置 GridItem 的宽高,优化滚动性能

📚 推荐资料

  • 官方文档:Grid 组件开发指南
  • 组件参考:Grid API 文档

我是盐焗西兰花,≈
不教理论,只给你能跑的代码和避坑指南。≈
下期见!🥦

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!