本文同步发表于我的微信公众号,微信搜索 程语新视界 即可关注,每个工作日都有文章更新
Grid是鸿蒙ArkUI框架提供的网格布局容器,由"行"和"列"分割的单元格组成,通过指定子组件所在的单元格实现各种布局效果。Grid组件从API Version 7开始支持,后续版本不断增强了其功能。
核心特点:
Grid的直接子组件必须是GridItem,这是网格布局的基本规则。GridItem用于定义网格中的单个项目,可以包含其他子组件。
重要说明:
通过设置行列模板确定网格的基本结构:
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%)")
控制网格行列之间的间距:
Grid()
.columnsGap(10) // 列间距10vp
.rowsGap('5%') // 行间距占容器高度的5%
注意:负值会被处理为0,优先级高于子组件边距
控制子项的排列方向:
Grid()
.layoutDirection(GridDirection.Row) // 默认,从左到右排列
// 或 GridDirection.Column 从上到下排列
GridItem用于定义网格中的单个项目,关键属性包括:
GridItem() {
// 子组件
}
.rowStart(0) // 起始行号
.rowEnd(1) // 结束行号
.columnStart(0) // 起始列号
.columnEnd(1) // 结束列号
.selectable(true) // 是否可被鼠标框选
注意事项:
Grid()
.scrollBar(BarState.Auto) // 自动显示滚动条(API 10+默认)
.scrollBarColor('#FFAABB') // 自定义颜色
.scrollBarWidth(6) // 宽度6vp
支持的状态:
BarState.Off
:隐藏滚动条BarState.On
:常显滚动条BarState.Auto
:滚动时显示 Grid()
.onScrollIndex((firstIndex: number) => {
console.log('当前首项索引:', firstIndex)
})
可用于实现分页加载等高级功能
Grid()
.cachedCount(3) // 上下各缓存3屏数据
仅对LazyForEach生效,平衡内存与流畅度
GridItem()
.forceRebuild(true) // 数据变更时重新计算布局
适用于动态增删或修改跨行跨列属性的场景
启用拖拽排序功能(API 8+):
Grid()
.editMode(true) // 允许用户拖拽GridItem调整位置
需配合onItemDragStart
/onItemDragMove
事件实现完整交互
启用鼠标框选(API 8+):
Grid()
.multiSelectable(true) // 配合onSelect事件获取选中项索引
适用于文件管理器等需要批量操作的场景
处理跨行跨列的高级配置(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不支持跨多行,水平滚动不支持跨多列
针对不同屏幕尺寸动态调整列数:
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 } }) { /* 子项 */ }
}
实现自适应栅格系统,常用于仪表盘布局
@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%')
}
}
@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')
}
}
// 不规则商品列表
@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;
}
效果说明:
大数据集处理:
渲染优化:
内存管理:
版本差异:
布局限制:
调试技巧: