鸿蒙原生应用 ArkUI 声明式:阅读管理首页的竖版书封与页码进度条
鸿蒙原生应用 ArkUI 声明式:阅读管理首页的竖版书封与页码进度条
App 20「阅读管理」首页(HomeTab),主题色
#A855F7紫色,4 个 Tab 分别为首页(📚)、书库(📖)、记录(✍️)、我的(👤)。首页采用固定 Header(阅读管理/让阅读成为习惯)+ 紫色渐变正在阅读卡(三体·第360页/480页 + 今日已读45分钟)+ 独立搜索栏 + 3 栏统计 + 4 个圆形入口 + 本周阅读时长条形图 + 在读书架 4 张书卡(竖版书封面 56×72 + 环形进度 + 页码进度条 currentPage/totalPages + 写笔记/继续阅读双按钮) + 分类统计排行(文学小说/科幻奇幻/历史人文/心理学,带涨跌)。本篇基于20-reading-manager/entry/src/main/ets/pages/HomeTab.ets(约 243 行)逐段拆解,附 4 张实机截图。
一、整体结构:8 个 @Builder 的内容流
HomeTab 与 App 18/19 首页结构同源(8 个 @Builder 块),但主内容换成"书卡"(BookList):
build() {
Column() {
this.Header()
Scroll() {
Column({ space: 14 }) {
this.HeroCard()
this.SearchBar()
this.StatRow()
this.QuickActionRow()
this.WeekChart()
this.SectionTitle('在读书架')
this.BookList()
this.SectionTitle('分类统计')
this.RankList()
}
.width('100%')
.padding({ left: D.pad, right: D.pad, top: 14, bottom: D.pad + this.safeBottom + 20 })
}
.layoutWeight(1).scrollBar(BarState.Off).align(Alignment.Top)
}
.width('100%').height('100%').backgroundColor(C.bg)
}
8 个块按"品牌 → 在读 → 搜索 → 数据 → 入口 → 图表 → 书架 → 排行"叙事:
- Header — 阅读管理 + Slogan
- HeroCard — 正在阅读(三体·第360页/480页)
- SearchBar — 搜索(书名/作者/分类)
- StatRow — 3 栏统计(已读书籍/阅读时长/读书笔记)
- QuickActionRow — 4 个入口(添加书/书签/笔记/统计)
- WeekChart — 本周阅读时长条形图
- SectionTitle('在读书架') + BookList — 4 张书卡(本页核心)
- SectionTitle('分类统计') + RankList — 4 条分类排行
与 App 19 的差异:App 19 是"精选作品卡"(摄影),App 20 是"在读书架卡"(书)——"封面"从作品 emoji 换成"竖版书封"(56×72 竖长方形模拟书籍比例)——"书封的竖版比例"是阅读 App 的特色(书 vs 照片的形态差异)。

二、Header + HeroCard:正在阅读卡
Header 是"阅读管理/让阅读成为习惯 📚"两行品牌区。HeroCard 是正在阅读的横向渐变卡:
@Builder HeroCard() {
Row({ space: 14 }) {
Column({ space: 6 }) {
Text('正在阅读').fontSize(12).fontColor('#FFFFFF').opacity(0.85)
Text('三体 · 第360页/480页').fontSize(16).fontWeight(FontWeight.Bold).fontColor('#FFFFFF')
Text('今日已读 45 分钟').fontSize(11).fontColor('#FFFFFF').opacity(0.75)
}.alignItems(HorizontalAlign.Start).layoutWeight(1)
Text('📖').fontSize(44)
}
.width('100%').padding(20).borderRadius(D.rLg)
.linearGradient({ angle: 135, colors: [[C.primary, 0.0], [C.accent, 1.0]] })
.onClick(() => { promptAction.showToast({ message: '继续阅读' }); })
}
"正在阅读 / 三体 · 第360页/480页 / 今日已读 45 分钟" 三行——"书名 · 页码 / 今日时长"的阅读进度卡:
- 第360页/480页——"读到哪了"(页码是阅读 App 最核心的进度信号)
- 今日已读 45 分钟——"今天读了多久"(时长是习惯信号)
与 App 19 "本周精选"的差异:App 19 是"作品精选"(内容推荐),App 20 是"正在阅读"(个人进度)——HeroCard 承载"用户当前状态"(阅读 App 最关心"我读到哪了")。"第360页/480页"与 BookList 第一条《三体》进度条(360/480)一致 ✓——"正在阅读的书 = 书架第一本书",数据互相印证。
onClick 整卡可点("继续阅读"跳转阅读器)。
三、SearchBar + StatRow + QuickActionRow
SearchBar 与 App 18/19 同构(🔍 + 透明 TextInput),占位"搜索书名、作者、分类..."——阅读 App 搜索 3 个维度。@State searchText 双向绑定。
StatRow 是 3 栏统计(已读书籍 36/阅读时长 128h/读书笔记 246),数字全部 C.primary 紫色加粗:
@Builder StatRow() {
Row() {
ForEach(this.stats, (s: StatItem) => {
Column({ space: 4 }) {
Text(s.value).fontSize(20).fontWeight(FontWeight.Bold).fontColor(C.primary)
Text(s.label).fontSize(11).fontColor(C.textDim)
}.layoutWeight(1)
}, (s: StatItem) => s.label)
}
.width('100%').padding({ top: 14, bottom: 14 })
.backgroundColor(C.card).borderRadius(D.rMd).border({ width: 1, color: C.stroke })
}
数据一致性:已读书籍 36 = 我的页"历史订单 28"?(不直接对应——但"已读书籍 36"是阅读统计)——App 20 的我的页还是 App 13 的"订单"统计(模板复刻的遗留),首页与我的页数据口径不完全一致(App 13 同款问题)——**"模板换肤时统计项没换"**是 App 20 与 App 17 相同的瑕疵(读者应引以为戒:换业务要换全部数据)。
QuickActionRow(4 个圆形入口:添加书📖/书签🔖/笔记✏️/统计📊):
@Builder QuickActionRow() {
Row() {
ForEach(this.actions, (a: QuickAction) => {
Column({ space: 8 }) {
Row() { Text(a.icon).fontSize(22) }
.width(48).height(48).backgroundColor(C.primarySoft).borderRadius(24).justifyContent(FlexAlign.Center)
Text(a.label).fontSize(11).fontColor(C.textSub)
}.layoutWeight(1).onClick(() => { promptAction.showToast({ message: a.label }); })
}, (a: QuickAction) => a.id.toString())
}
.width('100%').padding({ top: 14, bottom: 14 })
.backgroundColor(C.card).borderRadius(D.rMd).border({ width: 1, color: C.stroke })
}
"添加书/书签/笔记/统计"覆盖阅读 App 4 个核心功能:书(添加)、签(书签)、记(笔记)、统(统计)。
四、WeekChart:本周阅读时长
WeekChart 与 App 18/19 周图同构(7 天条形图 + 共 8.5 小时):
@Builder WeekChart() {
Column({ space: 10 }) {
Row() {
Text('本周阅读时长').fontSize(15).fontWeight(FontWeight.Bold).fontColor(C.text)
Blank()
Text('共 8.5 小时').fontSize(11).fontColor(C.textDim)
}.width('100%')
Row() {
ForEach(this.weekBars, (b: BarItem) => {
Column({ space: 6 }) {
Column().width(14).height(b.value * 0.7).backgroundColor(C.primary).borderRadius(4)
Text(b.day).fontSize(10).fontColor(C.textDim)
}.layoutWeight(1)
}, (b: BarItem) => b.day)
}.width('100%').alignItems(VerticalAlign.Bottom).height(90)
}
.width('100%').padding(14).backgroundColor(C.card).borderRadius(D.rMd).border({ width: 1, color: C.stroke })
}
数据:一 70/二 55/三 85/四 60/五 75/六 100/日 90——周六 100 最高(周末阅读高峰)、周二 55 最低(工作日低谷)——"周末读书多"符合阅读习惯。
"共 8.5 小时"——70+55+85+60+75+100+90 = 535(强度),如果按"每天 1 小时 ≈ 强度 100"换算,535 ≈ 5.35 小时 ≠ 8.5——"强度 vs 小时"口径不完全匹配(又是本系列高频的"图表口径"瑕疵)。但"8.5 小时"按"每强度单位 0.0159 小时"(535×0.0159≈8.5)能对上——反正不是严格自洽,读者应引以为戒。
height(b.value * 0.7) 直接倍率(100×0.7=70vp)——简化版周图(App 18/19 同款)。

五、BookList:在读书架卡(本页核心)
BookList 是 4 张书卡,每张含竖版书封面、书名、星级、作者、分类标签、环形进度、页码进度条(currentPage/totalPages)、写笔记/继续阅读双按钮:
@Builder BookList() {
Column({ space: 12 }) {
ForEach(this.books, (b: BookCard) => {
Column({ space: 12 }) {
Row({ space: 12 }) {
Row() { Text(b.cover).fontSize(36) }
.width(56).height(72).backgroundColor(C.primarySoft).borderRadius(D.rMd).justifyContent(FlexAlign.Center)
Column({ space: 4 }) {
Row({ space: 6 }) {
Text(b.title).fontSize(16).fontWeight(FontWeight.Bold).fontColor(C.text)
Text('⭐' + b.rating + '.0').fontSize(10).fontColor(C.warn)
}
Text(b.author).fontSize(12).fontColor(C.textDim)
Text(b.category).fontSize(11).fontColor(C.primary)
.padding({ left: 6, right: 6, top: 2, bottom: 2 })
.backgroundColor(C.primarySoft).borderRadius(4)
}.alignItems(HorizontalAlign.Start).layoutWeight(1)
Stack({ alignContent: Alignment.Center }) {
Progress({ value: b.progress, total: 100, type: ProgressType.Ring }).width(48).height(48).color(C.primary).style({ strokeWidth: 4 })
Text(b.progress + '%').fontSize(11).fontColor(C.primary).fontWeight(FontWeight.Bold)
}
}.width('100%')
Row() {
Text('进度').fontSize(11).fontColor(C.textDim)
Progress({ value: b.currentPage, total: b.totalPages }).color(C.primary).layoutWeight(1).height(6).borderRadius(3).margin({ left: 8, right: 8 })
Text(b.currentPage + '/' + b.totalPages).fontSize(11).fontColor(C.textSub)
}.width('100%')
Row({ space: 12 }) {
Button('写笔记').fontSize(13).fontColor(C.textSub).backgroundColor(C.cardSoft)
.borderRadius(D.rMd).height(38).layoutWeight(1)
.onClick(() => { promptAction.showToast({ message: '写笔记: ' + b.title }); })
Button('继续阅读').fontSize(13).fontColor('#FFFFFF').backgroundColor(C.primary)
.borderRadius(D.rMd).height(38).layoutWeight(1)
.onClick(() => { promptAction.showToast({ message: '继续阅读: ' + b.title }); })
}.width('100%')
}
.width('100%').padding(14).backgroundColor(C.card).borderRadius(D.rLg).border({ width: 1, color: C.stroke })
}, (b: BookCard) => b.id.toString())
}.width('100%')
}
5.1 4 本书的数据设计
private books: BookCard[] = [
{ id: 1, cover: '📕', title: '三体', author: '刘慈欣', category: '科幻', rating: 5, progress: 75, totalPages: 480, currentPage: 360 },
{ id: 2, cover: '📗', title: '活着', author: '余华', category: '文学', rating: 5, progress: 100, totalPages: 320, currentPage: 320 },
{ id: 3, cover: '📘', title: '人类简史', author: '尤瓦尔', category: '历史', rating: 4, progress: 45, totalPages: 600, currentPage: 270 },
{ id: 4, cover: '📙', title: '思考快与慢', author: '卡尼曼', category: '心理学', rating: 4, progress: 30, totalPages: 520, currentPage: 156 }
];
4 本书的"经典书单":三体(刘慈欣科幻)、活着(余华文学)、人类简史(尤瓦尔历史)、思考快与慢(卡尼曼心理学)——"中国当代文学 + 世界畅销书"的混合书单,每本都是豆瓣高分(4-5 星)——"书架 = 好书单"(真实可信)。
4 本书的"阅读进度梯度":75%(三体)→ 100%(活着)→ 45%(人类简史)→ 30%(思考快与慢)——"一本书在读、一本读完、两本在读中"——"书架该有的状态"(不是全在读、也不是全读完)。
"currentPage/totalPages"页码字段是本页的特色——"页码 = 阅读进度的真实单位"(对比 App 19 的"完成度"、App 16 的"库存",页码是阅读 App 特有的进度表达)。
5.2 竖版书封面(本页特色)
Row() { Text(b.cover).fontSize(36) }
.width(56).height(72).backgroundColor(C.primarySoft).borderRadius(D.rMd).justifyContent(FlexAlign.Center)
56×72 竖长方形封面(宽高比 56:72 ≈ 7:9)——模拟真实书籍的竖版比例(对比 App 19 作品卡的 64×64 方形封面)——"书是竖的、照片是方的"的形态差异。📕📗📘📙 四色书封(红/绿/蓝/橙对应 4 本书)——"书封颜色 = 书"(emoji 替代真实书封)。
borderRadius(D.rMd) 圆角——书封四周圆角(真实书封边缘是直角,demo 圆角更柔和)。
5.3 书名 + 星级 + 作者 + 分类标签
Row({ space: 6 }) {
Text(b.title).fontSize(16).fontWeight(FontWeight.Bold).fontColor(C.text)
Text('⭐' + b.rating + '.0').fontSize(10).fontColor(C.warn)
}
Text(b.author).fontSize(12).fontColor(C.textDim)
Text(b.category).fontSize(11).fontColor(C.primary)
.padding({ left: 6, right: 6, top: 2, bottom: 2 })
.backgroundColor(C.primarySoft).borderRadius(4)
- 书名 + ⭐5.0 数字星级(
'⭐' + rating + '.0'字符串拼接,App 18/19 同款) - 作者(灰色小字)——刘慈欣/余华/尤瓦尔/卡尼曼
- 分类标签(紫字浅紫底胶囊)——科幻/文学/历史/心理学
"作者"字段是本页的特色(App 18/19 作品卡没有作者维度,App 20 书卡必须有作者)——"书的作者 = 书卡必备信息"。
5.4 环形进度 + 页码进度条(双进度)
Stack({ alignContent: Alignment.Center }) {
Progress({ value: b.progress, total: 100, type: ProgressType.Ring }).width(48).height(48).color(C.primary).style({ strokeWidth: 4 })
Text(b.progress + '%').fontSize(11).fontColor(C.primary).fontWeight(FontWeight.Bold)
}
...
Row() {
Text('进度').fontSize(11).fontColor(C.textDim)
Progress({ value: b.currentPage, total: b.totalPages }).color(C.primary).layoutWeight(1).height(6).borderRadius(3).margin({ left: 8, right: 8 })
Text(b.currentPage + '/' + b.totalPages).fontSize(11).fontColor(C.textSub)
}
双进度设计(本页最值得学的点):
| 进度 | 类型 | 数值 | 语义 |
|---|---|---|---|
| 环形(右上) | ProgressType.Ring | 75% | 百分比进度(整体进度) |
| 页码条(底部) | Progress 线性 | 360/480 | 页码进度(具体读到哪页) |
Progress({ value: b.currentPage, total: b.totalPages }) 用页码做进度条——value=360, total=480 自动算 75%——"页码直接驱动进度条"(不用手动算百分比,Progress 组件内部算)——"真实单位驱动进度"比"百分比字段"更可靠(页码更新 → 进度条自动跟进)。
"环形(概览)+ 页码条(细节)"双进度——右上环形看"大概读了多少",底部页码条看"具体读到 360 页"——**"概览 + 细节双通道"**是阅读 App 进度显示的最佳实践。
5.5 写笔记 + 继续阅读双按钮
Row({ space: 12 }) {
Button('写笔记').fontSize(13).fontColor(C.textSub).backgroundColor(C.cardSoft)
.borderRadius(D.rMd).height(38).layoutWeight(1)
.onClick(() => { promptAction.showToast({ message: '写笔记: ' + b.title }); })
Button('继续阅读').fontSize(13).fontColor('#FFFFFF').backgroundColor(C.primary)
.borderRadius(D.rMd).height(38).layoutWeight(1)
.onClick(() => { promptAction.showToast({ message: '继续阅读: ' + b.title }); })
}
- 写笔记(灰底灰字次级)——记笔记入口
- 继续阅读(紫底白字主操作)——进入阅读器
"写笔记/继续阅读"是书卡的标准双操作(微信读书/掌阅同款)——"继续阅读"是阅读 App 最高频的操作(用户每天点它进入阅读)——主按钮紫色高亮。
六、RankList:分类统计排行
RankList 与 App 18/19 同构(4 条排行 + 涨跌),计数单位是"本":
private ranks: RankItem[] = [
{ id: 1, rank: 1, name: '文学小说', count: 12, trend: '↑' },
{ id: 2, rank: 2, name: '科幻奇幻', count: 8, trend: '↑' },
{ id: 3, rank: 3, name: '历史人文', count: 6, trend: '↓' },
{ id: 4, rank: 4, name: '心理学', count: 5, trend: '↑' }
];
4 条分类:文学小说 12 本↑/科幻奇幻 8 本↑/历史人文 6 本↓/心理学 5 本↑——"本数"计数——"排行单位 = 业务对象"(阅读排"本")。
涨跌箭头 ↑绿↓红(3 涨 1 跌)+ 排名数字前 3 名紫加粗 + Divider().strokeWidth(0.5) 超细分隔线——App 18/19 同款。
分类与书架呼应:文学(活着)/科幻(三体)/历史(人类简史)/心理学(思考快与慢)——"分类排行 = 书架书的分类分布" ✓——"排行榜数据从书架推导"(12 本文学 > 8 本科幻 > 6 本历史 > 5 本心理学)。

七、@State 与主题色的设计
HomeTab 只有 1 个 @State:searchText。其余数据 private 不可变。
主题色 #A855F7 紫色——阅读 App 常用紫色系(Kindle/微信读书偏暖,但"紫色"象征"深度阅读/文化")——与 App 13 音乐紫(#8B5CF6)的区别:App 20 的 #A855F7 更"浓"(偏紫红),App 13 更"淡"(偏蓝紫)——"同系不同调"。
C.primarySoft: '#F3EAFE' 浅紫(比 App 13 的 #F0EBFE 略深)——**"紫色主题的书架"**营造"知识氛围"。
八、与 App 18/19 首页的对比总结
| 维度 | App 18 社交 | App 19 摄影 | App 20 阅读 |
|---|---|---|---|
| 主题色 | 粉红 #EC4899 | 青绿 #0D9488 | 紫 #A855F7 |
| Hero | 今日推荐同好 | 本周精选作品 | 正在阅读(书名·页码) |
| 统计 | 匹配/好友/互动 | 照片/点赞/拍摄 | 书籍/时长/笔记 |
| 主列表 | 同好卡 | 作品卡 | 书卡(竖版封+页码条) |
| 双按钮 | 跳过/打招呼 | 收藏/查看详情 | 写笔记/继续阅读 |
| 排行 | 兴趣 | 分类 | 分类(本数) |
| 特色 | 匹配度环形 | 完成度环形 | 页码进度条(currentPage) |
"内容流首页"模板第 6 次复用(App 13-20 中 App 18/19/20 是"三连同源")——"同一套模板换主题 + 换数据 = 社交/摄影/阅读三个 App"。

九、书架的数据设计:"在读书架"的阅读状态
App 20 首页书架 4 本书的"阅读状态"设计得非常真实:
private books: BookCard[] = [
{ id: 1, cover: '📕', title: '三体', author: '刘慈欣', category: '科幻', rating: 5, progress: 75, totalPages: 480, currentPage: 360 },
{ id: 2, cover: '📗', title: '活着', author: '余华', category: '文学', rating: 5, progress: 100, totalPages: 320, currentPage: 320 },
{ id: 3, cover: '📘', title: '人类简史', author: '尤瓦尔', category: '历史', rating: 4, progress: 45, totalPages: 600, currentPage: 270 },
{ id: 4, cover: '📙', title: '思考快与慢', author: '卡尼曼', category: '心理学', rating: 4, progress: 30, totalPages: 520, currentPage: 156 }
];
"一本在读 + 一本读完 + 两本在读"的梯度:
- 三体 75%(360/480)——"正在读的大部头"(还在读)
- 活着 100%(320/320)——"刚读完的"(进度满)
- 人类简史 45%(270/600)——"读了一半的大部头"(600 页的书读到 270 页)
- 思考快与慢 30%(156/520)——"刚开始读的"(520 页读到 156 页)
"总页数"的设计(480/320/600/520)——"书的厚度不一"(320 页中篇到 600 页大部头)——"书架该有的书形态"(有厚有薄、有在读有读完)。
"currentPage"字段是阅读 App 的核心数据——真实项目里每次翻页都更新 currentPage,进度条自动推进(Progress(value: currentPage, total: totalPages))——"页码 = 阅读器与书架的桥梁"(阅读器改页码 → 书架进度条自动更新)。
"写笔记/继续阅读"双按钮的业务闭环:写笔记(阅读中随时记)→ 继续阅读(回到阅读器)→ 读完(进度 100%)→ 从书架移入"已读"——"书架 → 阅读器 → 笔记 → 已读"是阅读 App 的完整用户旅程。
十、总结
App 20 首页解析完毕。正在阅读 HeroCard(书名·页码)+ 竖版书封 + 环形/页码双进度 + 写笔记/继续阅读是四大亮点。**"页码进度条"(currentPage/totalPages 直接驱动 Progress)**是阅读 App 最核心的进度设计。

更多推荐

所有评论(0)