1、前言

🎉 新作已上架 —— 「古诗学习宝」鸿蒙原生应用已在华为应用市场上架,欢迎各位家长 / 老师 / 鸿蒙开发者下载体验!搜索「古诗学习宝」即可,零广告、零内购、纯净版。觉得好用,烦请帮忙点个五星 🌟,您的反馈是我继续打磨的最大动力。

陪娃背古诗这件事,每个家长都不陌生 —— 一年级《静夜思》、三年级《登鹳雀楼》、五年级《将进酒》,12 册语文课本里前前后后 277 首必背古诗,错一句还得重背。

市面上的古诗 App 我下载过十几款,真正给小学生用的不多:要么开屏广告糊脸、要么诱导内购、要么把"背诵"做成了小学奥数式的填鸭题、要么 UI 花花绿绿完全没有古诗词该有的文化氛围。

作为一个鸿蒙开发者 + 一个一年级小朋友的家长,我用一个月业余时间从 0 写了这款**「古诗学习宝」**,专门给小学生设计

  • 📚 277 首小学必背古诗全收录 —— 1~6 年级 上下册同步课本、12 个朝代分组、4 大诗人主题(李白 / 杜甫 / 王维 / 苏轼)
  • 🎯 4 种背诵模式 —— 默写填空 / 选择题 / 连线题 / 听音造句,挑娃喜欢的练
  • 📝 错题本自动派生 —— 背错的题自动记录、复习时优先弹出、可"标记已掌握"软删除
  • 游戏化激励体系 —— 打卡 / 连续天数 / 5 段等级(童生→秀才→举人→进士→诗仙)/ 8 大成就奖章
  • 🎨 宣纸色系古典美学 —— 不是花花绿绿,是真正配得上诗词氛围的视觉:宣纸色 + 墨绿 + 朱砂 + serif 字体 + 国画素材
  • 🔒 零广告 / 零内购 / 零追踪 —— 全本地存储 Preferences,不联网、不收集任何数据

技术栈基于 HarmonyOS 6 + ArkUI V2 原生实现,使用 @kit.ArkUI / @kit.ArkData / @kit.MediaLibraryKit 等系统 Kit 完成路由、持久化、相册接入。本文是「古诗学习宝」开发的总览复盘:产品定位、技术选型、架构设计、12 张实拍截图配解读、6 大技术亮点速览、上架真实经验,让你30 分钟看懂一款完整鸿蒙应用从 0 到 1 的全貌

👉 后续会按主题拆 5 篇文章(凹槽 TabBar / Navigation 跨页同步 / 7 类弹窗选型 / 错题本派生系统 / 等级成就算法),每篇代码即贴即用。


2、整体架构

2.1 技术架构图

┌──────────────────────────────────────────────────────────────────┐
│                         View 视图层                                │
│  ┌──────────────────────────────────────────────────────────┐    │
│  │ Index.ets (Navigation 根) + MainTabsPage.ets (5 大 Tab)   │    │
│  │  ┌─ HomeView 首页 ─┬─ CategoryPage 分类 ─┬─ PlanView 计划 │    │
│  │  ┬─ RecordView 记录 ─┬─ ProfileView 我的                  │    │
│  └──────────────────────────────────────────────────────────┘    │
│  ┌──────────────────────────────────────────────────────────┐    │
│  │ 13 个 NavDestination 子页面:                             │    │
│  │  PoemList / PoemDetail / ReciteMode / 4 种背诵模式页 /    │    │
│  │  CharStudy / Achievements / Feedback ...                  │    │
│  └──────────────────────────────────────────────────────────┘    │
└──────────────────────────────┬───────────────────────────────────┘
                               │ V2 状态:@Local @Param @Event @Computed
                               ▼
┌──────────────────────────────────────────────────────────────────┐
│                       Service 服务层(单例)                       │
│  PoemService    │ RecordService  │ FavoriteService               │
│  PlanService    │ WrongQuestionService │ PhotoHistoryService     │
└──────────────────────────────┬───────────────────────────────────┘
                               │ 数据流
                               ▼
┌──────────────────────────────────────────────────────────────────┐
│                  数据持久化层 + 系统 Kit                           │
│  @kit.ArkData (Preferences) - 10 类 StorageKey                   │
│  @kit.MediaLibraryKit - PhotoViewPicker / Camera Picker          │
│  @kit.ArkUI - router / promptAction / window                     │
│  本地诗词数据库(JSON 静态资源 + 拼音生成工具)                    │
└──────────────────────────────────────────────────────────────────┘

2.2 项目目录结构

wqsy/
└── entry/src/main/
    ├── ets/
    │   ├── entryability/EntryAbility.ets          # 应用入口
    │   ├── common/
    │   │   ├── Constants.ets                      # 路由名/StorageKey/TabKey
    │   │   ├── Format.ets                         # uid / dateStr / todayStr
    │   │   └── UserLevel.ets                      # 5 段等级算法
    │   ├── model/
    │   │   ├── Poem.ets                           # 诗词模型
    │   │   ├── StudyRecord.ets                    # 学习记录
    │   │   └── WrongQuestion.ets                  # 错题模型
    │   ├── service/
    │   │   ├── PoemService.ets                    # 诗词数据查询
    │   │   ├── RecordService.ets                  # 学习记录
    │   │   ├── FavoriteService.ets                # 收藏
    │   │   ├── PlanService.ets                    # 计划/打卡
    │   │   ├── WrongQuestionService.ets           # 错题本
    │   │   └── PreferencesUtil.ets                # 持久化封装
    │   ├── components/
    │   │   ├── BottomTabBar.ets                   # 凹槽 Tab 栏
    │   │   ├── PoemCard / StatCard / NavBar / EmptyView ...
    │   └── pages/
    │       ├── Index.ets                          # Navigation 根
    │       ├── MainTabsPage.ets                   # 5 Tab 容器
    │       ├── views/
    │       │   ├── HomeView / CategoryView /
    │       │   ├── PlanView / RecordView / ProfileView
    │       ├── CategoryPage / PoemListPage / PoemDetailPage
    │       ├── ReciteModePage
    │       ├── ReciteFillPage / ReciteChoicePage /
    │       ├── ReciteConnectPage / ReciteListenPage
    │       ├── CharStudyPage / AchievementsPage / FeedbackPage
    └── resources/
        ├── base/
        │   ├── element/{color,float,string}.json  # 设计 token
        │   ├── media/                              # 27 张资源图 + 6 SVG 图标
        │   └── profile/main_pages.json
        └── dark/element/color.json                 # 暗色模式适配

2.3 架构模式说明

经典「视图 + 服务 + 数据」三层 + 系统 Kit 集成:

层级 文件示例 职责
视图层 HomeView.ets / ProfileView.ets UI 渲染、用户交互、@Local 状态管理
服务层 FavoriteService.ets 等 4 个单例 业务逻辑、跨页面状态唯一源
持久层 PreferencesUtil.ets + 10 个 StorageKey JSON 序列化、跨重启数据保留
系统 Kit @kit.ArkData / @kit.MediaLibraryKit / @kit.ArkUI Preferences、PhotoPicker、router、promptAction

设计原则:3 件事在项目里贯彻到底——① 状态只通过 @Param+@Event 单向传递(V2 模式)② 持久化只走 Service 单例(页面不直接操作 Preferences)③ 路由参数对象化(用 interface CategoryRouteParam 等强类型替代 Record<string, any>)。


3、效果展示

3.1 启动页 - 水墨书卷 + 入场动画

00_splash.jpeg

冷启动第一眼就是浓郁的中国风:宣纸色背景 + 远山水墨纹 + 左下竹叶素材;中央**水墨书卷圆形 Logo(带山日图案)**配 12vp 阴影;下方品牌字「古诗学习宝」40vp 加粗、绿色 #436444、字距 4vp;副标题「教材同步 · 启蒙必备」两侧带装饰线,再下一行「古诗学习 · 背诵 · 赏析」点出 3 大核心能力。

底部 160vp 进度条 3 秒匀速从 0 走到 100%,配合「正在为你打开诗书 · · ·」呼吸感文案;右下「跳过」胶囊按钮让用户随时进入主页面,尊重用户时间

动画分层设计:印章 200ms 起 600ms 弹入、标题 500ms 起 700ms 上浮淡入、副标题 900ms 起 600ms 淡入——错峰入场,3 秒看完整套节奏完全不局促。代码上用 @Local 状态 + getUIContext().animateTo 命令式动画驱动,aboutToDisappear 里清理 timer 防泄漏,是「启动页 + 动画 + 自动跳转」的标准模板。

启动页代码全部在 SplashPage.ets第 6 篇文章会详细拆它怎么用 V2 动画 + Stack 多层合成 + 进度条 setInterval 实现。

3.2 首页 - 古典氛围 + 快捷入口

01_home.jpeg

打开就是浓浓中国风:宣纸色背景 #F9F7F2 + serif 字体 + 国画素材封面。顶部「你好,小诗友 👋 今天也要快乐学习哦」+ 卡通头像(李白形象)。**「今日推荐」**卡片可左右滑切换,当前是清代王士禛的《题秋江独钓图》。

下方 8 个快捷入口:背诵 / 生字 / 今日一首 / 随机一首 / 打卡 / 收藏 / 成就 / 挑战 —— 全部走 NavPathStack.pushPathByName 跳转到对应详情页或切到对应 Tab。最下方「猜你喜欢」继续推荐诗词。

底部 Tab 栏是整个 App 视觉亮点:左 2 个(首页 / 分类)+ 中央凹槽凸起的「学习」(白色圆形带书本图标 + 阴影向上凸出)+ 右 2 个(记录 / 我的),这种设计模仿小红书 / 闲鱼底栏,第 1 篇文章会详细讲怎么用原生 ArkUI 手写凹槽凸起

3.3 分类 - 277 首 6 年级全收录

02_category.jpeg

顶部「分类」NavBar + 搜索框 + 3 个分类 Tab(按年级 active / 按主题 / 按诗人)。下方第一张是"小学生必背古诗词 277 首经典古诗词 [去学习 ›]"主推卡。

往下是 6 个年级卡,每个卡显示 共 X 首 + 已学进度 + 百分比

  • 一年级(启蒙)共 13 首 0%
  • 二年级(基础)共 14 首 0%
  • 三年级(提升)共 17 首 0%
  • 四年级(进阶)共 39 首 3%
  • 五年级(进阶)共 68 首 0%
  • 六年级(拓展)共 126 首 0%

进度数字是 @Computed 实时计算,根据 RecordService 的学习记录自动派生。

3.4 学习计划 - 月历 + 今日任务

03_plan.jpeg

「学习计划 / 每天进步一点点 ✨」+ 顶部月历卡 2026年5月 + 3 张统计胶囊(🔥1 连续 / 📅1 本月 / 🏆1 累计)。下方是完整月历,13 号是今天(绿色圆圈高亮),已打卡的天会显示打钩。

底部「今日任务 0/3」:3 张诗词任务卡(学新诗《新雷》/ 背诵《寒菊》/ 复习《春日》),每张右侧「去学习 ›」按钮一键进入对应页面。

这就是「Keep 同款打卡 + 等级激励」的鸿蒙版实现,第 5 篇文章详细讲 @Computed 派生算法。

3.5 学习记录 - 错题本 active 状态

04_record.jpeg

顶部 4 张统计卡:本周打卡 1 天 / 已学诗词 1 首 / 平均分 / 连续 1 天。然后是本周打卡日历条(7 天)。

下方 3 个 sub-tab:学习记录 0 / 背诵记录 3 / 错题本 3(当前选中)。错题本里显示 2 道《夏日绝句》的选择题错题——每道题展示「我的答案 / 正确答案 / 再练这首 / 已掌握 ✓」。

这就是错题本自动派生系统,第 4 篇文章详细讲 WrongQuestionService 复合 key 去重 + 软删除字段设计。

3.6 我的 - 等级进阶 + 菜单

05_profile.jpeg

头像区:卡通李白头像 + 名字「小诗友」+ 一年级 + 童生 + 等级进度条(再 9 首升「秀才」)。

4 张统计:连续天数 1 / 已学诗词 1 / 收藏 0 / 总天数 1。

「今日学习目标」进度条 1/3 +「已经开始啦,再坚持一下完成今日目标!」鼓励语。

下方 5 个菜单:成就奖章 ⭐ / 我的收藏 ⭐ / 每日学习目标(3 首)🎯 / 意见反馈 💬 / 关于 ℹ️。

最底是杜甫名言「读书破万卷,下笔如有神」+ 出处《奉赠韦左丞丈二十二韵》。

5 段等级(童生→秀才→举人→进士→诗仙)的算法在 UserLevel.ets第 5 篇详细讲。

3.7 头像选择 - 自定义 Bottom Sheet 弹窗

06_avatar_sheet.jpeg

点头像弹起底部浮层:「更换头像」+ 3 个动作(拍照 / 相册 / 清除)+ 4 个卡通预设(李白 / 杜甫 / 王维 / 苏轼水墨画头像)+ 取消按钮。

  • 拍照new cameraPicker.Picker 调起系统相机零权限
  • 相册new photoAccessHelper.PhotoViewPicker.select() 零权限选图
  • 卡通:直接存 cartoon:libai 等协议字符串到 AppStorage

零权限三合一头像是 wqsy 的一个特色实现,第 3 篇弹窗大全会顺带提到这种 Bottom Sheet 形态。

3.8 学习目标 picker - showActionMenu

07_goal_picker.jpeg

点「每日学习目标」菜单项 → 弹出 showActionMenu 系统级 picker:

  • 1 首 / 天(轻松)
  • 2 首 / 天
  • 3 首 / 天(推荐) 绿色高亮
  • 4 首 / 天
  • 5 首 / 天(挑战) 红色警示
  • 取消

不同 button 用不同 color 字段表达"推荐 vs 挑战"的产品意图——这是 ArkUI 七大类弹窗之一。

第 3 篇弹窗大全会展开 ActionMenu / Dialog / ActionSheet / TextPickerDialog 等 7 类弹窗在真实场景里的选型决策。

3.9 诗词详情 - 注释 / 译文 / 赏析 / 作者

10_poem_detail.jpeg

顶部 NavBar 返回 + 标题「夏日绝句」。中央竖排诗题 + 作者「宋·李清照」+ 4 行诗句:

生当作人杰,
死亦为鬼雄。
至今思项羽,
不肯过江东。

下方 4 个内容 Tab:注释 active / 译文 / 赏析 / 作者。当前显示「人杰 — 人中豪杰。」

底部 3 个动作按钮:收藏(次按钮)/ 开始背诵(主按钮深绿色)/ 生字(次按钮)。

这是单首诗的"中枢页面",从这里能跳转去4 种背诵模式第 4 篇文章讲它们与错题本的派生关系。

3.10 选择背诵方式 - 4 种模式入口

11_recite_mode.jpeg

「选择背诵方式」标题。诗题「夏日绝句」+ 元数据胶囊:4年级·上册 / 难度 ⭐⭐⭐⭐☆ / 爱国。

4 个模式卡 2×2 网格:

  • 默写填空(推荐)✏️ - 挖空补字,巩固记忆
  • 选择题 A - 辨认上下句
  • 连线题 🔗 - 上下句配对,强化记忆
  • 听音造句 🎧 - 听音排字,还原诗句

下方"诗文预览"+“学习贴士:建议顺序:先用「默写填空」打牢字词基础,再用「选择题」检验上下句记忆。”

3.11 选择题答题中

12_recite_choice.jpeg

「选择题」标题 + 第 1/3 题进度。

上句:生当作人杰,
请选择下一句:
  Ⓐ 死亦为鬼雄。   ✓ 正确
  Ⓑ 不是遮头是使风。
  Ⓒ 野火烧不尽,
  Ⓓ 出没风波里。

如果用户选错(B/C/D),结果会通过 WrongQuestionService.add 自动派生一条错题到错题本,记录字段:poemId / mode=‘choice’ / prompt=‘上句:生当作人杰,’ / userAnswer / correctAnswer。同一首诗 + 同模式 + 同 prompt 的错题去重合并

3.12 成就奖章 - 8 大徽章按分类展示

09_achievements.jpeg

顶部「成就奖章」NavBar + 大徽章墙卡(🏆 我的徽章墙 0/8 + 大圆环 0% 完成 + “坚持学习,解锁第一枚徽章 ✨”)。

下方按 4 大类分组:

  • 📖 学习篇 0/2:初入诗坛(学习 10 首古诗 1/10 = 10%)+ 博学多才(学习 50 首 1/50 = 2%)
  • ✏️ 背诵篇 0/2:背诵达人(50 次背诵 3/50 = 6%)+ 百战不殆(100 次 3/100 = 3%)
  • 🔥 坚持篇 0/3(截断显示):连续 X 天打卡 / 累计 Y 天 / …
  • 收藏篇 0/1(未显示)

每个徽章卡:图标 + 名字 + 进度条 + 当前/目标 + 百分比 角标。所有数据 @Computed 实时从 RecordService / FavoriteService 派生,不存独立状态。

第 5 篇文章详讲 @Computed 派生算法 + 8 大成就的条件设计。


4、技术选型与关键决策

4.1 ArkUI V2 全面 Opt-in(@ComponentV2 + @Local + @Param + @Event)

项目启动时 HarmonyOS 6 刚发布 V2 状态管理,权衡后全面用 V2(不混用 V1)。理由:

  • 差分粒度精确到字段@Local 单字段变化只重渲对应组件,比 V1 的 @State 全量 diff 性能更好
  • @Computed 派生缓存:成就进度、统计数字等派生值自动缓存
  • @Event 强类型回调:替代 V1 的非装饰器属性,IDE 自动补全更清晰
@Entry
@ComponentV2
struct Index {
  @Local pathStack: NavPathStack = new NavPathStack();
  @Local favVersion: number = 0;   // 跨页同步版本号
  @Local avatarRef: string = '';
}

@ComponentV2
struct PoemCard {
  @Param poem: PoemBrief = {} as PoemBrief;
  @Param favorite: boolean = false;
  @Event onTap: (id: string) => void = () => {};
  @Event onFav: (id: string) => void = () => {};
}

第 2 篇 Navigation 文章会展开讲 favVersion 版本号是如何让"详情页改了收藏态、返回首页自动刷新"的。

4.2 单一 Navigation + 13 个 NavDestination

整个 App 只有一个 Navigation(在 Index.ets),所有页面都注册成 NavDestination

@Builder
PageMap(name: string) {
  if (name === RouteName.Category) {
    CategoryPage({ pathStack: this.pathStack })
  } else if (name === RouteName.PoemList) {
    PoemListPage({ pathStack: this.pathStack })
  } else if (name === RouteName.PoemDetail) {
    PoemDetailPage({
      pathStack: this.pathStack,
      onFavChange: () => { this.favVersion++; },   // 跨页同步关键
    })
  } else if (name === RouteName.ReciteFill) {
    ReciteFillPage({ pathStack: this.pathStack })
  }
  // ... 13 个 case
}

好处:

  • 统一的路由参数对象router.getParams() as CategoryRouteParam
  • 统一的返回手势:系统左滑返回直接走 pathStack.pop()
  • 跨页同步靠 onShown + favVersion:避免 V2 数组重赋值不触发刷新的边角问题

4.3 Service 单例 + Preferences 持久化

所有跨页面状态走 Service 单例,组件不直接读写 Preferences:

class FavoriteServiceImpl {
  private cache: Set<string> = new Set();
  private loaded = false;

  async load(): Promise<void> {
    const arr = await Pref.getJSON<string[]>(StorageKey.Favorites, []);
    this.cache = new Set(arr);
    this.loaded = true;
  }

  isFav(id: string): boolean {
    return this.cache.has(id);
  }

  async toggle(id: string): Promise<boolean> {
    if (this.cache.has(id)) this.cache.delete(id);
    else this.cache.add(id);
    await Pref.setJSON(StorageKey.Favorites, Array.from(this.cache));
    return this.cache.has(id);
  }
}
export const FavoriteService = new FavoriteServiceImpl();

PrefPreferencesUtil.ets 封装的同步/异步混合接口,内部 JSON.stringify + flush 一气呵成。10 类 StorageKey 集中管理

export class StorageKey {
  static readonly UserGrade      = 'user_grade';
  static readonly UserName       = 'user_name';
  static readonly UserAvatar     = 'user_avatar';
  static readonly Favorites      = 'favorites_v1';
  static readonly StudyRecords   = 'study_records_v1';
  static readonly DailyPlans     = 'daily_plans_v1';
  static readonly Streak         = 'streak_v1';
  static readonly Settings       = 'settings_v1';
  static readonly WrongQuestions = 'wrong_questions_v1';
  static readonly DailyGoal      = 'daily_goal_v1';
}

命名带 _v1 后缀,方便未来 schema 升级时灰度迁移。

4.4 古典美学设计语言

完全自研的"宣纸 + 墨绿 + 朱砂"色板:

{
  "color": [
    { "name": "surface",      "value": "#F9F7F2" },   // 宣纸底色
    { "name": "surface_card", "value": "#FFFFFF" },
    { "name": "primary",      "value": "#436444" },   // 墨绿主色
    { "name": "primary_soft", "value": "#E4EFE4" },
    { "name": "accent",       "value": "#B35C5C" },   // 朱砂强调色
    { "name": "text_title",   "value": "#2D3436" },
    { "name": "text_body",    "value": "#3F4543" },
    { "name": "text_sub",     "value": "#737970" }
  ]
}

字号体系 float.json:fs_caption 12 / fs_body 14 / fs_subtitle 16 / fs_title 18 / fs_headline 22 / fs_poetry 24(专用于诗句)。

诗句一律 serif 字体 + 竖排(emulated with \n + center align),完美贴合古典调性。

4.5 4 种背诵模式的题型抽象

填空 / 选择 / 连线 / 听写 4 种模式表面差异大,核心抽象都是「题面 + 用户输入 + 正确答案 + 校验」。背诵完成时统一调:

// 每个 RecitePage 在答题结束时
await RecordService.add({
  poemId, poemTitle,
  type: 'recite',
  mode: 'choice',    // or 'fill' / 'connect' / 'listen'
  score, rightCount, wrongCount,
  durationSec,
});

RecordService 内部检测 wrongCount > 0 || score < 100,自动派生错题到 WrongQuestionService。一处入口,4 种模式共用。

4.6 等级 + 成就 + 打卡的 @Computed 派生

所有"统计型"数据都不存独立状态,全部 @Computed 派生

@Computed
get stat(): StudyStat {
  const _v = this.favVersion;   // 依赖 favVersion 触发重算
  return RecordService.stat();
}

@Computed
get levelInfo(): LevelInfo {
  return levelOf(this.stat.totalPoems);
}

这样确保:背诵完成 → RecordService 更新 → favVersion++ → @Computed 重算 → 等级/成就 UI 自动刷新。


5、完整数据流分析

以「冷启 → 选诗 → 选模式背诵 → 答错派生错题 → 返回首页看进度」为例:

冷启
    │
    ▼
EntryAbility.onCreate
    └─ WindowStage.loadContent('pages/Index')
            │
            ▼
    Index.aboutToAppear
            ├─ FavoriteService.load() / RecordService.load() / WrongQuestionService.load() / PlanService.load()
            └─ pathStack 初始化、avatarRef 读取
                    │
                    ▼
            MainTabsPage 显示 → HomeView 默认 active
─────────────────────────────────────────────────────────────────
用户点首页「今日一首」快捷入口
    └─ pathStack.pushPathByName(RouteName.PoemDetail, poemId)
            │
            ▼
    PoemDetailPage.aboutToAppear
            └─ this.poem = PoemService.byId(poemId)
                    ↓ 展示「夏日绝句」+ 注释/译文/赏析/作者 + 3 动作按钮
─────────────────────────────────────────────────────────────────
用户点「开始背诵」
    └─ pathStack.pushPathByName(RouteName.ReciteMode, poemId)
            │
            ▼
    ReciteModePage 显示 4 种模式 → 用户点「选择题」
            └─ pathStack.pushPathByName(RouteName.ReciteChoice, poemId)
─────────────────────────────────────────────────────────────────
ReciteChoicePage 答题
    └─ 用户选 B(错) → 第 1 题答错记录 wrongCount++
            ↓
    答完 3 道 → RecordService.add({ type: 'recite', mode: 'choice', wrongCount: 1, ... })
            ├─ Preferences 落盘
            └─ 内部自动派生:WrongQuestionService.addBatch([{ poemId, mode, prompt, userAnswer, correctAnswer }])
                    ├─ 同首诗+同 mode+同 prompt 已存在 → 更新(计数 +1)
                    └─ 不存在 → 新增 + mastered=false
                            ↓
                    Preferences 落盘
─────────────────────────────────────────────────────────────────
用户返回 ReciteMode → 返回 PoemDetail → 返回 Home
    └─ 每次 onShown → 触发 @Computed 重算 stat / levelInfo
            ↓
    HomeView 顶部「童生 → 秀才」进度条更新
    RecordView 错题本数从 0 → 1
    AchievementsPage 背诵篇「背诵达人 3/50 → 4/50」更新

观察点:

  1. 冷启一次性 load 4 个 Service:保证后续读取全部命中内存 cache,UI 渲染零等待。
  2. 答错自动派生RecordService.add 内部检测 wrongCount > 0 自动写错题,业务方完全无感。
  3. 复合 key 去重:同首诗反复答错同一道题不会堆积,而是更新 lastWrongAt 时间戳。
  4. @Computed 全链路联动:1 次 RecordService.add 触发 N 个派生值重算,但每个值只算一次。

6、6 大技术亮点速览

6.1 凹槽凸起底部 Tab 栏(原生 ArkUI 手写)

5 个 Tab:左 2 + 中央凹槽凸起 + 右 2。中央"学习"按钮白色圆形 + 阴影 + 上移制造凸起视觉,凹槽用 Shape + Path SVG 路径切割。

详见第 1 篇文章

6.2 Navigation 路由架构 + 跨页状态同步

Navigation 根 + 13 个 NavDestination + onShown 回调 + favVersion 版本号让收藏态从详情页同步回首页 PoemCard。

详见第 2 篇文章

6.3 7 类弹窗在一个项目里

Toast / Popup / Menu / showDialog / showActionSheet / showActionMenu / showTextPickerDialog / openCustomDialog / bindSheet / bindContentCover / OverlayManager —— 全在 wqsy 里用到了不同业务场景。

详见第 3 篇文章

6.4 错题本自动派生 + 题级去重 + 软删除

WrongQuestionServicepoemId + mode + prompt 复合 key 去重,mastered=true 软删除(保留审计字段)。

详见第 4 篇文章

6.5 5 段等级 + 8 大成就 + 连续打卡

童生→秀才→举人→进士→诗仙 5 段进阶。8 大成就 4 个分类。连续天数算法基于「最近一次打卡时间 - 今天 ≤ 1 天」递推。

详见第 5 篇文章

6.6 零权限头像三合一

卡通预设(cartoon:libai)+ 系统相册 PhotoViewPicker + 系统相机 cameraPicker,全程零权限申请,鸿蒙隐私模型最佳实践。

→ 后续文章


7、上架真实经验

7.1 审核反馈与修复

第一次上架收到 3 个审核 issue:

审核问题 根因 修复
❌ 图标不符合 UX 规范 EntryAbility 用 $media:foreground 而非 $media:layered_image 改成分层图标 + 补 background.png + 1024×1024
❌ 错题本移除后未实时更新 V2 数组重赋值依赖 Service 引用变化 立即本地 filter + [...spread] 双保险刷新
❌ 我的收藏取消后未实时更新 同上 同上模式修复 PoemListPage
❌ 意见反馈即将开放 该入口未实装功能 新建 FeedbackPage + Preferences 持久化

修复后 versionName 1.0.1 → 1.0.2,二次提审通过。

7.2 性能保障

  • 首屏加载 < 500ms:4 个 Service 冷启并发 load
  • 列表滚动稳 60fps:诗词列表用 LazyForEach + key=id
  • 暗色模式适配resources/dark/element/color.json 全套色表

8、总结

「古诗学习宝」从 0 到 1 用了约一个月业余时间,基于 HarmonyOS 6 + ArkUI V2 原生实现,关键能力均走系统 Kit:

  1. 完整产品形态:5 大 Tab + 13 子页面 + 4 种背诵模式 + 错题本 + 打卡 + 等级 + 成就 + 收藏 + 头像系统 + 意见反馈,277 首小学必背古诗全收录

  2. 技术栈以系统 Kit 为主@ComponentV2 状态管理 + Navigation 路由 + Preferences 持久化 + photoAccessHelper 选图,关键能力全部走系统 Kit,源码即贴即用。

  3. 架构清晰可扩展:「视图 + Service 单例 + 持久化」三层 + @Computed 派生算法,每个 Service 单一职责,跨页面状态唯一源——任何新模块(笔记 / 群学习 / AI 翻译 / 服务卡片)都可在此架构上 30 分钟集成。

  4. 古典美学设计语言:宣纸色 #F9F7F2 + 墨绿 #436444 + 朱砂 #B35C5C + serif 字体 + 国画素材,从颜色到字体到布局都贴合古诗词的氛围。

  5. 真实上架经验:审核 3 个 issue 全部修复 + 性能保障 + 暗色模式 + 隐私模型,已上架 App 市场

  6. 6 大技术亮点值得单独成篇:凹槽 TabBar / Navigation 跨页同步 / 7 类弹窗选型 / 错题本派生 / 等级成就 / 零权限头像 —— 接下来会分篇详解。


🎁 下载体验

**「古诗学习宝」**已上架华为应用市场,搜索 古诗学习宝 即可下载。

  • ✅ 完全免费 / 零广告 / 零内购
  • ✅ 277 首小学必背古诗全收录,1~6 年级同步课本
  • ✅ 4 种背诵模式 + 错题本 + 打卡激励
  • ✅ 宣纸色古典美学,专为小学生设计

如果觉得对孩子学习古诗有帮助,烦请帮忙点个五星 🌟,您的好评是我持续投入的最大动力。也欢迎在评论区留下使用反馈或想看的技术细节,我会优先回复并改进。

📚 文中所有截图均来自真机运行,源码托管中(关注作者获取)。

👋 鸿蒙开发者朋友,如果你也在做教育 / 工具类应用,欢迎评论区交流,互相学习。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐