本文同步发表于我的微信公众号,微信搜索 程语新视界 即可关注,每个工作日都有文章更新
一、Grid组件基础
1.1 Grid组件概述
Grid是鸿蒙ArkUI框架提供的网格布局容器,由"行"和"列"分割的单元格组成,通过指定子组件所在的单元格实现各种布局效果。Grid组件从API Version 7开始支持,后续版本不断增强了其功能。
核心特点:
- 支持自定义行列数和尺寸占比
- 允许子组件跨越多行或多列
- 提供水平和垂直两种布局方向
- 支持滚动、懒加载等性能优化特性
- 可与GridItem子组件配合实现复杂布局
1.2 Grid与GridItem关系
Grid的直接子组件必须是GridItem,这是网格布局的基本规则。GridItem用于定义网格中的单个项目,可以包含其他子组件。
重要说明:
- Grid子组件的索引值按顺序递增计算
- if/else条件分支中,只有成立的分支会参与索引计算
- ForEach/LazyForEach会展开所有子节点计算索引
- visibility属性为Hidden或None时仍会计算索引
二、Grid核心API与属性
2.1 布局控制API
2.1.1 行列模板(rowsTemplate/columnsTemplate)
通过设置行列模板确定网格的基本结构:
Grid() {
// 子组件
}
.rowsTemplate('1fr 1fr 1fr') // 3行,每行高度相等
.columnsTemplate('1fr 2fr 1fr') // 3列,中间列宽度是两侧的2倍
高级用法:
- 使用
repeat()
函数简化重复模式 auto-fill
/auto-fit
实现自适应布局
// 自动填充列,最小列宽100vp
.columnsTemplate("repeat(auto-fit, 100vp)")
// 按容器高度10%自动计算行数
.rowsTemplate("repeat(auto-fill, 10%)")
2.1.2 行列间距(columnsGap/rowsGap)
控制网格行列之间的间距:
Grid()
.columnsGap(10) // 列间距10vp
.rowsGap('5%') // 行间距占容器高度的5%
注意:负值会被处理为0,优先级高于子组件边距
2.1.3 布局方向(layoutDirection)
控制子项的排列方向:
Grid()
.layoutDirection(GridDirection.Row) // 默认,从左到右排列
// 或 GridDirection.Column 从上到下排列
2.2 GridItem属性
GridItem用于定义网格中的单个项目,关键属性包括:
GridItem() {
// 子组件
}
.rowStart(0) // 起始行号
.rowEnd(1) // 结束行号
.columnStart(0) // 起始列号
.columnEnd(1) // 结束列号
.selectable(true) // 是否可被鼠标框选
注意事项:
- 行列号从0开始计数
- 合理范围是0到总行数/列数-1
- 只有设置了columnTemplate和rowTemplate的Grid中,这些属性才有效
2.3 滚动控制API
2.3.1 滚动条设置
Grid()
.scrollBar(BarState.Auto) // 自动显示滚动条(API 10+默认)
.scrollBarColor('#FFAABB') // 自定义颜色
.scrollBarWidth(6) // 宽度6vp
支持的状态:
BarState.Off
:隐藏滚动条BarState.On
:常显滚动条BarState.Auto
:滚动时显示
2.3.2 滚动监听
Grid()
.onScrollIndex((firstIndex: number) => {
console.log('当前首项索引:', firstIndex)
})
可用于实现分页加载等高级功能
2.4 性能优化API
2.4.1 懒加载缓存(cachedCount)
Grid()
.cachedCount(3) // 上下各缓存3屏数据
仅对LazyForEach生效,平衡内存与流畅度
2.4.2 强制重建(forceRebuild)
GridItem()
.forceRebuild(true) // 数据变更时重新计算布局
适用于动态增删或修改跨行跨列属性的场景
三、Grid高级特性
3.1 编辑模式(editMode)
启用拖拽排序功能(API 8+):
Grid()
.editMode(true) // 允许用户拖拽GridItem调整位置
需配合onItemDragStart
/onItemDragMove
事件实现完整交互
3.2 多选模式(multiSelectable)
启用鼠标框选(API 8+):
Grid()
.multiSelectable(true) // 配合onSelect事件获取选中项索引
适用于文件管理器等需要批量操作的场景
3.3 不规则布局(GridLayoutOptions)
处理跨行跨列的高级配置(API 10+):
Grid({ layoutOptions: {
regularSize: [1, 1], // 常规项占1行1列
irregularIndexes: [0, 5], // 指定不规则项的索引
onGetIrregularSizeByIndex: (index) => {
return (index === 0) ? [2, 1] : [1, 2] // 第0项跨2行,第5项跨2列
}
}})
注意:垂直滚动Grid不支持跨多行,水平滚动不支持跨多列
3.4 响应式断点(GridRow/GridCol)
针对不同屏幕尺寸动态调整列数:
GridRow({
columns: { xs: 2, sm: 4, md: 8, lg: 12 }, // 不同断点列数
gutter: { x: 12, y: 8 }, // 行列间距
breakpoints: ['320vp', '520vp', '840vp'] // 断点阈值
}) {
GridCol({ span: { xs: 1, lg: 2 } }) { /* 子项 */ }
}
实现自适应栅格系统,常用于仪表盘布局
四、Grid实战示例
4.1 基础网格布局
@Entry
@Component
struct BasicGridExample {
build() {
Grid() {
GridItem() {
Text('会议').fontSize(20)
}.backgroundColor('#D4F2E7')
GridItem() {
Text('签到').fontSize(20)
}.backgroundColor('#87CEFA')
GridItem() {
Text('投票').fontSize(20)
}.backgroundColor('#FFF0F5')
GridItem() {
Text('打印').fontSize(20)
}.backgroundColor('#E6E6FA')
}
.rowsTemplate('1fr 1fr')
.columnsTemplate('1fr 1fr')
.columnsGap(10)
.rowsGap(15)
.height('100%')
.width('100%')
}
}
4.2 可滚动网格布局
@Component
struct ScrollableGridExample {
@State services: string[] = ['直播', '进口', '秒杀', '超市', '充值', '机票', '酒店', '电影', '理财', '美食']
build() {
Column({ space: 5 }) {
Grid() {
ForEach(this.services, (service: string) => {
GridItem() {
Text(service)
.fontSize(16)
.textAlign(TextAlign.Center)
.width('100%')
.height('100%')
}
.width('25%')
.height(80)
.backgroundColor('#FFFFFF')
})
}
.rowsTemplate('1fr 1fr') // 只设置rowsTemplate,内容超出时可水平滚动
.rowsGap(15)
.height('80%')
.width('100%')
}
.height('100%')
.width('100%')
.backgroundColor('#F1F3F5')
}
}
4.3 不规则网格
// 不规则商品列表
@Entry
@Component
struct ECommerceGridExample {
@State productList: Product[] = [
{ id: 1, type: 'banner' },
{ id: 2, type: 'normal' },
{ id: 3, type: 'normal' },
{ id: 4, type: 'normal' },
{ id: 5, type: 'promotion' },
// 更多商品...
]
build() {
Grid({ layoutOptions: {
regularSize: [1, 1],
irregularIndexes: [0, 4],
onGetIrregularSizeByIndex: (index) =>
index === 0 ? [2, 2] : [1, 2]
}}) {
LazyForEach(this.productList, (item: Product, index: number) => {
GridItem() {
if (item.type === 'banner') {
Image('banner.jpg')
.objectFit(ImageFit.Cover)
} else if (item.type === 'promotion') {
Text('限时促销')
.fontSize(18)
.textAlign(TextAlign.Center)
} else {
Column() {
Image('product.jpg')
.width(80)
.height(80)
Text(`商品 ${item.id}`)
.fontSize(14)
}
}
}
.backgroundColor('#FFF')
.rowStart(index === 0 ? 1 : 2)
.columnEnd(index === 4 ? 3 : 1)
})
}
.columnsTemplate('1fr 1fr 1fr')
.rowsTemplate('repeat(auto-fill, 200vp)')
.columnsGap(8)
.rowsGap(12)
.scrollBar(BarState.Auto)
.cachedCount(2)
.height('100%')
.width('100%')
}
}
class Product {
id: number;
type: string;
}
效果说明:
- 首项为大图广告(跨2×2)
- 第5项为横向促销栏(跨2列)
- 支持懒加载与流畅滚动
五、总结
5.1 性能优化建议
-
大数据集处理:
- 单页渲染超过50项时强制使用LazyForEach
- 合理设置cachedCount(通常2-3)
- 避免在GridItem中使用复杂计算
-
渲染优化:
- 使用@Recycle装饰器复用组件
- 简化GridItem内部结构
- 避免频繁更新Grid布局属性
-
内存管理:
- 对于动态内容,及时清理不可见项的资源
- 监控Grid的内存使用情况
- 在页面隐藏时释放非必要资源
5.2 兼容性
-
版本差异:
- editMode、multiSelectable等特性需API 8+
- GridLayoutOptions需要API 10+
- 开发前需确认目标设备的API支持情况
-
布局限制:
- Grid直接子组件必须是GridItem
- 复杂布局需在GridItem内部嵌套其他容器
- 跨行跨列有方向限制(垂直滚动不支持跨多行等)
-
调试技巧:
- 使用开发者工具的布局检查器
- 开启Grid的边框显示辅助调试
- 使用console.log输出布局计算的关键值