SpaceManagePage:空间分类 + 进度条 + 清理缓存

前言
随着用户使用「海风日记」的时间增长,App 占用空间会越来越大——文字日记、图片附件、缩略图缓存……如何让用户一目了然地看到空间使用情况,并提供一键清理的能力?
SpaceManagePage(空间管理页)就是回答这个问题的页面。它由三部分组成:
- 总览进度条 —— 显示已用/总容量的比例
- 空间分类列表 —— 文字日记 / 图片附件 / 其他,各自占用多少
- 清理缓存按钮 —— 一键清理临时文件
本文将从 SpaceManagePage.ets 源码出发,深入讲解:
- 空间分类数据的获取与展示
- 单行布局的视觉层级(色块 + 标签 + 大小 + 百分比)
- 进度条在空间管理页的特殊样式
- 清理缓存的二次确认与执行
- 与
DataManagePage的协作关系
好的空间管理页,应当让用户「3 秒内看懂自己占了多少」。
一、页面整体结构
@Entry
@Component
struct SpaceManagePage {
@State totalSize: string = '45 MB / 128 MB'
@State usedPct: number = 35
@State categories: CategoryItem[] = [
{ label: '文字日记', size: '12 MB', color: '#F5A623', pct: 26 },
{ label: '图片附件', size: '28 MB', color: '#C8956A', pct: 62 },
{ label: '其他', size: '5 MB', color: '#E8D5B0', pct: 12 }
]
build() {
Column() {
this.NavBarBuilder()
Scroll() {
Column({ space: 20 }) {
this.TotalProgressBuilder() // 1. 总览进度条
this.CategoryListBuilder() // 2. 空间分类列表
this.ClearCacheButtonBuilder() // 3. 清理缓存按钮
}
.padding({ left: 16, right: 16, top: 20, bottom: 40 })
}
.layoutWeight(1).scrollBar(BarState.Off)
}
.height('100%').backgroundColor(COLOR_BG)
}
}
1.1 页面结构图
┌────────────────────────────┐
│ ‹ 空间管理 │ ← 导航栏
├────────────────────────────┤
│ 45 MB / 128 MB │
│ ▓▓▓▓▓░░░░░░░░░░░ 35% │ ← 总览进度条
├────────────────────────────┤
│ ■ 文字日记 12 MB 26% │
│ ■ 图片附件 28 MB 62% │ ← 空间分类
│ ■ 其他 5 MB 12% │
├────────────────────────────┤
│ [ 清理缓存 ] │ ← 清理按钮
└────────────────────────────┘
二、总览进度条
@Builder
TotalProgressBuilder() {
Column({ space: 12 }) {
// 容量文字
Row() {
Text(this.totalSize).fontSize(14).fontColor(COLOR_TEXT_MAIN)
Text(`${this.usedPct}%`).fontSize(14).fontColor(COLOR_AMBER)
.margin({ left: 'auto' })
}
.width('100%')
// 进度条本体
Stack({ alignContent: Alignment.Start }) {
Row().height(10).width('100%')
.backgroundColor('rgba(200,180,140,0.18)').borderRadius(5)
Row().height(10).width(`${this.usedPct}%`)
.linearGradient({ angle: 90, colors: [['#FFD4A0', 0], [COLOR_PRIMARY, 1]] })
.borderRadius(5)
}
}
.padding(16)
.backgroundColor(COLOR_CARD_BG)
.borderRadius(CARD_RADIUS)
}
2.1 与「我的」页进度条的对比
| 属性 | 我的页(简版) | 空间管理页(详版) |
|---|---|---|
| 高度 | 6vp | 10vp |
| 圆角 | 3vp | 5vp |
| 容量文字 | 无 | 显示 |
| 百分比文字 | 无 | 显示 |
三、空间分类列表
@Builder
CategoryListBuilder() {
Column() {
ForEach(this.categories, (item: CategoryItem, idx: number) => {
Row({ space: 12 }) {
// 1. 色块
Row()
.width(12).height(12).borderRadius(6)
.backgroundColor(item.color)
// 2. 标签
Text(item.label)
.fontSize(14).fontColor(COLOR_TEXT_MAIN)
.layoutWeight(1)
// 3. 大小
Text(item.size)
.fontSize(13).fontColor(COLOR_TEXT_SECONDARY)
// 4. 百分比
Text(`${item.pct}%`)
.fontSize(12).fontColor(COLOR_TEXT_HINT)
.width(36).textAlign(TextAlign.End)
}
.width('100%').height(50)
.padding({ left: PAGE_PADDING, right: PAGE_PADDING })
// 分隔线
if (idx < this.categories.length - 1) {
Divider().color(COLOR_DIVIDER).margin({ left: PAGE_PADDING })
}
})
}
.backgroundColor(COLOR_CARD_BG)
.borderRadius(CARD_RADIUS)
}
3.1 单行四元素布局
┌──────────────────────────────────────────────────┐
│ [■ 12vp] [文字日记 14vp] [12 MB 13vp] [26% 12vp] │
│ │
│ 12vp layoutWeight(1) auto 36vp │
└──────────────────────────────────────────────────┘
height: 50vp
3.2 色块设计
| 类别 | 颜色 | 含义 |
|---|---|---|
| 文字日记 | #F5A623 琥珀 | 主类别 |
| 图片附件 | #C8956A 棕橙 | 占用最大 |
| 其他 | #E8D5B0 浅米 | 杂项 |
颜色饱和度与「占用大小」正相关——图片最大,颜色最深沉。
3.3 百分比对齐
Text(`${item.pct}%`)
.width(36).textAlign(TextAlign.End)
固定 36vp 宽度 + 右对齐,确保「12%」「26%」「62%」数字右边缘对齐。
四、清理缓存按钮
@Builder
ClearCacheButtonBuilder() {
Button('清理缓存')
.width('100%').height(50)
.fontSize(16).fontColor(COLOR_WHITE).fontWeight(FontWeight.Bold)
.backgroundColor(COLOR_AMBER)
.borderRadius(BTN_RADIUS_FULL)
.onClick(() => this.confirmClear())
}
4.1 二次确认
清理缓存是「会改变状态」的操作,必须二次确认:
confirmClear() {
AlertDialog.show({
title: '清理缓存',
message: '将清除临时文件,日记数据不受影响。是否继续?',
buttons: [
{ text: '取消', role: ButtonRole.CANCEL },
{
text: '清理',
role: ButtonRole.DESTRUCTIVE,
action: () => this.doClear()
}
]
})
}
async doClear() {
try {
const freedBytes = await storageService.clearCache()
const freedMB = (freedBytes / 1024 / 1024).toFixed(1)
promptAction.showToast({ message: `已释放 ${freedMB} MB` })
// 刷新分类数据
await this.refreshCategories()
} catch (e) {
promptAction.showToast({ message: '清理失败:' + e.message })
}
}
五、空间数据的获取
5.1 异步加载
async aboutToAppear() {
await this.refreshCategories()
}
async refreshCategories() {
try {
const data = await storageService.getSpaceInfo()
this.categories = data.categories
this.totalSize = data.totalSize
this.usedPct = data.usedPct
} catch (e) {
console.error('Failed to load space info', e)
}
}
5.2 数据结构
interface CategoryItem {
label: string // 类别名称
size: string // 占用大小(格式化后的字符串)
color: string // 色块颜色
pct: number // 占总容量的百分比
}
六、与其他页面的协作关系
MinePage (我的)
│ 点击「空间管理」
▼
SpaceManagePage (空间管理)
│ 点击「清理缓存」
▼
storageService.clearCache()
│ 释放空间
▼
刷新 categories + Toast
七、常见问题与踩坑记录
Q1:进度条宽度不准确?
原因:usedPct 未取整或超过 100。
解决:Math.min(100, Math.round(pct * 100) / 100)
Q2:清理缓存后空间没变?
原因:系统缓存清理有延迟。
解决:Toast 后延时 500ms 再刷新数据。
Q3:分类列表不滚动?
原因:列表被 Scroll 包裹,但高度溢出。
解决:用 List 替代 ForEach + Column,或确保 Column 在 Scroll 内。
八、总结
本文通过「海风日记」的空间管理页,深入讲解了:
- 总览进度条 —— Stack 叠加 + 10vp 高度 + 容量文字
- 空间分类列表 —— 色块 + 标签 + 大小 + 百分比四元素
- 清理缓存 —— 二次确认 + Toast 反馈
- 数据获取 —— 异步加载 + 错误处理
下一篇文章将进入 第十一系列:Premium 订阅页,敬请期待。
如果这篇文章对你有帮助,欢迎点赞👍、收藏⭐、关注🔔,你的支持是我持续创作的动力!

329

被折叠的 条评论
为什么被折叠?



