鸿蒙应用开发实战【22】— 空状态引导页设计与实现
·
鸿蒙应用开发实战【22】— 空状态引导页设计与实现
前言
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
用户首次打开号码助手时,数据库是空的——没有卡号、没有应用绑定记录。这时候如果直接展示一个空白列表,用户会感到困惑。一个好的 App 应该在空状态时展示友好的引导信息,告诉用户「这里还没有内容,接下来你可以这样做」。这就是 EmptyState 组件的价值。
本篇涵盖:EmptyState @Builder 组件设计、Blank() 弹性布局实现垂直居中、多场景空状态(列表为空/搜索无结果/筛选无匹配/导入为空)、条件渲染展示空状态、引导按钮点击导航。
图1:EmptyState 组件结构 + Blank 弹性布局 + 多场景空状态 + 条件渲染
一、EmptyState 组件设计
1.1 组件结构
// 通用的空状态组件
@Builder
private EmptyState(icon: string, title: string,
desc: string, btnText: string, btnAction: () => void) {
Column() {
// ① 大图标(Emoji / 自定义图标)
Text(icon)
.fontSize(48)
.margin({ bottom: 16 })
// ② 标题(主要说明)
Text(title)
.fontSize(18)
.fontWeight(AppFonts.WEIGHT_BOLD)
.fontColor(AppColors.TEXT)
.margin({ bottom: 8 })
// ③ 描述文案(详细说明)
Text(desc)
.fontSize(12)
.fontColor(AppColors.TEXT_2)
.textAlign(TextAlign.Center)
.margin({ bottom: 20 })
// ④ 引导按钮(操作入口)
Button(btnText, { type: ButtonType.Normal })
.fontSize(12)
.fontColor('#FFFFFF')
.backgroundColor(AppColors.PRIMARY)
.borderRadius(18)
.height(36)
.width(140)
.onClick(() => btnAction())
}
.alignItems(HorizontalAlign.Center)
.width('100%')
}
1.2 组件层级
| 层级 | 组件 | 说明 |
|---|---|---|
| ① | Text(icon) | 大号 Emoji 图标,视觉吸引注意 |
| ② | Text(title) | 核心状态说明,粗体醒目 |
| ③ | Text(desc) | 辅助引导文案 |
| ④ | Button(btnText) | CTA 按钮,点击后引导用户操作 |
二、Blank() 弹性布局实现垂直居中
2.1 为什么需要 Blank
空状态页面需要在屏幕中间展示,但页面上方有 TitleBar、下方有 TabBar。直接设置 justifyContent(FlexAlign.Center) 会让内容整体居中,但如果内容偏少可能会偏上或偏下。使用 Blank() 可以弹性撑开,将 EmptyState 推到视觉中心。
// 空状态页面结构
Column() {
// ① 标题栏(固定高度)
TitleBar()
// ② 弹性撑开区域
Blank()
// ③ 空状态内容(居中显示)
this.EmptyState('📭', '暂无绑定记录',
'你还没有添加任何应用绑定\n点击下方按钮开始添加',
'+ 添加应用', () => this.navigateToAddPage())
// ④ 弹性撑开区域
Blank()
// ⑤ 底部 TabBar(固定高度)
TabBar()
}
.height('100%')
2.2 Blank() 的弹性原理
Blank() 是一个不可见的弹性组件,会自动占据 Column 中的剩余空间。两个 Blank() 会把中间的 EmptyState 推到 Column 的垂直中心。
| 对比 | 实现方式 | 效果 |
|---|---|---|
| justifyContent: Center | 只居中子元素 | 内容可能偏上(TabBar 占位) |
| Blank() 双边撑开 | 上下 Balance | 严格视觉居中 |
2.3 结合三态 UI
// 在实际页面中,空状态是三态 UI 的"正常内容"的一部分
if (this.loading) {
this.LoadingState()
} else if (this.loadError) {
this.ErrorState(this.loadError)
} else if (this.rows.length === 0) {
this.EmptyStatePage() // ← 这里使用 Blank + EmptyState
} else {
this.ContentPage() // ← 正常列表
}
三、多场景空状态
不同的场景需要不同的空状态文案和图标:
3.1 场景定义
// 各个页面调用 EmptyState 的场景
// 首页 — 没有绑定记录
this.EmptyState('📭', '暂无绑定记录',
'你还没有添加任何应用绑定\n点击下方按钮开始添加',
'+ 添加应用', () => { this.navigate('pages/AddAppPage') })
// 卡号管理 — 没有卡号
this.EmptyState('💳', '暂无卡号',
'点击右上角「+」添加你的第一张卡号',
'', () => {}) // 不需要按钮,右上角有 +
// 搜索页 — 搜索无结果
this.EmptyState('🔍', '未找到匹配结果',
'尝试修改搜索关键词或筛选条件',
'', () => {})
// 粘贴导入 — 未识别到应用
this.EmptyState('📎', '未识别到应用信息',
'请确认粘贴内容包含应用名称和卡号',
'重新粘贴', () => { this.pastedText = '' })
3.2 场景矩阵
| 场景 | 图标 | 标题 | 引导操作 |
|---|---|---|---|
| 绑定记录为空 | 📭 | 暂无绑定记录 | + 添加应用 |
| 卡号列表为空 | 💳 | 暂无卡号 | 右上角 + |
| 搜索无结果 | 🔍 | 未找到匹配结果 | 无(仅在搜索框输入) |
| 粘贴导入为空 | 📎 | 未识别到应用信息 | 重新粘贴 |
| 筛选无匹配 | ❌ | 当前筛选无结果 | 重置筛选 |
四、引导按钮导航
4.1 导航封装
// 页面内部的导航方法
private navigateToAddPage(): void {
router.pushUrl({
url: 'pages/AddAppPage',
params: {} as Record<string, Object>
})
}
4.2 关闭弹窗后导航
有时候空状态出现在弹窗或面板中,需要先关闭再导航:
private handleAction(): void {
if (this.isInSheet) {
this.showSheet = false // 先关闭面板
}
// 延迟导航,给关闭动画留时间
setTimeout(() => {
this.navigateToAddPage()
}, 300)
}
五、本篇小结
| 知识点 | 核心要点 |
|---|---|
| EmptyState @Builder | 参数化:icon/title/desc/btnText/btnAction |
| Blank() 弹性布局 | 两个 Blank() 将内容推到垂直居中 |
| 多场景复用 | 搜索/列表/导入/筛选各自定制文案 |
| 条件渲染 | if/else 判断数据长度为 0 时展示空状态 |
| 引导按钮 | 点击导航到添加页面,考虑弹窗延迟 |
参考资料
如果这篇文章对你有帮助,欢迎点赞👍、收藏⭐、关注🔔,你的支持是我持续创作的动力!
相关资源:
更多推荐




所有评论(0)