鸿蒙新特性:Grid 网格布局——构建应用抽屉
手机桌面是一个典型的网格布局——4 列或 5 列的图标矩阵,每行等宽排列,点击即可打开应用。这种"等宽多列、自动换行"的布局需求在移动端无处不在:应用抽屉、照片网格、商品列表、分类导航……都是网格布局的典型场景。
HarmonyOS NEXT ArkUI 提供了 Grid 组件——一个高性能的网格布局容器,配合 GridItem 组件灵活控制每列的内容和样式。本文将深入讲解 Grid/GridItem 的 API 和使用技巧,并构建一个"应用抽屉"——支持动态列数切换、分类筛选和名称搜索。
关键词:HarmonyOS、ArkUI、Grid、GridItem、网格布局、应用抽屉、列数切换、分类筛选
一、Grid 组件 API
1.1 基本用法
Grid() {
ForEach(this.apps, (app: AppInfo) => {
GridItem() {
Column() {
Text(app.icon).fontSize(32)
Text(app.name).fontSize(12)
}
}
}, (app: AppInfo) => app.name)
}
.columnsTemplate('1fr 1fr 1fr 1fr')
.rowsGap(10)
.columnsGap(10)
核心参数与属性:
| 参数/属性 | 类型 | 说明 |
|---|---|---|
.columnsTemplate() |
string | 列模板,如 '1fr 1fr 1fr 1fr' 表示 4 等宽列 |
.rowsTemplate() |
string | 行模板(可选,通常由内容撑开即可) |
.rowsGap() |
Length | 行间距 |
.columnsGap() |
Length | 列间距 |
.width() / .height() |
Length | 容器尺寸 |
1.2 columnsTemplate 详解
columnsTemplate 是 Grid 最核心的属性——它决定了网格有多少列以及每列的宽度。它使用 CSS Grid 的 fr 单位语法:
| 值 | 含义 |
|---|---|
'1fr 1fr 1fr' |
3 列等宽 |
'1fr 1fr 1fr 1fr' |
4 列等宽 |
'1fr 1fr 1fr 1fr 1fr' |
5 列等宽 |
'1fr 2fr 1fr' |
3 列,中间列宽度是两边的 2 倍 |
'100px 1fr 1fr' |
3 列,第一列固定 100px,后两列等分剩余空间 |
fr(fraction)是弹性单位——所有 fr 值相加后按比例分配可用空间。Demo 中使用纯 1fr 模板实现等宽列:
getColumnsTemplate(): string {
if (this.columnCount === 3) return '1fr 1fr 1fr';
if (this.columnCount === 5) return '1fr 1fr 1fr 1fr 1fr';
return '1fr 1fr 1fr 1fr';
}
1.3 动态列数切换
Demo 通过在页面右上角提供 3/4/5 三个列数按钮,让用户实时切换网格密度:
@State columnCount: number = 4;
// 切换按钮
Text('3').onClick(() => { this.columnCount = 3; })
Text('4').onClick(() => { this.columnCount = 4; })
Text('5').onClick(() => { this.columnCount = 5; })
选中按钮为蓝底白字(#1677FF 背景 + #FFFFFF 文字),未选中为灰底灰字(#F2F3F5 背景 + #888899 文字)。
Grid 的 .columnsTemplate(this.getColumnsTemplate()) 会随着 columnCount 变化自动重新计算列模板,Grid 中的内容即时重新排列——不需要手动刷新任何内容。
1.4 GridItem 样式
每个 GridItem 包含应用的图标和名称:
GridItem() {
Column() {
Text(app.icon) // emoji 图标(32sp)
.fontSize(32)
.margin({ bottom: 6 })
Text(app.name) // 应用名称(12sp)
.fontSize(12)
.fontColor('#1a1a2e')
.maxLines(1)
.textOverflow({ overflow: TextOverflow.Ellipsis })
}
.width('100%')
.padding({ top: 16, bottom: 12 })
.borderRadius(12)
.backgroundColor(app.bgColor.concat('18')) // 半透明背景
}
图标使用 emoji 字符(如 💬🎵🛒),32sp 字号在网格单元中足够醒目。名称限制单行 + 省略号,防止长名称撑破布局。背景色使用应用的品牌色 + '18' 后缀(即 9% 透明度),形成半透明的淡色背景——比纯色背景更柔和,用户能感知到不同应用的"色调"但不会过于刺眼。
二、应用抽屉的整体设计
2.1 页面架构
AppDrawerPage
├── 标题栏 — "应用抽屉" + 当前应用数
├── 搜索栏(圆角 pill 风格)
├── 操作栏
│ ├── 分类筛选标签(横向滚动)
│ └── 列数切换按钮(3/4/5)
├── Grid 网格区域
│ └── GridItem(应用图标 + 名称)
├── 空状态提示
└── 底部信息栏(列数 + 总应用数)
2.2 数据类设计
class AppInfo {
name: string; // 应用名称
icon: string; // emoji 图标
category: string; // 分类标签
bgColor: string; // 品牌色(用于半透明背景)
constructor(name: string, icon: string, category: string, bgColor: string) {
this.name = name;
this.icon = icon;
this.category = category;
this.bgColor = bgColor;
}
}
AppInfo 的 bgColor 字段被巧用——不仅是数据属性,也直接用于 UI 渲染(.backgroundColor(app.bgColor.concat('18')))。这种"数据驱动视觉"的模式避免了在 UI 层做颜色映射逻辑,数据本身就包含了视觉信息。
2.3 应用数据
Demo 包含 19 款应用,覆盖 5 个分类:
| 分类 | 应用 | 数量 |
|---|---|---|
| 社交 | 微信💬、QQ🐧、微博📢、钉钉📌 | 4 |
| 工具 | 计算器🔢、时钟⏰、日历📅、文件管理📁、翻译🌐 | 5 |
| 娱乐 | 音乐🎵、视频🎬、游戏中心🎮、直播📺 | 4 |
| 购物 | 商城🛒、优惠券🎫、二手交易♻️ | 3 |
| 阅读 | 新闻📰、电子书📚、知识社区💡 | 3 |
每个应用的 emoji 图标尽量贴近实际用途——微信的 💬、音乐的 🎵、商城的 🛒——让用户在 Demo 中也能感受到真实的应用抽屉氛围。
三、搜索与筛选
3.1 搜索过滤
if (this.searchText.length > 0) {
let keyword = this.searchText;
let filtered: AppInfo[] = [];
for (let i = 0; i < apps.length; i++) {
if (apps[i].name.indexOf(keyword) >= 0) {
filtered = filtered.slice().concat(apps[i]);
}
}
apps = filtered;
}
使用 String.indexOf() 做简单的子串匹配——输入"音"可以搜到"音乐",输入"乐"也能搜到。不需要完整的拼音搜索或模糊匹配,但对于 19 款应用的 Demo 规模来说已经足够实用。
3.2 分类筛选
6 个标签(全部 + 5 个分类)使用横向 Scroll 容器,确保在小屏手机上也能完整显示:
Scroll() {
Row() {
ForEach(this.getCategories(), (cat: string) => {
Text(this.getCategoryLabel(cat))
.fontSize(12)
.fontColor(cat === this.activeCategory ? '#FFFFFF' : '#666677')
// ...
.onClick(() => { this.activeCategory = cat; })
})
}
}
.scrollBar(BarState.Off)
.layoutWeight(1)
选中标签蓝底白字,未选中灰底灰字。横向滚动 + 隐藏滚动条,保持视觉整洁。
3.3 搜索+分类双重过滤
搜索和分类是叠加关系——先按分类过滤,再按搜索词过滤:
getFilteredApps(): AppInfo[] {
let apps = this.getAllApps();
// 第一层:分类过滤
if (this.activeCategory !== 'all') { /* 按分类过滤 */ }
// 第二层:搜索过滤
if (this.searchText.length > 0) { /* 按关键词过滤 */ }
return apps;
}
用户可以组合使用:先选择"工具"分类(看所有工具类应用),再搜"计"(只看计算器)——两个维度的过滤协同工作。
3.4 空状态
当筛选结果为空时,显示差异化提示:
if (this.getFilteredApps().length === 0) {
Column() {
Text('没有找到应用')
.fontSize(14).fontColor('#BBBBCC')
Text('试试切换分类或修改搜索关键词')
.fontSize(12).fontColor('#CCCCDD').margin({ top: 4 })
}
.width('100%').padding({ top: 48, bottom: 48 })
}
提示引导用户"切换分类"或"修改搜索关键词"——两个可操作的下一步动作,而非简单的"无结果"。
四、列数切换与视觉密度
4.1 三种密度
| 列数 | columnsTemplate | 适用场景 |
|---|---|---|
| 3 | '1fr 1fr 1fr' |
大图标、老年模式、信息密集型卡片 |
| 4 | '1fr 1fr 1fr 1fr' |
标准手机桌面(默认) |
| 5 | '1fr 1fr 1fr 1fr 1fr' |
小图标、高密度、大屏手机 |
每种密度下,GridItem 的图标大小(32sp)保持不变,但单元宽度不同——3 列时单元最宽,5 列时最窄。用户可以根据屏幕大小和个人偏好选择合适的密度。
4.2 切换按钮设计
列数切换使用三轮圆形按钮,位于分类标签右侧:
[3] [4] [5]
选中为蓝底白字(#1677FF),未选中为灰底灰字(#F2F3F5)。按钮尺寸 26×26dp,足够小不会占据太多空间,又足够大可以用手指点击。
4.3 底部信息
网格下方显示当前列数和总应用数:
当前 4 列网格 · 共 19 款应用
帮助用户感知当前视图参数,也让页面底部不显得空洞。
五、Grid vs WaterFlow vs List
三个组件的对比:
| 维度 | Grid | WaterFlow | List |
|---|---|---|---|
| 布局方式 | 等宽多列,行对齐 | 不等高瀑布流 | 单列垂直列表 |
| 列控制 | columnsTemplate 精确控制 |
columnsTemplate + 自动排列 |
无需列控制 |
| 子项高度 | 通常一致 | 各不相同 | 通常一致 |
| 典型场景 | 应用抽屉、照片网格 | 商品发现、Pinterest | 联系人、消息列表 |
| 滚动方向 | 垂直(默认) | 垂直(默认) | 垂直/水平 |
| 性能 | 高(虚拟化) | 高(虚拟化) | 高(虚拟化) |
选择建议:
- 每个子项尺寸相似、需要规整的行列对齐 → Grid
- 每个子项高度不同、需要充分利用空间 → WaterFlow
- 简单的线性排列、需要分组粘性头 → List
六、交互流程演示
6.1 默认视图
进入页面,19 款应用以 4 列网格排列。每行最多 4 个应用图标——微信、QQ、微博、钉钉在第一行。所有应用半透明彩色背景,视觉丰富而不杂乱。标题栏显示"19 个"。
6.2 搜索应用
在搜索栏输入"音"。Grid 立即刷新,只显示"音乐"一个匹配项。标题栏变为"1 个"。
清空搜索栏,Grid 恢复显示全部 19 款应用。
输入"不存在"。Grid 显示空状态:“没有找到应用” + “试试切换分类或修改搜索关键词”。
6.3 分类筛选
点击"工具"分类标签。Grid 只显示 5 款工具类应用:计算器、时钟、日历、文件管理、翻译。标题栏变为"5 个"。
再在搜索栏输入"计"。双重过滤生效,只显示"计算器"。
点击"全部"恢复所有应用。
6.4 列数切换
点击列数按钮"3"——网格变为 3 列,每行最多 3 个应用。图标间距变宽,视觉更松散。
点击"5"——网格变为 5 列,每行最多 5 个应用。图标间距变窄,信息密度更高。
点击"4"恢复默认。
底部信息栏同步更新:“当前 3 列网格” → “当前 5 列网格” → “当前 4 列网格”。
6.5 点击应用
点击任意应用图标。Toast 提示"打开 XXX"。背景色提供触摸反馈的视觉感知。
七、总结
本文通过"应用抽屉"这个实战案例,全面讲解了 ArkUI Grid 网格布局组件的使用方法。核心知识点包括:
- Grid 基础 API:
columnsTemplate()列模板 +rowsGap()/columnsGap()间距控制 - columnsTemplate 语法:
'1fr 1fr 1fr 1fr'表示 4 等宽列,fr是弹性比例单位 - 动态列数切换:
@State columnCount→getColumnsTemplate()动态生成模板字符串 - GridItem 样式:居中对齐的 icon + name + 半透明品牌色背景
- 双重过滤:分类筛选 + 关键词搜索的叠加过滤逻辑
- 空状态设计:筛选无结果时的引导性提示
- Grid vs WaterFlow vs List:三种布局组件的对比与场景选择
网格布局是移动端信息展示的核心模式之一。从手机桌面到相册网格,从电商首页到设置入口——Grid 组件提供的 columnsTemplate 机制让开发者可以精确控制每行的列数和每列的宽度。配合搜索和筛选,一个灵活的应用抽屉(或任意图标网格)就能轻松构建。
更多推荐



所有评论(0)