Grid 网格布局在企业应用中的实践
一、引言
在企业办公应用中,网格布局是一种常见且高效的 UI 组织方式。HarmonyOS NEXT 的 ArkUI 框架提供了Grid组件,它支持行列模板配置、自适应布局和响应式设计,非常适合用于展示网格状的功能入口、数据卡片等场景。本文将以"星办 OA"企业办公审批项目中的HomePage快捷发起区域的 Grid 布局为实际案例,深入解析Grid组件的使用方式、行列模板配置、自适应布局以及响应式设计。
二、Grid 组件的基本概念
2.1 什么是 Grid
Grid是 ArkUI 框架中用于实现网格布局的容器组件。它可以将子组件按照行和列的规则排列,形成整齐的网格结构。Grid组件支持自定义行列模板、间距控制、滚动等功能。
构造函数签名:
Grid({ scroller?: Scroller })2.2 GridItem 的概念
GridItem是Grid的子组件,用于表示网格中的单个单元格。每个GridItem只能包含一个根组件,但根组件可以包含任意复杂的子组件树。
三、快捷发起区域的实际实现
3.1 使用 Row 实现的网格布局
在"星办 OA"的HomePage中,快捷发起区域使用了Row组件来模拟网格布局,而不是直接使用Grid组件:
@Builder private buildQuickStart() { Column({ space: 14 }) { this.buildSectionTitle('快捷发起', '常用审批') Column({ space: 12 }) { Row({ space: 12 }) { this.buildQuickAction('请假', '休假与调休', '假') this.buildQuickAction('报销', '费用与票据', '报') this.buildQuickAction('出差', '行程与预算', '差') this.buildQuickAction('采购', '物品与服务', '采') } .width('100%') Row({ space: 12 }) { this.buildQuickActionSmall('加班', '加') this.buildQuickActionSmall('用印', '印') this.buildQuickActionSmall('报销', '报') this.buildQuickActionSmall('出差', '差') } .width('100%') } } .width('100%') .padding(16) .borderRadius(18) .backgroundColor(Color.White) }这里通过两行Row组件,每行 4 列,实现了 2 行 4 列的网格布局。第一行是主功能入口(请假、报销、出差、采购),第二行是次要功能入口(加班、用印、报销、出差)。
3.2 使用 Flex 实现的网格布局
在ApprovalCreatePage中,审批类型的选择器使用了Flex组件来实现网格布局:
@Builder private buildTypeSelector() { Column({ space: 12 }) { Row() { Text('审批类型').fontSize(16).fontWeight(FontWeight.Bold).fontColor('#182431') Text('必选').fontSize(11).fontColor('#E5484D') } .width('100%') .justifyContent(FlexAlign.SpaceBetween) Flex({ wrap: FlexWrap.Wrap, justifyContent: FlexAlign.Start }) { ForEach(this.types, (type: string) => { Text(type) .width('30%') .height(38) .margin({ right: '3%', bottom: 10 }) .textAlign(TextAlign.Center) .fontSize(14) .fontWeight(this.selectedType === type ? FontWeight.Medium : FontWeight.Regular) .fontColor(this.selectedType === type ? '#2459E0' : '#667085') .border({ width: 1, color: this.selectedType === type ? '#3B6FF5' : '#EAECF0' }) .borderRadius(10) .backgroundColor(this.selectedType === type ? '#F1F4FF' : Color.White) .onClick(() => { this.selectedType = type this.summary = '' }) }, (type: string) => type) } } .width('100%') .padding(15) .borderRadius(16) .backgroundColor(Color.White) }这里通过Flex({ wrap: FlexWrap.Wrap, justifyContent: FlexAlign.Start })配合width('30%')和margin({ right: '3%' }),实现了每行 3 列的网格布局。每个类型项占 30% 宽度,右侧 3% 间距,合计 99%(接近 100%)。
四、Grid 组件的详细配置
4.1 行列模板配置
Grid组件支持通过columnsTemplate和rowsTemplate属性来配置行列模板:
Grid() { // 网格内容 } .columnsTemplate('1fr 1fr 1fr 1fr') // 4列等宽 .rowsTemplate('1fr 1fr') // 2行等高 .columnsGap(12) // 列间距 .rowsGap(12) // 行间距其中columnsTemplate和rowsTemplate使用字符串模板语法:
'1fr 1fr 1fr 1fr':4 列等宽,每列占 1 份可用空间'100px 1fr 2fr':第 1 列固定 100px,第 2 列占 1 份,第 3 列占 2 份'repeat(3, 1fr)':3 列等宽(repeat 语法)
4.2 间距配置
Grid组件支持配置行间距和列间距:
columnsGap:列间距rowsGap:行间距
4.3 自适应布局
Grid组件支持通过onGridItemIndexChange事件监听网格项的变化,实现自适应布局。当网格容器的尺寸变化时,可以根据可用空间自动调整网格的列数。
五、Row 模拟网格 vs 原生 Grid 组件
5.1 对比分析
| 特性 | Row 模拟网格 | 原生 Grid 组件 |
| 列数固定 | 手动控制 | 通过模板配置 |
| 滚动支持 | 需要 Scroll 包装 | 内置支持 |
| 自适应 | 需要手动计算 | 支持行列模板 |
| 使用场景 | 固定列数的小型网格 | 数据量大的网格 |
5.2 选择建议
在"星办 OA"项目中,快捷发起区域使用Row模拟网格而不是原生Grid组件,是因为:
- 列数固定:快捷发起区域固定为 4 列,不需要动态调整
- 数据量小:只有 8 个功能入口,不需要虚拟滚动
- 布局简单:每行独立,不需要复杂的跨行跨列配置
对于数据量大、需要虚拟滚动或复杂行列配置的场景,建议使用原生的Grid组件。
六、Grid 的响应式设计
6.1 响应式列数
在响应式设计中,Grid 可以根据屏幕宽度自动调整列数。例如:
Grid() { ForEach(this.items, (item: GridItemData) => { GridItem() { this.buildGridItem(item) } }) } .columnsTemplate(this.getColumnsTemplate()) .rowsGap(12) .columnsGap(12)private getColumnsTemplate(): string { let width: number = this.getUIContext().getWindowSizeSync().width if (width >= 1000) { return '1fr 1fr 1fr 1fr 1fr' // 大屏 5 列 } if (width >= 600) { return '1fr 1fr 1fr 1fr' // 中屏 4 列 } return '1fr 1fr 1fr' // 小屏 3 列 }6.2 自适应间距
除了列数,间距也可以根据屏幕尺寸调整:
private getGridGap(): number { let width: number = this.getUIContext().getWindowSizeSync().width if (width >= 1000) { return 16 // 大屏大间距 } return 12 // 小屏小间距 }七、Grid 的高级用法
7.1 跨行跨列
GridItem支持通过columnStart、columnEnd、rowStart、rowEnd属性实现跨行跨列:
Grid() { GridItem() { Text('1').fontSize(30) } GridItem() { Text('2').fontSize(30) } .columnStart(1) // 从第 2 列开始 .columnEnd(2) // 到第 3 列结束(跨 2 列) } .columnsTemplate('1fr 1fr 1fr') .rowsTemplate('1fr 1fr')7.2 虚拟滚动
当网格数据量很大时,Grid组件支持虚拟滚动:
Grid() { ForEach(this.largeDataSet, (item: DataItem) => { GridItem() { Text(item.label) } }) } .columnsTemplate('1fr 1fr 1fr') .rowsTemplate('1fr 1fr 1fr 1fr 1fr')Grid的虚拟滚动机制与List类似,只渲染当前可见区域的网格项,大幅提升性能。
八、企业应用中的网格布局模式
8.1 功能入口网格
这是"星办 OA"中快捷发起区域的模式,适用于展示多个功能入口:
- 每行 4 列,每列等宽
- 每个入口包含图标和文字标签
- 点击入口跳转到对应功能页面
8.2 数据展示网格
适用于展示数据卡片或统计信息:
Grid() { ForEach(this.statCards, (card: StatCard) => { GridItem() { Column({ space: 8 }) { Text(card.value).fontSize(28).fontWeight(FontWeight.Bold) Text(card.label).fontSize(12).fontColor('#667085') } .padding(16).borderRadius(12).backgroundColor(Color.White) } }) } .columnsTemplate('1fr 1fr') .rowsGap(12).columnsGap(12)8.3 审批类型选择器
"星办 OA"中审批类型选择器使用了 Flex 换行实现网格,适用于需要动态选择多个选项的场景:
- 每行 3 列
- 选中状态高亮
- 点击切换选中项
九、最佳实践
9.1 选择合适的布局方案
- 固定列数的小型网格(< 10 项):使用
Row模拟 - 固定列数的大型网格(>= 10 项):使用
Grid组件 - 需要自适应的网格:使用
Grid组件配合响应式模板 - 需要换行的网格:使用
Flex配合FlexWrap.Wrap
9.2 性能优化
- 对于大型网格,使用
Grid组件的虚拟滚动特性 - 为
GridItem提供稳定的 key,帮助框架识别哪些项需要更新 - 避免在
GridItem内部创建过于复杂的组件树
9.3 视觉一致性
- 保持网格项之间的间距一致
- 使用统一的卡片样式(圆角、阴影、背景色)
- 确保网格项在视觉上具有相同的权重
十、总结
Grid网格布局是 ArkUI 框架中重要的布局方式,特别适合企业应用中功能入口、数据卡片等场景。通过"星办 OA"项目中快捷发起区域和审批类型选择器的实际代码分析,我们深入理解了多种网格布局的实现方式。
在"星办 OA"中,快捷发起区域使用Row模拟网格布局,审批类型选择器使用Flex换行实现网格,它们各有适用场景。对于数据量大、需要虚拟滚动或复杂行列配置的场景,Grid组件是更好的选择。
理解网格布局的原理、掌握行列模板配置、合理选择布局方案,是构建高质量 HarmonyOS NEXT 应用界面布局的基础。