10项目总结与优化方向-鸿蒙PC端Electron开发
本文总结了开源鸿蒙PC社区英语学习项目的架构与技术实现。项目采用分层架构设计,包含UI层、业务逻辑层、数据层和系统能力层,核心功能包括手写识别、OCR接入和单词学习。技术栈涵盖ArkUI框架、ArkTS语言、Canvas绘图、百度OCR API等。文章回顾了关键设计决策,如Stack三层结构优化手写体验,以及touchMove事件处理提升性能。项目已完成10篇系列教程,提供完整源码和优化方向参考。
欢迎加入开源鸿蒙 PC社区
https://harmonypc.csdn.net/
效果截图

第10篇:项目总结与优化方向
系列教程导航
| 篇号 | 标题 | 状态 |
|---|---|---|
| 01 | 环境搭建与项目创建 | ✅ 已完成 |
| 02 | 数据模型与单词仓库 | ✅ 已完成 |
| 03 | 主入口页面与导航结构 | ✅ 已完成 |
| 04 | 极速划词页面实现 | ✅ 已完成 |
| 05 | 手写画布实现 | ✅ 已完成 |
| 06 | 百度OCR手写识别接入 | ✅ 已完成 |
| 07 | 答案比对与反馈UI | ✅ 已完成 |
| 08 | 单词切换与底部导航 | ✅ 已完成 |
| 09 | 词根分解与水印展示 | ✅ 已完成 |
| 10 | 项目总结与优化方向 | 📖 本篇(完结) |
源码仓库:https://gitcode.com/qq_33247427/englishProject
一、项目架构总览
1.1 目录结构
HarmonyElectronProject/libelectron/ohos_hap/
├── AppScope/ # 应用级配置
│ ├── app.json5 # 应用信息(包名、版本)
│ └── resources/base/media/ # 应用图标
├── electron/ # 主模块
│ ├── src/main/ets/ # ArkTS 业务源码
│ │ ├── entryability/ # Ability 入口
│ │ │ └── EntryAbility.ets
│ │ ├── models/ # 数据模型层
│ │ │ └── VocabularyWord.ets
│ │ ├── data/ # 数据仓库层
│ │ │ ├── SpeedWordRepository.ets
│ │ │ └── HandwritingWordRepository.ets
│ │ ├── services/ # 服务层
│ │ │ └── BaiduOCRService.ets
│ │ ├── components/ # 可复用组件
│ │ │ └── HandwritingCanvas.ets
│ │ ├── pages/ # 页面层
│ │ │ ├── NativeListPage.ets
│ │ │ ├── SpeedVocabPage.ets
│ │ │ ├── DictationPage.ets
│ │ │ └── Index.ets
│ │ └── utils/ # 工具类
│ │ └── CanvasManager.ets
│ ├── src/main/resources/ # 模块资源
│ └── src/main/module.json5 # 模块配置(权限声明)
├── build-profile.json5 # 构建配置
├── hvigorfile.ts # 构建脚本
└── oh-package.json5 # 依赖管理
1.2 分层架构图
┌─────────────────────────────────────────────────────┐
│ UI 层 (ArkUI) │
│ │
│ pages/ components/ @Builder │
│ NativeListPage HandwritingCanvas WordPartsRow │
│ SpeedVocabPage │
│ DictationPage │
├─────────────────────────────────────────────────────┤
│ 业务逻辑层 │
│ │
│ 状态管理 比对逻辑 切换逻辑 │
│ @State/@Link normalize() prevWord() │
│ @Prop/@Watch checkAnswer() nextWord() │
├─────────────────────────────────────────────────────┤
│ 数据层 │
│ │
│ data/ services/ │
│ SpeedWordRepository BaiduOCRService │
│ HandwritingWordRepository │
├─────────────────────────────────────────────────────┤
│ 系统能力层 │
│ │
│ Canvas 2D componentSnapshot @kit.NetworkKit │
│ TouchEvent @kit.ImageKit Preferences │
└─────────────────────────────────────────────────────┘
1.3 数据流向
用户手写 → TouchEvent → Canvas 绘制笔迹
↓
点击识别 → componentSnapshot 截图 → PixelMap
↓
BaiduOCRService → HTTP 请求 → 百度 API
↓
识别结果 → normalize() → checkAnswer()
↓
更新 @State → UI 自动刷新(反馈浮层)
二、技术栈总结表
| 技术领域 | 具体技术 | 用途 | 所在篇章 |
|---|---|---|---|
| UI 框架 | ArkUI (声明式) | 页面布局、组件渲染 | 全系列 |
| 编程语言 | ArkTS | 业务逻辑、类型安全 | 全系列 |
| 画布绑图 | Canvas 2D API | 手写笔迹绘制 | 第 5 篇 |
| 组件截图 | componentSnapshot | 将画布导出为图片 | 第 6 篇 |
| 网络请求 | @kit.NetworkKit (http) | 调用百度 OCR API | 第 6 篇 |
| 图片处理 | @kit.ImageKit (image) | PixelMap → Base64 转换 | 第 6 篇 |
| OCR 识别 | 百度手写文字识别 API | 手写内容转文字 | 第 6 篇 |
| 状态管理 | @State / @Link / @Prop | 响应式 UI 更新 | 第 7、8 篇 |
| 页面路由 | @kit.ArkUI (router) | 页面间导航跳转 | 第 3 篇 |
| 数据模式 | Repository Pattern | 数据层抽象 | 第 2 篇 |
三、核心设计决策回顾
3.1 为什么用 Stack 三层结构?
Stack({ alignContent: Alignment.TopStart }) {
Column().backgroundColor('#FFFFFF') // 白色底层
Column() { /* 水印内容 */ } // 水印层
Canvas(this.canvasContext) // 手写层
.backgroundColor(Color.Transparent)
// 反馈浮层、工具栏...
}
问题:如果 Canvas 有白色背景,会遮住水印;如果没有白底,OCR 截图是透明的,识别失败。
解决:三层分离——白底保证截图对比度,透明 Canvas 让水印可见,水印层提供书写参考。
3.2 为什么每次 touchMove 都 beginPath?
// 每次 Move 事件
this.canvasContext.beginPath();
this.canvasContext.moveTo(lastX, lastY);
this.canvasContext.lineTo(currentX, currentY);
this.canvasContext.stroke();
问题:如果不 beginPath,每次 stroke 会重绘整条路径,笔画越多帧率越低。
解决:每次只绘制最新一小段线条(从上一个点到当前点),保持恒定的绘制性能。
3.3 为什么选择百度 OCR 而不是端侧 CoreVisionKit?
| 对比项 | 百度手写 OCR | 华为 CoreVisionKit |
|---|---|---|
| 稳定性 | ✅ 高(云端服务) | ⚠️ 部分设备超时 |
| 识别准确率 | ✅ 高(专门的手写模型) | ⚠️ 通用 OCR,手写效果一般 |
| 网络依赖 | ❌ 需要联网 | ✅ 端侧离线 |
| 延迟 | ~500ms | ~200ms |
| 免费额度 | 每天 500 次 | 无限制 |
决策:学习场景下网络通常可用,稳定性和准确率优先。百度手写 OCR 的免费额度(500次/天)对个人学习完全够用。
3.4 为什么用 Repository 模式?
// UI 层只依赖接口,不关心数据来源
private repository: SpeedWordRepository = new SpeedWordRepository();
this.words = this.repository.getWordsByDate('3/12');
好处:
- 当前用硬编码数据,开发快
- 将来换数据库或网络 API,UI 层零修改
- 方便单元测试(可以 mock Repository)
四、主题色体系
4.1 完整调色板
| 分类 | 色名 | 色值 | 用途 |
|---|---|---|---|
| 主题色 | primary | #8B9D6B |
主按钮、开关、强调 |
| primary-dark | #6F7F52 |
按钮按下态 | |
| primary-light | #B6C496 |
次要强调 | |
| primary-surface | #EEF1E4 |
词根背景、卡片底色 | |
| 中性色 | text-primary | #1A1A1A |
标题文字 |
| text-body | #374151 |
正文文字 | |
| text-secondary | #6B7280 |
辅助文字 | |
| text-muted | #9CA3AF |
弱化文字(音标) | |
| text-disabled | #D1D5DB |
禁用文字 | |
| border | #E5E7EB |
边框线 | |
| border-light | #F3F4F6 |
禁用边框 | |
| background | #FAFAF7 |
页面背景 | |
| surface | #FFFFFF |
卡片/按钮背景 | |
| 语义色 | success | #5B8A4A |
正确反馈 |
| error | #B5533C |
错误反馈 | |
| warning | #C9A14A |
警告提示 | |
| 词根色 | prefix-bg | #FFEBEE |
前缀背景 |
| prefix-text | #E57373 |
前缀文字 | |
| root-bg | #EEF1E4 |
词根背景 | |
| root-text | #8B9D6B |
词根文字 | |
| suffix-bg | #DBEAFE |
后缀背景 | |
| suffix-text | #64B5F6 |
后缀文字 | |
| 水印色 | watermark-word | #E8EDE0 |
水印单词 |
| watermark-phonetic | #D0D8C8 |
水印音标 |
4.2 设计理念
整体色调以**鼠尾草绿(Sage Green)**为主题色,传达自然、舒适、专注的学习氛围。中性色采用暖灰色系(带微黄),避免冷灰的生硬感。背景色 #FAFAF7 是带暖调的近白色,长时间阅读不疲劳。
五、功能增强方向
以下是我们规划的功能增强方向。这些功能按照优先级排列,建议先实现对用户学习效果提升最大的功能(如错题本和艾宾浩斯复习),再逐步完善体验类功能(如动画和深色模式)。每个功能都可以作为一个独立的迭代版本来开发和发布。
5.1 TTS 语音播放
接入系统 TTS 能力,点击单词即可朗读:
import { textToSpeech } from '@kit.CoreSpeechKit';
async speakWord(word: string): Promise<void> {
const ttsEngine = await textToSpeech.createEngine({
language: 'en-US',
person: 0, // 默认发音人
online: 1 // 在线模式(发音更自然)
});
await ttsEngine.speak(word);
}
5.2 错题本
记录每次识别错误的单词,支持重点复习:
interface MistakeRecord {
wordId: string;
wrongAnswer: string; // 用户写错的内容
timestamp: number; // 错误时间
reviewCount: number; // 复习次数
}
5.3 艾宾浩斯复习曲线
根据遗忘曲线安排复习计划:
| 复习次数 | 间隔时间 | 记忆保持率 |
|---|---|---|
| 第 1 次 | 20 分钟后 | ~58% |
| 第 2 次 | 1 小时后 | ~44% |
| 第 3 次 | 1 天后 | ~34% |
| 第 4 次 | 2 天后 | ~28% |
| 第 5 次 | 7 天后 | ~25% |
| 第 6 次 | 30 天后 | ~21% |
5.4 键盘输入模式
除了手写,支持键盘直接输入:
TextInput({ placeholder: '请输入单词...' })
.type(InputType.Normal)
.fontSize(20)
.fontColor('#374151')
.onChange((value: string) => {
this.typedText = value;
})
.onSubmit(() => {
this.checkAnswer(this.typedText);
})
5.5 学习统计图表
使用 ArkUI 的绘图能力展示学习数据:
- 每日学习量柱状图
- 正确率折线图
- 连续学习天数
- 累计掌握单词数
六、性能优化
性能优化是应用从"能用"到"好用"的关键一步。在单词学习应用中,手写画布的流畅度直接影响用户的书写体验,而网络请求的速度则影响识别反馈的及时性。以下是几个重要的性能优化方向,每一个都能显著提升用户体验。
6.1 OffscreenCanvas 离屏渲染
将笔迹绘制移到离屏 Canvas,减少主线程压力:
// 创建离屏 Canvas
const offscreen = new OffscreenCanvas(width, height);
const offCtx = offscreen.getContext('2d');
// 在离屏 Canvas 上绘制
offCtx.beginPath();
offCtx.moveTo(lastX, lastY);
offCtx.lineTo(currentX, currentY);
offCtx.stroke();
// 将离屏内容绘制到主 Canvas
this.canvasContext.drawImage(offscreen, 0, 0);
6.2 图片压缩
OCR 前压缩 PixelMap,减少上传时间和流量:
// 获取原始 PixelMap 信息
const imageInfo = await pixelMap.getImageInfo();
const originalWidth = imageInfo.size.width;
const originalHeight = imageInfo.size.height;
// 如果图片过大,缩放到合理尺寸
const maxSize = 1024;
if (originalWidth > maxSize || originalHeight > maxSize) {
const scale = maxSize / Math.max(originalWidth, originalHeight);
await pixelMap.scale(scale, scale);
}
6.3 Token 持久化(Preferences)
百度 OCR 的 Access Token 有效期 30 天,不需要每次都重新获取:
import { preferences } from '@kit.ArkData';
async getToken(): Promise<string> {
const store = await preferences.getPreferences(getContext(this), 'ocr_config');
const savedToken = await store.get('access_token', '') as string;
const expireTime = await store.get('token_expire', 0) as number;
// Token 未过期,直接使用
if (savedToken.length > 0 && Date.now() < expireTime) {
return savedToken;
}
// Token 过期或不存在,重新获取
const newToken = await this.fetchTokenFromServer();
await store.put('access_token', newToken);
await store.put('token_expire', Date.now() + 29 * 24 * 3600 * 1000); // 29天后过期
await store.flush();
return newToken;
}
6.4 LazyForEach 懒加载
单词列表较长时,使用 LazyForEach 替代 ForEach,只渲染可见区域:
// 需要实现 IDataSource 接口
class WordDataSource implements IDataSource {
private words: VocabularyWord[] = [];
totalCount(): number {
return this.words.length;
}
getData(index: number): VocabularyWord {
return this.words[index];
}
// ... registerDataChangeListener, unregisterDataChangeListener
}
// 使用 LazyForEach
LazyForEach(this.wordDataSource, (word: VocabularyWord) => {
ListItem() {
WordCard({ word: word })
}
}, (word: VocabularyWord) => word.id)
七、体验优化
7.1 手势滑动切换
左右滑动切换单词,比点击按钮更自然:
.gesture(
PanGesture({ direction: PanDirection.Horizontal })
.onActionEnd((event: GestureEvent) => {
if (event.offsetX > 100) {
this.prevWord(); // 右滑 → 上一个
} else if (event.offsetX < -100) {
this.nextWord(); // 左滑 → 下一个
}
})
)
7.2 动画反馈
正确/错误时添加缩放动画,增强反馈感:
@State feedbackScale: number = 1.0;
// 正确时的弹跳动画
animateTo({ duration: 200, curve: Curve.EaseOut }, () => {
this.feedbackScale = 1.2;
});
animateTo({ duration: 150, curve: Curve.EaseIn, delay: 200 }, () => {
this.feedbackScale = 1.0;
});
7.3 深色模式适配
响应系统深色模式切换:
// 获取当前颜色模式
const colorMode = getContext(this).config.colorMode;
// 根据模式选择颜色
const bgColor = colorMode === ColorMode.DARK ? '#1A1A1A' : '#FAFAF7';
const textColor = colorMode === ColorMode.DARK ? '#E5E7EB' : '#374151';
7.4 横竖屏适配
鸿蒙 PC 和平板可能横屏使用,需要响应式布局:
// 监听窗口尺寸变化
@StorageProp('windowWidth') windowWidth: number = 0;
build() {
if (this.windowWidth > 840) {
// 宽屏:左右分栏布局
Row() {
// 左侧:单词列表
// 右侧:手写画布
}
} else {
// 窄屏:上下布局
Column() {
// 上方:提示信息
// 下方:手写画布
}
}
}
八、数据层升级
数据层的升级是应用从单机版走向完整产品的必经之路。当前我们使用硬编码数据,虽然开发效率高,但无法满足用户自定义词库、学习记录持久化、多设备同步等需求。以下是三个主要的数据层升级方向,可以根据产品规划分阶段实施。
8.1 RDB 关系型数据库
使用鸿蒙的关系型数据库存储学习记录:
import { relationalStore } from '@kit.ArkData';
// 创建数据库和表
const SQL_CREATE_TABLE = `
CREATE TABLE IF NOT EXISTS learning_records (
id INTEGER PRIMARY KEY AUTOINCREMENT,
word_id TEXT NOT NULL,
is_correct INTEGER NOT NULL,
recognized_text TEXT,
timestamp INTEGER NOT NULL
)
`;
const store = await relationalStore.getRdbStore(getContext(this), {
name: 'vocabulary.db',
securityLevel: relationalStore.SecurityLevel.S1
});
await store.executeSql(SQL_CREATE_TABLE);
8.2 云同步
多设备数据同步,让用户在手机和平板上都能学习:
// 使用分布式数据管理
import { distributedKVStore } from '@kit.ArkData';
const kvManager = distributedKVStore.createKVManager({
bundleName: 'com.example.vocabulary'
});
const kvStore = await kvManager.getKVStore('learning_data', {
createIfMissing: true,
encrypt: false,
backup: true,
autoSync: true // 自动同步到其他设备
});
8.3 导入导出词库
支持用户导入自定义词库(CSV/JSON 格式):
// 从文件选择器导入
import { picker } from '@kit.CoreFileKit';
async importWordList(): Promise<void> {
const documentPicker = new picker.DocumentViewPicker();
const result = await documentPicker.select({
fileSuffixFilters: ['.json', '.csv']
});
if (result.length > 0) {
const fileUri = result[0];
// 读取文件内容并解析...
}
}
九、发布准备
当应用开发完成并经过充分测试后,就可以准备发布到华为应用市场了。发布流程涉及签名证书申请、隐私合规声明、应用图标设计和市场审核等多个环节。建议在开发后期就开始准备这些材料,避免最后阶段手忙脚乱。
9.1 签名证书
发布到华为应用市场需要正式签名证书:
- 登录 AppGallery Connect
- 创建应用 → 获取 App ID
- 申请发布证书(.p12 + .cer)
- 在 DevEco Studio 中配置签名
9.2 隐私声明
本应用使用了网络权限(调用百度 OCR),需要在隐私声明中说明:
// module.json5
{
"requestPermissions": [
{
"name": "ohos.permission.INTERNET",
"reason": "$string:internet_permission_reason",
"usedScene": {
"abilities": ["EntryAbility"],
"when": "inuse"
}
}
]
}
9.3 应用图标
华为应用市场要求提供以下尺寸的图标:
| 用途 | 尺寸 | 格式 |
|---|---|---|
| 应用图标 | 216×216 px | PNG(圆角自动裁切) |
| 市场展示 | 1024×1024 px | PNG |
| 通知栏图标 | 72×72 px | PNG(单色) |
9.4 华为应用市场提交流程
- 在 AppGallery Connect 创建应用
- 填写应用信息(名称、描述、截图)
- 上传签名后的 HAP/APP 包
- 提交审核(通常 1-3 个工作日)
- 审核通过后上架
十、最终文件清单
项目所有关键文件一览:
✅ electron/src/main/ets/models/VocabularyWord.ets — 数据模型
✅ electron/src/main/ets/data/SpeedWordRepository.ets — 极速划词数据仓库
✅ electron/src/main/ets/data/HandwritingWordRepository.ets — 手写练习数据仓库
✅ electron/src/main/ets/services/BaiduOCRService.ets — 百度 OCR 服务
✅ electron/src/main/ets/components/HandwritingCanvas.ets — 手写画布组件
✅ electron/src/main/ets/pages/NativeListPage.ets — 主入口页面
✅ electron/src/main/ets/pages/SpeedVocabPage.ets — 极速划词 + 默写
✅ electron/src/main/ets/pages/DictationPage.ets — 独立默写页面
✅ electron/src/main/ets/pages/Index.ets — 手写详情页
✅ electron/src/main/ets/utils/CanvasManager.ets — Canvas 管理工具
✅ electron/src/main/module.json5 — 模块配置(含权限)
✅ electron/src/main/resources/base/profile/main_pages.json — 路由注册
✅ build-profile.json5 — 构建配置
✅ oh-package.json5 — 依赖管理
十一、系列结语
经过这 10 篇教程,我们从零搭建了一个基于 HarmonyOS NEXT 的单词学习应用,完整覆盖了以下技术领域:
这个系列教程的目标不仅仅是教会你如何实现一个具体的应用,更重要的是让你掌握鸿蒙应用开发的思维方式和最佳实践。从数据模型设计到组件封装,从状态管理到性能优化,每一个环节都体现了现代应用开发的工程化思想。希望通过这个完整的项目实战,你能够建立起对鸿蒙开发的系统性认知,并将这些知识迁移到自己的项目中去。
| 篇章 | 核心技能 |
|---|---|
| 第 1 篇 | 开发环境搭建、项目结构理解 |
| 第 2 篇 | 数据模型设计、Repository 模式 |
| 第 3 篇 | 页面路由、导航结构 |
| 第 4 篇 | 列表渲染、Tab 导航、卡片组件 |
| 第 5 篇 | Canvas 2D 绑图、触摸事件处理 |
| 第 6 篇 | HTTP 网络请求、图片处理、OCR 接入 |
| 第 7 篇 | 文本比对、浮层 UI、颜色语义系统 |
| 第 8 篇 | 状态管理、导航交互、布局间距 |
| 第 9 篇 | 条件渲染、组件复用、Canvas 绘图 |
| 第 10 篇 | 架构总结、优化方向、发布准备 |
这个项目虽然功能不复杂,但涵盖了鸿蒙应用开发的核心知识点:声明式 UI、状态管理、网络请求、Canvas 绑图、组件封装、数据层设计。掌握这些基础后,你可以轻松扩展到更复杂的应用场景。无论是社交应用、电商平台还是工具类软件,底层的开发模式和架构思想都是相通的。
鸿蒙生态正在快速发展,越来越多的设备(手机、平板、PC、车机、穿戴)都将运行 HarmonyOS NEXT。作为开发者,现在入场学习鸿蒙开发正是最好的时机。华为正在大力推动鸿蒙原生应用的生态建设,各大互联网公司也在积极适配鸿蒙平台,市场对鸿蒙开发人才的需求持续增长。希望这个系列教程能够成为你鸿蒙开发之旅的起点,祝你在鸿蒙生态中找到属于自己的机会。
更多推荐




所有评论(0)