【共创季稿事节】鸿蒙原生 ArkTS 布局深度解析:Grid 中 GridItem 的自适应尺寸
鸿蒙原生 ArkTS 布局深度解析:Grid 中 GridItem 的自适应尺寸



目录
- 引言:为什么需要自适应网格
- 背景知识:Grid 布局基础
- 核心技术:aspectRatio 的工作原理
- 实战场景一:固定高度正方形网格
- 实战场景二:横向矩形网格
- 实战场景三:纵向矩形网格
- 实战场景四:容器约束下的宽高比混排
- 完整代码与工程结构
- 常见编译错误与避坑指南
- 性能与最佳实践
- 总结
1. 引言:为什么需要自适应网格
在移动端应用开发中,网格布局是最常见的信息展示形式之一——相册缩略图、商品陈列、功能图标矩阵……几乎每个 App 都离不开它。传统的固定尺寸网格虽然在简单场景下够用,但当屏幕尺寸碎片化严重(从折叠屏到平板,再到车机屏幕),固定宽高的网格往往暴露出三个致命问题:
- 适配性差:同一套代码在不同屏幕密度下要么拥挤、要么空旷。
- 维护成本高:为每个断点写多套尺寸规则,工作量激增。
- 视觉不统一:图片或内容区域的宽高比被打乱,导致 UI 畸变。
鸿蒙 NEXT 提供的 Grid + GridItem 组件体系,配合 aspectRatio 修饰符,从根本上解决了上述问题。开发者只需设定一个高度基准和一个宽高比,框架自动完成剩余的计算与布局。
2. 背景知识:Grid 布局基础
2.1 Grid 组件核心属性
鸿蒙 ArkTS 中的 Grid 是一个二维布局容器,支持行与列的灵活定义。其核心属性包括:
| 属性 | 类型 | 说明 |
|---|---|---|
columnsTemplate |
string | 列模板,例如 '1fr 1fr 1fr' 表示三等分 |
rowsTemplate |
string | 行模板,例如 'auto' 表示高度由内容决定 |
columnsGap |
Length | 列间距 |
rowsGap |
Length | 行间距 |
columnsTemplate 和 rowsTemplate 支持 fr 单位(分数单位)、px/vp 绝对单位以及 auto 关键字。其中 fr 是构建响应式网格的关键——它表示按比例分配剩余空间。
2.2 GridItem 的自适应能力
GridItem 作为 Grid 的直接子元素,可以独立设置尺寸修饰符。与 Flex 布局中的 Item 不同,GridItem 的尺寸计算规则更加丰富:
- 如果设定了
.height(),则以该值为高度基准 - 如果同时设定了
.aspectRatio(),则宽度 = 高度 × aspectRatio - 如果 Grid 设置了
rowsTemplate为非auto值,行高可能覆盖 Item 的 height
正是这种「高度基准 + 宽高比」的组合模式,让 GridItem 的自适应变得既简单又强大。
3. 核心技术:aspectRatio 的工作原理
3.1 数学公式
宽度 = 高度 × aspectRatio
- 当
aspectRatio = 1.0→ 宽度 = 高度 → 正方形 - 当
aspectRatio = 1.5→ 宽度 = 1.5 × 高度 → 横向矩形(3:2) - 当
aspectRatio = 0.75→ 宽度 = 0.75 × 高度 → 纵向矩形(3:4)
3.2 布局计算流程
- Grid 根据
columnsTemplate计算出每一列可用的宽度范围 - GridItem 从
.height()或 Grid 行高获取高度值 aspectRatio将高度换算为宽度- 如果计算出的宽度超过列宽,Grid 会自动压缩(取决于
columnsTemplate设定)
3.3 与 CSS aspect-ratio 的对比
Web 开发者可能熟悉 CSS 的 aspect-ratio 属性。鸿蒙的 aspectRatio 与之类似,但有几点关键差异:
- 方向性更强:鸿蒙以高度为基准计算宽度(而非 CSS 的宽度为基准)
- 与 Grid 体系深度集成:Grid 的列模板约束会影响最终呈现比例
- API 更简洁:无需
object-fit等辅助属性
4. 实战场景一:固定高度正方形网格
4.1 场景描述
最常见的场景:一个图标矩阵或图片墙,每个格子需要保持严格的正方形,且整体视觉整齐划一。
4.2 实现代码
Grid() {
ForEach(this.itemList, (item: ColorItem) => {
GridItem() {
// 内容区域
Column() {
Text(item.label).fontSize(14).fontColor(Color.White)
}
.width('100%').height('100%')
.justifyContent(FlexAlign.Center)
.alignItems(HorizontalAlign.Center)
}
.height(80) // ① 固定高度 80vp
.aspectRatio(1.0) // ② 宽高比 1:1 → 正方形
.backgroundColor(item.color)
})
}
.columnsTemplate('1fr 1fr 1fr') // 3 列等分
.rowsTemplate('auto')
.columnsGap(10).rowsGap(10)
4.3 要点分析
height(80)是高度基准,所有 GridItem 的高度统一为 80vpaspectRatio(1.0)使宽度与高度相等,形成正方形columnsTemplate('1fr 1fr 1fr')确保三个格子均分容器宽度- 当容器宽度变化时,每个格子的内容区自动缩小,但正方形比例保持不变
4.4 运行效果
在 API 24 的设备上,你将看到 6 个色彩鲜艳的正方形以 3 列 2 行的形式排列。每个格子中央显示标签(A1~A6)和比例说明(1:1)。
5. 实战场景二:横向矩形网格
5.1 场景描述
在商品卡片或封面图展示中,经常需要 3:2 或 16:9 的横向矩形。这种比例接近经典摄影构图,视觉上更自然。
5.2 实现代码
Grid() {
ForEach(this.itemList, (item: ColorItem) => {
GridItem() {
// ... 内容区域
}
.height(70) // 高度 70vp
.aspectRatio(1.5) // 宽高比 1.5 → 3:2 横向矩形
.backgroundColor(item.color)
})
}
.columnsTemplate('1fr 1fr') // 2 列,给横向矩形更多空间
.columnsGap(10).rowsGap(10)
5.3 要点分析
aspectRatio(1.5)即 3:2,是照片常用的比例- 高度设置为 70vp,宽度自动计算为 105vp(70 × 1.5)
- 2 列布局为横向矩形提供了更充裕的展示空间
6. 实战场景三:纵向矩形网格
6.1 场景描述
在证件照、竖版海报或名片展示中,纵向矩形(如 3:4)是刚需。这类场景要求高度大于宽度。
6.2 实现代码
Grid() {
ForEach(this.itemList, (item: ColorItem) => {
GridItem() {
// ... 内容区域
}
.height(80) // 高度 80vp
.aspectRatio(0.75) // 宽高比 0.75 → 3:4 纵向矩形
.backgroundColor(item.color)
})
}
.columnsTemplate('1fr 1fr 1fr 1fr') // 4 列,纵向矩形适合更密集排列
.columnsGap(8).rowsGap(8)
6.3 要点分析
aspectRatio = 0.75= 3/4,宽:高 = 3:4- 高度 80vp,宽度自动计算为 60vp(80 × 0.75)
- 4 列排列比 3 列更紧凑,适合纵向矩形
7. 实战场景四:容器约束下的宽高比混排
7.1 场景描述
在最复杂的真实场景中,我们希望 GridItem 的高度由外部容器决定,而非自身固定,同时保持各自的宽高比。例如一个横向滚动条或固定高度的 Banner 区域。
7.2 实现代码
// 外层 Row 固定高度
Row() {
Grid() {
ForEach(this.itemList, (item: ColorItem) => {
GridItem() {
// ... 内容区域
}
// 不设固定 height,由 Grid rowsTemplate 撑满
.aspectRatio(item.ratioVal) // 每个 Item 不同宽高比
.backgroundColor(item.color)
})
}
.columnsTemplate('1fr 1fr 1fr')
.rowsTemplate('1fr') // 单行占满父容器高度
.width('100%').height('100%')
}
.height(120) // 外层容器固定高度 120vp
7.3 要点分析
Row.height(120)提供了 120vp 的固定高度容器rowsTemplate('1fr')让 Grid 行高撑满 Row- GridItem 不设 height,高度继承自 Grid 行高(即 120vp)
- 三个 Item 分别使用 0.6、1.0、1.8 的宽高比 → 宽度分别为 72vp、120vp、216vp
- 这是四种场景中最灵活、最贴近真实产品的模式
8. 完整代码与工程结构
8.1 文件概览
entry/src/main/ets/pages/
├── Index.ets ← 主入口(@Entry)
└── GridAdaptivePage.ets ← 核心示例(@Component + export)
8.2 Index.ets
import { GridAdaptivePage } from './GridAdaptivePage';
@Entry
@Component
struct Index {
build() {
Column() {
GridAdaptivePage()
}
.width('100%')
.height('100%')
}
}
8.3 GridAdaptivePage.ets(完整版)
由于篇幅原因,完整代码已在项目仓库中提供。这里重点列出核心结构:
@Component
export struct GridAdaptivePage {
build() {
Scroll() {
Column({ space: 12 }) {
// 标题区
// 场景一:正方形 GridItem
// 场景二:横向矩形 GridItem
// 场景三:纵向矩形 GridItem
// 场景四:容器约束 + 不同宽高比混排
// 底部说明区
}
.width('100%')
.backgroundColor('#F5F5F5')
}
.width('100%')
.height('100%')
.scrollable(ScrollDirection.Vertical)
}
}
interface ColorItem {
label: string;
desc?: string;
color: string;
ratioVal?: number;
}
8.4 模块配置 (module.json5)
确保 module.json5 中的 pages 配置指向正确的路径:
{
"module": {
"pages": "$profile:main_pages",
// ...
}
}
main_pages 配置文件(通常在 resources/base/profile/ 下)需要包含 GridAdaptivePage:
{
"src": [
"pages/Index",
"pages/GridAdaptivePage"
]
}
9. 常见编译错误与避坑指南
在编写本文示例时,我们遇到了若干典型的 ArkTS 编译错误。以下是完整的问题清单和解决方案,供读者参考。
9.1 错误:Module has no exported member
Module '"@kit.ArkUI"' has no exported member 'Grid'
原因:在 HarmonyOS NEXT (API 24) 中,Grid、GridItem、Text、Column、Row、Color 等基础 ArkUI 组件是全局内置类型,由编译器自动注入,不需要手动 import。
修复:删除所有 import { Grid, GridItem, Text, ... } from '@kit.ArkUI' 语句。
// ❌ 错误
import { Grid, GridItem, Text, Color, Column, Row } from '@kit.ArkUI';
// ✅ 正确 — 无需任何 import,编译器自动注入
@Component
export struct MyPage {
build() {
Grid() { /* ... */ }
}
}
9.2 错误:Object literal must correspond to some explicitly declared class or interface
Object literal must correspond to some explicitly declared class or interface
(arkts-no-untyped-obj-literals)
原因:ArkTS 在严格模式下禁止未声明类型的对象字面量。Text('...', { style: { fontSize: 16 } }) 中的第二个参数是未显式声明类型的对象。
修复:改为链式方法调用:
// ❌ 错误 — 使用对象字面量
Text('标题', { style: { fontSize: 16, fontWeight: FontWeight.Medium } })
// ✅ 正确 — 链式设置属性
Text('标题')
.fontSize(16)
.fontWeight(FontWeight.Medium)
9.3 错误:build() 的根节点必须是容器组件
In an '@Entry' decorated component, the 'build' method can have only one root
node, which must be a container component.
原因:@Entry 装饰的顶层页面组件,其 build() 方法返回的根节点必须是系统容器组件(Column、Row、Stack、Grid、Scroll 等),不能直接使用自定义组件。
修复:用容器组件包裹:
// ❌ 错误 — 直接使用自定义组件作为根节点
@Entry
@Component
struct Index {
build() {
MyCustomComponent() // 不是容器组件
}
}
// ✅ 正确 — 用 Column 包裹
@Entry
@Component
struct Index {
build() {
Column() {
MyCustomComponent()
}
.width('100%').height('100%')
}
}
9.4 错误:跨文件引用需显式 import
Cannot find name 'GridAdaptivePage'
'GridAdaptivePage()' does not meet UI component syntax.
原因:ArkTS 编译器不会自动跨文件解析符号名,必须在使用处显式 import。
修复:
// Index.ets
import { GridAdaptivePage } from './GridAdaptivePage';
// ↑ 注意文件路径,不带 .ets 后缀
@Entry
@Component
struct Index {
build() {
Column() {
GridAdaptivePage() // 现在可以正常使用了
}
}
}
同时,被引用的组件必须用 export 导出:
// GridAdaptivePage.ets
@Component
export struct GridAdaptivePage { // 必须 export
// ...
}
9.5 错误:Column 没有 scrollable 属性
Property 'scrollable' does not exist on type 'ColumnAttribute'.
原因:Column 本身不支持 .scrollable() 属性。滚动能力需要通过外层包裹 Scroll 组件实现。
修复:
// ❌ 错误 — Column 没有 scrollable 属性
Column() {
// 内容...
}
.width('100%').height('100%')
.scrollable(ScrollDirection.Vertical)
// ✅ 正确 — 用 Scroll 包裹 Column
Scroll() {
Column() {
// 内容...
}
.width('100%')
}
.width('100%').height('100%')
.scrollable(ScrollDirection.Vertical)
9.6 ArkTS 严格模式速查表
| 规范项 | 规则 | 示例 |
|---|---|---|
| 对象字面量 | 必须有显式声明的接口/类 | 用 interface 定义类型 |
| 动态属性 | 禁止,必须明确定义 | 所有属性在 struct 中声明 |
| 可选属性 | 用 ? 标记,可省略 |
label?: string |
| export | 跨文件引用需 export | export struct MyComp |
| import | 只能引用 export 的符号 | import { MyComp } from './path' |
| 泛型 | 有限支持 | ForEach<T> 可用 |
| 运算符 | 数值运算需同类型 | 避免 string + number |
10. 性能与最佳实践
10.1 数据源管理
- 使用
@State装饰响应式数据,确保 Grid 在数据变化时自动刷新 - 数据量较大时考虑
LazyForEach替代ForEach,实现按需加载 - 避免在
ForEach的 builder 函数中执行耗时计算
10.2 Grid 列数选择
| 场景 | 推荐列数 | 比例 | 说明 |
|---|---|---|---|
| 图标矩阵 | 3~4 | 1:1 | 视觉整齐 |
| 商品卡片 | 2 | 3:2 或 16:9 | 展示空间充足 |
| 相册缩略图 | 3~4 | 1:1 | 经典九宫格 |
| 竖版海报 | 3~4 | 3:4 或 2:3 | 纵向矩形 |
10.3 嵌套 Grid 的注意事项
- 避免超过 3 层嵌套,会影响布局性能
- 内层 Grid 建议设置明确的
columnsTemplate,不要依赖auto - 内层 GridItem 的
aspectRatio如果与外层约束冲突,优先服从外层
10.4 适配不同屏幕尺寸
// 响应式列数示例
@State private cols: number = 3;
aboutToAppear(): void {
const displayInfo = display.getDefaultDisplaySync();
if (displayInfo.width >= 600) {
this.cols = 4; // 平板:4 列
} else {
this.cols = 3; // 手机:3 列
}
}
11. 总结
通过本文的深度解析,我们完整地掌握了鸿蒙 NEXT 中 Grid + GridItem 的自适应尺寸布局技术。核心公式极其简洁:
宽度 = 高度 × aspectRatio
围绕这个公式,我们构建了四种典型场景:
- 固定高度 + ratio=1.0 → 正方形,适合图标/头像网格
- 固定高度 + ratio=1.5 → 3:2 横向矩形,适合商品卡片
- 固定高度 + ratio=0.75 → 3:4 纵向矩形,适合竖版内容
- 容器约束高度 + 多种 ratio → 适合灵活设计系统
在编码过程中,我们总结出 ArkTS 的六大编译陷阱(无需 import 内置组件、对象字面量需显式声明接口、@Entry 根节点需为容器、跨文件引用需 export/import、滚动需用 Scroll 组件、属性需用链式调用),这些都是 API 24 开发者的必修课。
对比传统的前端网格方案,鸿蒙 Grid + aspectRatio 的优势在于:
- 一次编码,多屏适配——不再为不同屏幕写多套样式
- 声明式语法——代码即意图,可读性极强
- 编译时检查——大量布局错误在编译阶段即暴露,避免运行时崩溃
希望本文能帮助你在鸿蒙原生开发中游刃有余地驾驭 Grid 布局,构建出既美观又健壮的用户界面。
本文代码基于 HarmonyOS NEXT (API 24) 编写,使用 ArkTS 语言。
运行环境:DevEco Studio NEXT + 模拟器/真机
许可协议:MIT License
更多推荐




所有评论(0)