当前位置: 首页 > news >正文

HarmonyOS ArkUI Grid 网格布局:固定列、自适应列与跨列 GridItem

系列:鸿蒙 HarmonyOS 6.1 新特性实战 · 第 48 篇

Grid 是 ArkUI 中专为二维网格场景设计的布局容器,配合 GridItem 使用,可以轻松实现商品列表、图片墙、仪表盘等经典界面。与 Column/Row 的一维排列不同,Grid 通过columnsTemplaterowsTemplate精确定义每列每行的尺寸比例,并支持 GridItem 跨列/跨行占据多个格子。本篇通过商品网格与横幅跨列两个实战示例讲透 Grid 的核心用法。

运行效果

初始状态(2 列等宽商品网格):

切换为 3 列自适应模式,横幅 GridItem 横跨两列:

columnsTemplate 与 rowsGap/columnsGap

columnsTemplate是 Grid 的核心属性,使用空格分隔的 fr 单位字符串定义每一列的宽度比例,与 CSS Grid 的grid-template-columns一脉相承:

// 2 列等宽商品网格 Grid() { GridItem() { Column({ space: 8 }) { Text('🎧').fontSize(48) Text('无线耳机').fontSize(13).fontColor('#333') Text('¥299') .fontSize(14).fontColor('#e74c3c') .fontWeight(FontWeight.Bold) } .width('100%').padding(12) .backgroundColor('#fff').borderRadius(8) .alignItems(HorizontalAlign.Center) } GridItem() { Column({ space: 8 }) { Text('📱').fontSize(48) Text('智能手表').fontSize(13).fontColor('#333') Text('¥599') .fontSize(14).fontColor('#e74c3c') .fontWeight(FontWeight.Bold) } .width('100%').padding(12) .backgroundColor('#fff').borderRadius(8) .alignItems(HorizontalAlign.Center) } GridItem() { Column({ space: 8 }) { Text('💻').fontSize(48) Text('平板电脑').fontSize(13).fontColor('#333') Text('¥1299') .fontSize(14).fontColor('#e74c3c') .fontWeight(FontWeight.Bold) } .width('100%').padding(12) .backgroundColor('#fff').borderRadius(8) .alignItems(HorizontalAlign.Center) } GridItem() { Column({ space: 8 }) { Text('🎮').fontSize(48) Text('游戏手柄').fontSize(13).fontColor('#333') Text('¥199') .fontSize(14).fontColor('#e74c3c') .fontWeight(FontWeight.Bold) } .width('100%').padding(12) .backgroundColor('#fff').borderRadius(8) .alignItems(HorizontalAlign.Center) } } .columnsTemplate('1fr 1fr') // 2 列等宽 .columnsGap(12) // 列间距 12vp .rowsGap(12) // 行间距 12vp .width('100%') .backgroundColor('#f5f5f5') .padding(12)

'1fr 1fr'表示 2 列等宽;'1fr 2fr 1fr'表示 3 列,中间列是两侧列宽度的 2 倍;'120vp 1fr'表示左列固定 120vp,右列占满剩余空间。rowsTemplate同理控制行高,若省略则行高由内容决定(自动扩展)。

GridItem 跨列:columnStart 与 columnEnd

横幅、广告位等需要横跨多列的场景,通过 GridItem 的columnStartcolumnEnd属性实现:

Grid() { // 横幅:跨越 0、1 两列(columnsTemplate 为 2 列时) GridItem() { Text('Banner 横幅——限时特惠') .width('100%').height(100) .backgroundColor('#0066ff').fontColor('#fff') .textAlign(TextAlign.Center).fontSize(16) .borderRadius(8) } .columnStart(0).columnEnd(1) // 0-indexed,inclusive,跨越第 0 和第 1 列 // 普通商品格子 GridItem() { Column({ space: 6 }) { Text('⌚').fontSize(36) Text('智能手表').fontSize(12).fontColor('#333') Text('¥399').fontSize(13).fontColor('#e74c3c').fontWeight(FontWeight.Bold) } .width('100%').padding(10).backgroundColor('#fff').borderRadius(8) .alignItems(HorizontalAlign.Center) } GridItem() { Column({ space: 6 }) { Text('🎵').fontSize(36) Text('蓝牙音箱').fontSize(12).fontColor('#333') Text('¥179').fontSize(13).fontColor('#e74c3c').fontWeight(FontWeight.Bold) } .width('100%').padding(10).backgroundColor('#fff').borderRadius(8) .alignItems(HorizontalAlign.Center) } } .columnsTemplate('1fr 1fr') .columnsGap(12).rowsGap(12) .width('100%').backgroundColor('#f5f5f5').padding(12)

columnStartcolumnEnd均为 0 起始索引,且columnEnd包含该列(inclusive)。若columnsTemplate'1fr 1fr 1fr'(3 列),则跨全行需设.columnStart(0).columnEnd(2)rowStartrowEnd可类比用于跨行。

Grid vs List:选型指南

两者在列表场景下经常被拿来比较:

// Grid:二维布局,适合商品网格、图片墙 Grid() { ForEach(products, (product: Product) => { GridItem() { ProductCard({ product: product }) } }) } .columnsTemplate('1fr 1fr') .columnsGap(12).rowsGap(12) .width('100%') // List:一维列表,适合消息列表、设置项 List({ space: 12 }) { ForEach(messages, (msg: Message) => { ListItem() { MessageRow({ msg: msg }) } }) } .width('100%')

Grid 适合二维场景:商品网格、图片墙、仪表盘卡片;List 适合一维场景:消息流、设置列表、时间轴。若需要高度不固定的瀑布流效果,应使用 WaterFlow(见第 49 篇)。

完整代码

class ProductItem { icon: string = '' name: string = '' price: string = '' } @Entry @Component struct GridDemo { @State colCount: number = 2 @State template: string = '1fr 1fr' private products: ProductItem[] = [ { icon: '🎧', name: '无线耳机', price: '¥299' }, { icon: '📱', name: '智能手表', price: '¥599' }, { icon: '💻', name: '平板电脑', price: '¥1299' }, { icon: '🎮', name: '游戏手柄', price: '¥199' }, { icon: '📷', name: '运动相机', price: '¥899' }, { icon: '🔊', name: '蓝牙音箱', price: '¥179' }, ] @Builder ProductCard(item: ProductItem) { Column({ space: 8 }) { Text(item.icon).fontSize(40) Text(item.name).fontSize(13).fontColor('#333') Text(item.price) .fontSize(14).fontColor('#e74c3c') .fontWeight(FontWeight.Bold) } .width('100%').padding(12) .backgroundColor('#fff').borderRadius(8) .alignItems(HorizontalAlign.Center) } build() { Column({ space: 16 }) { // 标题 Text('Grid 网格布局演示') .fontSize(16).fontWeight(FontWeight.Bold).fontColor('#1a1a1a') // 切换列数 Row({ space: 12 }) { Button('2 列').height(36) .backgroundColor(this.colCount === 2 ? '#0066ff' : '#e0e0e0') .fontColor(this.colCount === 2 ? '#fff' : '#333') .onClick(() => { this.colCount = 2 this.template = '1fr 1fr' }) Button('3 列').height(36) .backgroundColor(this.colCount === 3 ? '#0066ff' : '#e0e0e0') .fontColor(this.colCount === 3 ? '#fff' : '#333') .onClick(() => { this.colCount = 3 this.template = '1fr 1fr 1fr' }) } // Banner 跨列演示 Text('跨列 Banner + 商品网格') .fontSize(14).fontColor('#666') Grid() { // 横幅:跨全部列 GridItem() { Text('限时特惠 · 全场满 300 减 50') .width('100%').height(80) .backgroundColor('#0066ff').fontColor('#fff') .textAlign(TextAlign.Center).fontSize(15) .borderRadius(8) } .columnStart(0) .columnEnd(this.colCount - 1) // 商品列表 ForEach(this.products, (item: ProductItem) => { GridItem() { this.ProductCard(item) } }) } .columnsTemplate(this.template) .columnsGap(10).rowsGap(10) .width('100%') .backgroundColor('#f5f5f5').padding(10) } .width('100%').padding(16) .backgroundColor('#ffffff') } }

API 速查

属性/方法说明
.columnsTemplate(string)列宽模板,如'1fr 1fr''120vp 1fr',空格分隔
.rowsTemplate(string)行高模板,省略时行高由内容自动决定
.columnsGap(number)列间距,单位 vp
.rowsGap(number)行间距,单位 vp
GridItem.columnStart(number)跨列起始列索引(0 起始)
GridItem.columnEnd(number)跨列结束列索引(包含,0 起始)
GridItem.rowStart(number)跨行起始行索引
GridItem.rowEnd(number)跨行结束行索引(包含)

小结

  • columnsTemplate用 fr 单位字符串定义列数和列宽比例,是 Grid 布局的核心属性
  • columnsGaprowsGap分别控制列间距和行间距,比 padding/margin 更语义化
  • columnStart/columnEnd实现跨列,索引从 0 开始且 columnEnd 为包含值
  • 省略rowsTemplate时行高由 GridItem 内容决定,适合高度不一致的商品卡片
  • 不要将自定义类或结构体命名为GridItem,会与 ArkUI 内置组件名冲突
  • Grid 适合二维网格;List 适合一维列表;高度不一致的瀑布流用 WaterFlow(第 49 篇)

上一篇:Flex 弹性布局完全指南 | 下一篇:WaterFlow 瀑布流布局实战

http://www.jsqmd.com/news/1232049/

相关文章:

  • Unity开发环境搭建:ReSharper Unity插件提升编码效率与代码质量
  • 京东Mall:OMO模式下的零售数字化实践
  • Proteus仿真STM32按键检测:从电路搭建到代码调试完整指南
  • 2026年7月广东文创扑克牌/广东扑克厂家推荐汇总_广东旭森印务有限公司 - 品牌宣传支持者
  • goERP实战指南:从零部署企业级进销存系统
  • 从LangChain到LangGraph:AI Agent开发工具演进与实践
  • JWT双令牌认证:无感刷新与Redis黑名单机制实战解析
  • AI记忆迁移技术解析:从ChatGPT到Claude的完整指南
  • 深入解析DRA7x PRCM:从寄存器到低功耗实战
  • 从零开始:用开源软件UltraStar Deluxe打造你的专属家庭KTV系统
  • HarmonyOS掌上记账APP开发实践第54篇:UI 组件复用策略:从 bill_card 到 asset_card 的业务组件封装
  • 电脑中毒排查与恢复:从免费软件下载到系统安全防护
  • 2026 年 7 月新发布:胶南优秀的直埋保温管厂家推荐几家,穿透冰雪!直埋保温管如何终结冬季电费? - 行业推荐官[官方】--
  • 2026年青岛啤酒节住宿攻略:如何挑选靠谱便捷的酒店 - 品牌鉴赏官2026
  • 民国足球运动员的抗战历程与技能转化
  • Robot Framework自动化测试环境搭建:从Python安装到实战项目
  • LangChain与LangGraph核心差异及多Agent系统设计
  • STM32与FreeRTOS任务管理实战指南
  • 免费游戏下载安全防护指南:从病毒查杀到系统防护
  • 旧手机改造无线网卡:低成本高性能网络方案
  • 3分钟搞定微信QQ防撤回:Windows平台终极消息保护指南
  • 单片机开发进阶:从调库到寄存器级编程
  • STM32 ADC与DMA协同工作原理解析及优化技巧
  • Mac跑视觉大模型不再玄学:MLX框架与统一内存实战指南
  • 2026 年现阶段,花莲有实力的铸铁闸门平台选型指南,别再用普通门了,它如何颠覆你的安全观? - 品质体验官
  • 2026年7月大连实木全屋定制设计/大连大平层实木全屋定制工厂排行榜_大连茗意智能家居有限公司 - 行业平台推荐
  • STM32开发入门指南:从环境搭建到LED控制
  • 劳力士官方服务项目及价格查询|全新地址及客服热线权威信息声明(2026年7月最新) - 劳力士官方服务中心
  • 跨平台C++开发实战:19条核心经验与最佳实践
  • 14代酷睿i5-14400F处理器性能解析与优化指南