news 2026/9/7 17:25:38

鸿蒙原生应用 ArkUI 声明式:阅读管理首页的竖版书封与页码进度条

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
鸿蒙原生应用 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 个块按"品牌 → 在读 → 搜索 → 数据 → 入口 → 图表 → 书架 → 排行"叙事

  1. Header— 阅读管理 + Slogan
  2. HeroCard— 正在阅读(三体·第360页/480页)
  3. SearchBar— 搜索(书名/作者/分类)
  4. StatRow— 3 栏统计(已读书籍/阅读时长/读书笔记)
  5. QuickActionRow— 4 个入口(添加书/书签/笔记/统计)
  6. WeekChart— 本周阅读时长条形图
  7. SectionTitle('在读书架') + BookList— 4 张书卡(本页核心
  8. SectionTitle('分类统计') + RankList— 4 条分类排行

与 App 19 的差异:App 19 是"精选作品卡"(摄影),App 20 是"在读书架卡"()——"封面"从作品 emoji 换成"竖版书封"(56×72 竖长方形模拟书籍比例)——"书封的竖版比例"是阅读 App 的特色(书 vs 照片的形态差异)。

项目源码开源:https://gitee.com/codenestFlow/HarmonyOSHub

二、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.Ring75%百分比进度(整体进度)
页码条(底部)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 个 @StatesearchText。其余数据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 最核心的进度设计。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/7 17:25:00

Excel与WPS中REDUCE和LAMBDA递归:循环与函数式编程的盟主之争

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/7 17:23:21

UL 1030-2019标准解读:范围、测试与认证实操指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/7 17:22:25

Java开发必备:Git回退与origin远程联动实战指南

1. 项目概述与实战场景:为什么Java开发绕不开Git回退和origin联动做Java开发这么多年,Git几乎是每天都要打交道的工具。但说实话,绝大多数人只是停留在git add、git commit、git push三板斧的阶段,一旦遇到代码回退、远程分支同步…

作者头像 李华
网站建设 2026/9/7 17:22:15

小程序码生成与scene参数解析:从接口调用到渠道归因实战

做小程序推广的同学应该都遇到过这类需求:给每个渠道生成专属的小程序码,用户扫进来之后,后台要能区分出这个用户是从哪个渠道来的;做分销裂变的项目,需要知道这个分享动作是谁发起的,业绩算在谁头上&#…

作者头像 李华
网站建设 2026/9/7 17:21:04

开发工具全解析:从IDEA、跨端框架到调试与离线方案

1. 开发工具全景图:先把“用什么”和“为什么用”搞清楚如果你去问一个刚入行的开发者,开发工具是什么,他大概率会回答“就是写代码的软件呗”。这话没错,但只对了一小半。真正的开发工具是一个完整的工具箱——从你写下第一行代码…

作者头像 李华