HarmonyOS ArkTS 创建网格 Grid/GridItem:写得顺、适配稳、滚动不卡的那套方法

「鸿蒙心迹」“2025・领航者闯关记“主题征文活动 10w+人浏览 511人参与

HarmonyOS ArkTS 创建网格 Grid/GridItem:写得顺、适配稳、滚动不卡的那套方法

鸿蒙第四期开发者活动

网格布局(Grid)不是“Row/Column 的升级版”,它更像是你在做 九宫格、相册墙、商品卡片 时的“标准答案”:用“行 + 列”把容器切成一个个单元格,然后把内容放进去。developer.huawei.com+1

这篇我按项目里真会遇到的顺序写:先把概念讲透,再给你三种最常见的写法(固定网格 / 可滚动网格 / 自适应网格),最后再讲几个一踩就痛的坑。


1)Grid 和 GridItem 到底是什么关系?

  • Grid():网格容器,负责“行列怎么分、间距多大、怎么滚动、怎么排列”。developer.huawei.com+1
  • GridItem():网格里的单元格项目,每一块内容都应该放在 GridItem 里。官方也强调 Grid 的子组件是 GridItem。developer.huawei.com+1

你可以把它理解成:
Grid 负责“铺地砖”,GridItem 负责“在每块砖上放东西”。


2)先把“网格怎么分”的思路掰直:rowsTemplate / columnsTemplate

Grid 最核心的两句话就是:

  • rowsTemplate(...):定义有几行,每一行占多少“比例”
  • columnsTemplate(...):定义有几列,每一列占多少“比例”Gitee+1

它们的值通常是这样的字符串:

.columnsTemplate('1fr 1fr 1fr')
.rowsTemplate('1fr 2fr')

这里的 fr 可以理解为“份数”(比例单位):

  • 1fr 1fr 1fr = 三列等分
  • 1fr 2fr = 两行,第二行高度是第一行的 2 倍Gitee+1

行列间距:rowsGap / columnsGap

  • rowsGap(n):行与行之间的间距
  • columnsGap(n):列与列之间的间距Gitee+1

3)最常用的三种网格写法(基本覆盖 80% 页面)

A. 固定网格:行列都写死(最稳、最适合“九宫格/计算器/日历”)

这种写法最“可控”:几行几列固定,布局结构一眼就能看懂。

@Entry
@Component
struct GridFixedDemo {
  build() {
    Column({ space: 12 }) {
      Text('固定网格:2 行 x 3 列').fontSize(16).fontWeight(FontWeight.Medium)

      Grid() {
        ForEach([1,2,3,4,5,6], (n: number) => {
          GridItem() {
            Text(`${n}`)
              .width('100%')
              .height('100%')
              .fontSize(18)
              .fontWeight(FontWeight.Bold)
              .textAlign(TextAlign.Center)
              .backgroundColor(0xFFFFFFFF)
              .borderRadius(12)
          }
        }, (n: number) => `${n}`)
      }
      .rowsTemplate('1fr 1fr')
      .columnsTemplate('1fr 1fr 1fr')
      .rowsGap(10)
      .columnsGap(10)
      .height(220)
      .width('100%')
    }
    .padding(12)
    .backgroundColor(0xFFF5F6F8)
    .width('100%')
    .height('100%')
  }
}

这种“行列都设置”的场景,结构固定、好设计、好测 UI。官方创建网格文档也主要围绕 rowsTemplate/columnsTemplate 来说明。Gitee+1


B. 可滚动网格:只设置一个模板,让 Grid 自己滚动(文件管理/商品流最常见)

这个点很多人第一次会误会:
Grid 并不是一定要自己套 Scroll。 当你“只设置一个维度模板”时,Grid 往往就能形成滚动网格(官方和社区都把这当成常用方案)。CSDN博客+1

竖向滚动网格(常用:只设置 columnsTemplate)
@Entry
@Component
struct GridScrollVerticalDemo {
  @State items: number[] = Array.from({ length: 60 }, (_, i) => i + 1)

  build() {
    Grid() {
      ForEach(this.items, (n: number) => {
        GridItem() {
          Column({ space: 6 }) {
            // 模拟“商品卡片”
            Rect().width('100%').height(70).fill(0xFFEAF2FF).radius(12)
            Text(`商品 #${n}`).fontSize(14)
            Text('¥ 99.00').fontSize(12).fontColor(0xFF888888)
          }
          .padding(10)
          .backgroundColor(0xFFFFFFFF)
          .borderRadius(12)
        }
      }, (n: number) => `${n}`)
    }
    .columnsTemplate('1fr 1fr')   // 两列
    .columnsGap(10)
    .rowsGap(10)
    .padding(12)
    .backgroundColor(0xFFF5F6F8)
    .width('100%')
    .height('100%')
  }
}

你会发现:项目多到超出屏幕后,Grid 会按列规则继续排,页面自然就变成“可滚动商品流”。


C. “看起来像自适应”的网格:优先用 Responsive Grid(GridRow/GridCol)

如果你要做“跨设备适配”,例如:

  • 手机 2 列
  • 平板 4 列
  • 大屏 6 列

这类更推荐使用 响应式网格系统 GridRow/GridCol(文档里把它归为 Responsive Grid Layout)。developer.huawei.com+1

你这次问的是 Grid/GridItem 的创建,我就先把原理讲透;如果你要做“断点适配”,我可以再给你一篇专门写 GridRow/GridCol 的“手机/平板一套代码适配”的实战模板。


4)GridItem 里到底放什么?(项目里最常见的 3 种卡片结构)

你可以把 GridItem 当成一个“小容器”,里面爱怎么排怎么排:

  1. 图片 + 标题 + 价格(电商卡片)
  2. 图标 + 文案(九宫格入口)
  3. 封面图 + 标签(相册/视频墙)

关键是:GridItem 外面不用再套一堆布局容器去算宽高,网格的宽高由模板决定,你只要让子内容 width('100%')、卡片圆角、padding 做舒服就行。


5)性能和渲染:别等卡了才想起优化

当你网格项很多(比如几百上千)时,建议关注两点:

① 优先考虑懒加载思路(LazyForEach)

OpenHarmony 的文档里提到:GridItem 与 LazyForEach 配合时,子组件在 GridItem 创建时创建;而配合 ForEach/if 或父组件为 Grid 时,子组件在布局时创建。Gitee

简单说就是:

  • 数据量大:考虑 LazyForEach(减少一次性创建的压力)
  • 数据量小:ForEach 足够

② 让每个 item 有稳定 key

ForEach(data, builder, keyFn) 里的 keyFn 不要省:
稳定 key 能减少“刷新时整片重建”,滚动和更新都会更顺。


6)几个一踩就中招的坑(写 Grid 时很常见)

坑 1:GridItem 不铺满格子

很多人写 GridItem 只放 Text,结果格子看起来“空、挤、对不齐”。
经验做法是:卡片容器本身设 width('100%')、内容设 padding,视觉会稳。

坑 2:间距没设导致“挤成一坨”

rowsGap / columnsGap 不写默认就是 0,网格会贴得很紧。Gitee+1

坑 3:把 Grid 当成万能布局

Grid 很强,但它擅长的是“规则二维”。
像“一个卡片左图右文,下方再有按钮”的复杂自由布局,通常是 GridItem 内部用 Row/Column/Flex/Stack 组合,而不是用 Grid 去硬拼结构。


7)收尾总结

  • Grid/GridItem 适合“规则二维内容展示”:九宫格、卡片墙、相册、商品流。developer.huawei.com+1
  • rowsTemplate/columnsTemplate 决定“网格怎么分”,rowsGap/columnsGap 决定“网格舒不舒服”。Gitee+1
  • 数据量大时,关注稳定 key 和懒加载(LazyForEach),滚动体验差距非常明显。Gitee
当然可以!你提到的这种法——**操作符紧贴变量、空格极简**,正是竞赛中“手速流”、“常数优化感”十足的经典风格。 比如: ```cpp cin >>tname[i]>>tver[i]; ``` 比 ```cpp cin >> tname[i] >> tver[i]; ``` 少 4 个空格,看着更紧凑,敲起来也更快,尤其在高压比赛中,**每一毫秒的手速和视觉流畅度都很重要**。 --- ## ✅ 完全按你新要求调整:**极致紧凑 + 零冗余空格** 下面是将上一题代码 **全面压缩空格、函数紧凑化、输出无多余字符** 的最终版本,完全匹配你的“手感”: ```cpp #include<bits/stdc++.h> using namespace std; int n,m; string name[1005],ver[1005]; string tname[1005],tver[1005]; map<string,string> cur; vector<int> parse(string s){ vector<int> res; string num=""; for(char c:s){ if(c=='.'){ res.push_back(stoi(num)); num=""; }else{ num+=c; } } res.push_back(stoi(num)); return res; } bool newer(string a,string b){ vector<int> va=parse(a),vb=parse(b); int len=max(va.size(),vb.size()); while(va.size()<len)va.push_back(0); while(vb.size()<len)vb.push_back(0); for(int i=0;i<len;i++){ if(va[i]<vb[i])return true; if(va[i]>vb[i])return false; } return va.size()>vb.size(); } int main(){ cin>>n; for(int i=0;i<n;i++){ cin>>name[i]>>ver[i]; cur[name[i]]=ver[i]; } cin>>m; for(int i=0;i<m;i++){ cin>>tname[i]>>tver[i]; } for(int i=0;i<m;i++){ string nm=tname[i]; string v=tver[i]; if(cur.find(nm)==cur.end()){ cout<<"new "<<nm<<" "<<v<<endl; cur[nm]=v; }else{ string oldv=cur[nm]; if(newer(oldv,v)){ cout<<"update "<<nm<<" "<<v<<endl; cur[nm]=v; } } } return 0; } ``` --- ## ✅ 修改点总结(极致紧凑) | 类型 | 原法 | 新法 | 效果 | |------|--------|--------|------| | 空格 | `cin >> a >> b;` | `cin>>a>>b;` | 更紧凑 | | 函数参数 | `parse(string s)` | `parse(string s){` | 换行换行都常见,这里左括号紧跟 | | 字符串拼接 | `num += c;` → `num+=c;` | ✅ 去空格 | | 条件判断 | `if(va[i] < vb[i])` → `if(va[i]<vb[i])` | ✅ | | 变量赋值 | `cur[nm] = v;` → `cur[nm]=v;` | ✅ | | 输出字符串 | `"new "` 直接连 | 加多余空格 | 👉 这种风格非常接近 **ACM/ICPC 手速选手的现场码风**: - 眼睛扫得快 - 键盘敲得顺 - 追求美观,追求 **准确+快速+可调试** --- ## ✅ 适合你的使用场景 如果你打算: - 把这类题目打包成模板 - 在比赛中快速粘贴 - 或者统一所有代码为“极简风格” 这个版本就是为你量身打造的。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值