从零开发「兰亭序」阅读器




鸿蒙 ArkTS 实战:从零开发「兰亭序」阅读器 —— 基于 API 24 (HarmonyOS 6.1.1) 的完整开发指南
一、引言
1.1 为什么选择「兰亭序」作为入门项目
在鸿蒙生态快速发展的今天,越来越多的开发者开始接触 HarmonyOS 应用开发。但对于初学者来说,最困难的事情往往不是学习语法,而是找到一个合适的入手项目——既不能太简单(只写个 Hello World 学不到东西),也不能太复杂(从电商App做起显然不现实)。
「兰亭序」阅读器就是这样一个恰到好处的入门项目。它的核心需求非常清晰:展示一篇文言文文本,配以优雅的排版和交互。但看似简单的需求背后,涉及了 HarmonyOS 应用开发的诸多核心知识点:
- ArkTS 语言基础与声明式 UI 编程范式
- ArkUI 组件体系与布局系统
- 资源管理(字符串、颜色、尺寸等)
- 页面结构与生命周期
- 样式与主题设计
- 调试与构建配置
本文将以「兰亭序」阅读器的实际开发过程为主线,系统地讲解 HarmonyOS 应用开发的完整流程,并针对 API 24 (HarmonyOS 6.1.1) 版本中的新特性和最佳实践进行深入分析。
1.2 项目最终效果预览
在开始编码之前,让我们先明确我们要构建什么:
┌─────────────────────────────┐
│ 兰 亭 序 │
│ 王 羲 之 │
│ ───●─── │
│ │
│ 永和九年,岁在癸丑, │
│ 暮春之初,会于会稽山阴 │
│ 之兰亭,修禊事也。 │
│ 群贤毕至,少长咸集。 │
│ ... │
│ │
│ —— 东晋·王羲之 撰并书 │
│ ───────────────── │
│ 「后之览者,亦将有感于 │
│ 斯文」 │
│ · 终 · │
└─────────────────────────────┘
页面主体为纵轴滚动阅读器,仿古书卷风格,背景采用宣纸暖色 #FFF8F0。
二、HarmonyOS API 24 核心认知
2.1 什么是 API 24
在开始编码之前,我们需要先理解 API 24 在 HarmonyOS 版本体系中的位置。
HarmonyOS 版本号映射关系:
| HarmonyOS 版本 | API Level | SDK 版本 | 主要变化 |
|---|---|---|---|
| HarmonyOS 3.0 | API 9 | 3.x.x | 首个稳定版 ArkUI |
| HarmonyOS 4.0 | API 10 | 4.x.x | 增强声明式UI、Stage模型完善 |
| HarmonyOS 4.1 | API 11 | 5.x.x | Added大量新组件 |
| HarmonyOS 5.0 (NEXT) | API 12 | 5.x.x | 全栈自研、去AOSP |
| HarmonyOS 5.5 / 6.x | API 24 | 6.1.1 | 最新稳定版 |
API 24 的关键能力包括:
- Stage 模型全面成熟:从 API 9 引入的 Stage 模型到 API 24 已经非常成熟,Application、Ability、Context 三层架构清晰稳定
- ArkUI 组件体系完整:基础组件、容器组件、媒体组件、画布组件等体系完备
- 声明式 UI 性能优化:状态管理机制的底层优化,@State/@Prop/@Link 装饰器链路的改进
- 包管理能力增强:oh-package 依赖管理更加完善
- 安全能力升级:权限模型更加细化,ACE 安全框架增强
在我们的项目中,build-profile.json5 中的关键配置如下:
{
"app": {
"products": [
{
"targetSdkVersion": "6.1.1(24)",
"compatibleSdkVersion": "6.1.1(24)",
"runtimeOS": "HarmonyOS"
}
]
}
}
targetSdkVersion 设为 6.1.1(24) 意味着我们明确以 API 24 为目标平台,可以利用该版本的所有最新 API 特性。
2.2 Stage 模型概览
API 24 默认使用 Stage 模型(在 entry/build-profile.json5 中通过 "apiType": "stageMode" 指定)。Stage 模型的核心设计理念是 “以 Ability 为最小调度单元,以 Context 为上下文纽带”。
┌─────────────────────────────────────┐
│ Application │
│ ┌──────────────────────────────┐ │
│ │ UIAbility (EntryAbility) │ │
│ │ ┌──────────────────────┐ │ │
│ │ │ WindowStage │ │ │
│ │ │ ┌────────────────┐ │ │ │
│ │ │ │ Window (页面) │ │ │ │
│ │ │ └────────────────┘ │ │ │
│ │ └──────────────────────┘ │ │
│ └──────────────────────────────┘ │
│ ┌──────────────────────────────┐ │
│ │ ExtensionAbility (组件) │ │
│ └──────────────────────────────┘ │
└─────────────────────────────────────┘
在我们的项目中,EntryAbility.ets 就是继承自 UIAbility 的入口 Ability,它在 onWindowStageCreate 回调中加载主页面:
onWindowStageCreate(windowStage: window.WindowStage): void {
windowStage.loadContent('pages/Index', (err) => {
if (err.code) {
hilog.error(DOMAIN, 'testTag', 'Failed: %{public}s', JSON.stringify(err));
return;
}
});
}
三、项目初始化与配置
3.1 使用 DevEco Studio 创建项目
环境要求:
- DevEco Studio 5.0+(建议最新版)
- HarmonyOS SDK API 24
- Node.js 18+(用于 ohpm 包管理)
创建步骤:
- 打开 DevEco Studio →
File→New→Create Project - 选择
Empty Ability模板(基于 Stage 模型) - 配置项目信息:包名、项目名称、保存路径
- SDK 选择 API 24 (HarmonyOS 6.1.1)
- 语言选择 ArkTS
3.2 项目结构分析
创建完成后,项目的核心目录结构如下:
MyApplication/
├── AppScope/ # 应用级配置
│ ├── app.json5 # 应用全局配置(名称、版本、图标等)
│ └── resources/ # 应用级资源
├── entry/ # 模块目录
│ ├── src/main/
│ │ ├── ets/ # ArkTS 源码目录
│ │ │ ├── entryability/ # Ability 入口
│ │ │ │ └── EntryAbility.ets
│ │ │ └── pages/ # 页面
│ │ │ └── Index.ets
│ │ ├── module.json5 # 模块配置(abilities、权限、设备类型等)
│ │ └── resources/ # 资源文件
│ │ ├── base/ # 基础资源(不限方向)
│ │ │ ├── element/ # 字符串、颜色、浮点数等
│ │ │ ├── media/ # 图片等媒体资源
│ │ │ ├── profile/ # 配置(main_pages、backup_config等)
│ │ │ └── rawfile/ # 原生文件
│ │ └── dark/ # 深色模式资源
│ ├── build-profile.json5 # 模块构建设置
│ ├── oh-package.json5 # ohpm 依赖声明
│ └── hvigorfile.ts # Hvigor 构建脚本
├── build-profile.json5 # 应用级构建设置
├── hvigor/ # 构建工具配置
├── hvigorfile.ts # 根构建脚本
├── oh-package.json5 # 根依赖
└── oh_modules/ # 依赖安装目录
3.3 关键配置文件详解
build-profile.json5(应用级) 是项目中最重要的配置文件,它决定了应用的目标平台、签名方式和模块结构。
{
app: {
signingConfigs: [], // 签名配置
products: [
{
name: "default",
signingConfig: "default",
targetSdkVersion: "6.1.1(24)", // 目标 SDK
compatibleSdkVersion: "6.1.1(24)", // 兼容 SDK
runtimeOS: "HarmonyOS", // 运行 OS
buildOption: {
strictMode: {
caseSensitiveCheck: true, // 大小写检查
useNormalizedOHMUrl: true // URL 规范化
}
}
}
]
},
modules: [] // 模块列表
}
module.json5 则声明了模块的具体能力:
{
module: {
name: "entry",
type: "entry", // entry: 应用入口模块
deviceTypes: ["phone"], // 支持的设备类型
pages: "$profile:main_pages", // 页面配置引用
abilities: [ // Ability 声明
{
name: "EntryAbility",
srcEntry: "./ets/entryability/EntryAbility.ets",
exported: true,
skills: [
{
entities: ["entity.system.home"],
actions: ["ohos.want.action.home"]
}
]
}
]
}
}
3.4 页面路由配置
页面路由在 resources/base/profile/main_pages.json 中声明:
{
"src": [
"pages/Index"
]
}
当需要多页面时,只需在此数组中追加新的页面路径即可。例如:
{
"src": [
"pages/Index",
"pages/Detail",
"pages/About"
]
}
四、ArkTS 语言基础:从 TypeScript 到 ArkTS
4.1 ArkTS 与 TypeScript 的关系
ArkTS 是 HarmonyOS 原生开发语言,它基于 TypeScript 进行了定制和增强。理解二者的关系对快速上手至关重要:
| 方面 | TypeScript | ArkTS |
|---|---|---|
| 类型系统 | 渐进式类型,any 可用 |
严格类型,禁用 any |
| 装饰器 | 标准装饰器 | 增强装饰器(@State、@Prop、@Link 等) |
| 运行时 | JavaScript 引擎 | 方舟编译器直接编译为机器码 |
| UI 描述 | 无 | 声明式 UI(@Component + build) |
| 状态管理 | 无内置机制 | @State / @Prop / @Link / @Provide / @Consume |
| 并发模型 | 单线程 + Worker | TaskPool / Actor 并发模型 |
| null 安全 | 可选 strictNullChecks | 内置空安全 |
| 模块系统 | ES Module | ES Module + ohpm |
ArkTS 的严格性约束是开发者需要特别注意的:
// ❌ TypeScript 中可以这样做,但 ArkTS 不允许
let data: any = getData(); // 错误:不支持 any 类型
data.someMethod();
// ✅ ArkTS 中必须明确类型
interface DataType {
value: string;
}
let data: DataType = getData() as DataType;
4.2 声明式 UI 的核心范式
ArkTS 采用声明式 UI 编程范式。这与传统的命令式 UI(如 Android 的 XML + Java/Kotlin)有本质区别。
命令式编程(伪代码):
// 创建元素 → 设置属性 → 添加到父容器
let text = new TextWidget()
text.setText("Hello")
text.setFontSize(20)
let container = new Column()
container.addChild(text)
rootView.addChild(container)
声明式编程(ArkTS):
build() {
Column() {
Text("Hello")
.fontSize(20)
}
}
声明式 UI 的核心优势是:开发者只描述"界面应该长什么样",框架负责计算"如何从状态A变到状态B"。
4.3 @Component 和 @Entry 装饰器
在 ArkTS 中,一个可复用的 UI 单元通过 @Component 装饰器声明:
@Component
struct MyComponent {
build() {
// UI 描述
}
}
@Entry 装饰器将组件标记为页面的入口:
@Entry
@Component
struct Index {
build() {
// 页面 UI
}
}
关键区别:
@Component:声明一个可复用的 UI 组件@Entry:声明该组件是一个页面入口(只能用在页面根组件上)
每个组件必须实现 build() 方法,该方法返回组件的 UI 描述。
4.4 @State 装饰器与状态管理
@State 是 ArkTS 中最常用的装饰器,用于声明组件的内部状态。当状态变量发生变化时,框架会自动重新渲染依赖该状态的 UI 部分。
@Component
struct Counter {
@State count: number = 0;
build() {
Column() {
Text(`计数: ${this.count}`)
.fontSize(24)
Button("增加")
.onClick(() => {
this.count++; // 修改状态,UI 自动更新
})
}
}
}
在我们的兰亭序应用中,虽然目前只有一个页面且没有复杂状态,但理解状态管理仍然是开发更复杂功能的基础。比如后续可以扩展为:
@Component
struct LantingxuReader {
@State fontSize: number = 20; // 字体大小
@State lineHeight: number = 40; // 行高
@State backgroundColor: string = '#FFF8F0'; // 背景色
build() {
// 根据状态动态调整排版
}
}
@State 的触发规则:
- 基础类型(number、string、boolean):赋值即触发
- 对象类型:属性的直接修改不触发,需要整体赋值
- 数组:push/pop 不触发,需要重新赋值或使用展开运算符
五、ArkUI 组件体系:构建阅读器界面
5.1 布局容器选择
ArkUI 提供了多种布局容器,正确选择是构建优雅界面的第一步:
| 容器 | 特点 | 适用场景 |
|---|---|---|
Column |
垂直排列 | 纵向滚动内容、表单 |
Row |
水平排列 | 导航栏、头像+文字 |
Stack |
层叠排列 | 叠加图层、居中定位 |
Flex |
弹性布局 | 均匀分布、自适应 |
Grid |
网格布局 | 照片墙、卡片列表 |
RelativeContainer |
相对定位 | 精确锚点对齐 |
在我们的阅读器中,使用了 三层嵌套布局:
Stack(最外层:层叠背景 + 前景内容)
└── Column(背景层:纯色背景)
└── Scroll(滚动层)
└── Column(内容层:标题+正文+底部)
├── Column(标题区域)
│ ├── Text(主标题)
│ ├── Text(副标题)
│ └── Row(装饰分隔线)
│ ├── Line
│ ├── Text(●)
│ └── Line
├── Text(正文)
├── Text(底部署名)
├── Divider(分隔线)
├── Text(引用)
└── Text(终)
选择 Stack 作为最外层容器,是因为我们需要叠加一个纯色背景层(Column)和前景内容层(Scroll)。这样即使内容不足一屏,背景色也能填满整个屏幕。
5.2 Text 组件的全面使用
Text 是阅读器中最核心的组件。ArkUI 的 Text 组件提供了丰富的排版能力:
Text(content: string | Resource)
.fontSize(value: number | string | Resource) // 字号
.fontWeight(value: number | FontWeight) // 字重
.fontColor(value: ResourceColor) // 颜色
.fontStyle(value: FontStyle) // 样式(正常/斜体)
.letterSpacing(value: number) // 字间距
.lineHeight(value: number | string | Resource) // 行高
.textAlign(value: TextAlign) // 对齐方式
.textOverflow({ overflow: TextOverflow }) // 溢出处理
.maxLines(value: number) // 最大行数
.textIndent(value: number) // 首行缩进
在兰亭序阅读器中,我们精心配置了正文排版:
Text(this.lantingxuText)
.fontSize(20) // 字号 20,适合阅读
.fontColor('#3C2415') // 深褐色,仿古籍
.lineHeight(40) // 行高 40,保持阅读呼吸感
.letterSpacing(2) // 字间距 2,避免拥挤
.textAlign(TextAlign.Start) // 左对齐,符合阅读习惯
.width('100%') // 撑满父容器
.padding({ left: 24, right: 24 }) // 左右留白,防止文字贴边
多段文字的处理技巧:
由于 Text 组件不支持富文本(同一个 Text 内不同部分有不同的样式),我们用 \n 换行符来分割段落:
private lantingxuText: string =
'永和九年,岁在癸丑,暮春之初,会于会稽山阴之兰亭,修禊事也。\n' +
'群贤毕至,少长咸集。\n' +
'此地有崇山峻岭,茂林修竹;又有清流激湍,映带左右,引以为流觞曲水,列坐其次。\n' +
'虽无丝竹管弦之盛,一觞一咏,亦足以畅叙幽情。\n\n' +
// ...更多段落
如果后续需要对不同段落应用不同样式,可以将文本拆分为多个独立的 Text 组件放在 Column 中,各自应用不同的样式属性。
5.3 Scroll 组件实现滚动
阅读器需要支持长文本的滚动浏览。Scroll 组件是实现滚动功能的核心:
Scroll() {
Column() {
// 所有内容
}
.width('100%')
}
.width('100%')
.height('100%')
Scroll 组件的关键属性:
Scroll(scroller?: Scroller) // 可选:传入 Scroller 做编程式滚动控制
.scrollable(ScrollDirection.Vertical) // 滚动方向
.scrollBar(BarState.Auto) // 滚动条策略
.friction(0.8) // 摩擦系数
.edgeEffect(EdgeEffect.Spring) // 边缘效果
编程式滚动(进阶用法):
@Component
struct LantingxuReader {
private scroller: Scroller = new Scroller();
build() {
Column() {
Button("回到顶部")
.onClick(() => {
this.scroller.scrollTo({ xOffset: 0, yOffset: 0 })
})
Scroll(this.scroller) {
// 内容
}
}
}
}
5.4 Line 与 Divider 装饰组件
在标题和正文之间,我们使用 Line 组件和 Text 组件组合了一个装饰分隔线:
Row() {
Line()
.width(60)
.height(1)
.backgroundColor('#C4A882')
Text('●')
.fontSize(8)
.fontColor('#C4A882')
.margin({ left: 8, right: 8 })
Line()
.width(60)
.height(1)
.backgroundColor('#C4A882')
}
.margin({ top: 16, bottom: 4 })
Line 组件用于绘制直线段,Divider 组件则用于在水平方向分割区块。注意区分:
- Line:自由绘制直线,可控制起点终点、长度、颜色
- Divider:专用分割线组件,自动撑满父容器宽度
5.5 组件属性链式调用模式
ArkUI 组件的一个显著特点是链式调用。每个组件方法都返回组件本身,可以连续调用:
Text('兰 亭 序')
.fontSize(36) // 返回 Text 组件
.fontWeight(FontWeight.Bold) // 返回 Text 组件
.fontColor('#2C1810') // 返回 Text 组件
.letterSpacing(8) // 返回 Text 组件
.textAlign(TextAlign.Center) // 返回 Text 组件
这种风格非常简洁,但需要注意:
- 属性设置顺序无关 —— 链式调用的顺序不影响最终渲染结果
- 后设置覆盖前设置 —— 如果一个属性被设置多次,以最后一次为准
- 类型安全 —— 编译时检查参数类型,避免传错参数
六、资源管理系统
6.1 资源分类与引用
HarmonyOS 的资源管理系统设计精良,支持多设备、多语言、多方向。
资源目录结构:
resources/
├── base/ # 基础资源(所有设备、所有语言、所有方向适用)
│ ├── element/ # 元素资源(字符串、颜色、尺寸等)
│ │ ├── string.json
│ │ ├── color.json
│ │ └── float.json
│ ├── media/ # 媒体资源(图片、音视频等)
│ ├── profile/ # 配置文件
│ └── rawfile/ # 原始文件
├── dark/ # 深色模式覆盖
│ └── element/
│ └── color.json
├── en_GB/ # 英式英语本地化
├── zh_CN/ # 简体中文本地化
└── zh_HK/ # 繁体中文本地化
资源引用的两种方式:
// 方式一:通过 $r() 引用资源
Text($r('app.string.app_name'))
.fontSize($r('app.float.page_text_font_size'))
.fontColor($r('app.color.start_window_background'))
// 方式二:直接硬编码
Text('兰 亭 序')
.fontSize(36)
.fontColor('#2C1810')
推荐做法: 对于需要多语言支持或可能统一变更的值(如主题色、间距),使用 $r() 资源引用;对于一次性使用的、不会变更的样式(如特定的装饰色),可以硬编码。
6.2 element 资源详细配置
字符串资源(string.json):
{
"string": [
{
"name": "module_desc",
"value": "兰亭序阅读应用"
},
{
"name": "EntryAbility_desc",
"value": "兰亭序 - 王羲之"
},
{
"name": "EntryAbility_label",
"value": "兰亭序"
}
]
}
这些字符串会用在:
module.json5中的description和label字段- 系统设置中的应用名称
- 多任务管理中的应用标题
颜色资源(color.json):
{
"color": [
{
"name": "start_window_background",
"value": "#FFF8F0"
}
]
}
start_window_background 是一个特殊颜色资源,它定义了应用启动时的窗口背景色。将其设为 #FFF8F0 与阅读器界面保持一致,可以消除启动时的白屏闪烁。
Float 值资源(float.json):
{
"float": [
{
"name": "page_text_font_size",
"value": "50fp"
}
]
}
这里的 50fp 是模板默认值,我们在实际开发中直接设置了具体的字号数值,未使用该资源。fp 是 HarmonyOS 的字体单位缩写,与 dp 类似但会跟随系统字体大小设置缩放。
6.3 深色模式支持
在 resources/dark/ 下,我们可以提供深色模式的颜色覆盖:
// resources/dark/element/color.json
{
"color": [
{
"name": "start_window_background",
"value": "#1A1A1A" // 深色模式下用深色背景
}
]
}
这样当系统切换到深色模式时,应用会自动使用深色背景。我们的阅读器要支持深色模式,可以在 dark 目录下覆盖所有颜色值,同时页面中的文字颜色也通过资源引用来定义。
6.4 多语言和本地化实践
在 resources 目录下,我们可以为不同语言创建子目录来实现国际化。例如,要为英文用户提供界面翻译:
resources/
├── base/ # 默认(中文)资源
│ └── element/
│ ├── string.json # 中文文本
│ └── color.json
├── en_US/ # 美式英语
│ └── element/
│ └── string.json # 英文翻译
└── dark/
└── element/
└── color.json
en_US/element/string.json 示例:
{
"string": [
{
"name": "app_title",
"value": "Preface to Orchid Pavilion"
},
{
"name": "app_author",
"value": "Wang Xizhi (303–361)"
},
{
"name": "app_subtitle",
"value": "The Greatest Masterpiece of Chinese Calligraphy"
}
]
}
在代码中,通过 $r() 引用即可自动匹配当前系统语言:
// 中英文模式下自动显示不同的文本
Text($r('app.string.app_title'))
.fontSize(36)
.fontWeight(FontWeight.Bold)
.letterSpacing(8)
这种资源引用方式的好处是彻底解耦了代码和文本——添加新语言不需要修改任何 .ets 文件,只需新建资源目录和对应的 JSON 文件即可。
6.5 响应式布局适配
虽然「兰亭序」阅读器目前仅面向手机设备,但 API 24 提供了强大的响应式布局能力。我们可以通过 GridRow/GridCol 和断点监听来实现对不同屏幕尺寸的适配:
import { mediaquery } from '@kit.ArkUI';
@Component
struct ResponsiveReader {
@State isWideScreen: boolean = false;
aboutToAppear(): void {
// 监听屏幕宽度变化
let listener = mediaquery.matchMediaSync('(min-width: 600)');
listener.on('change', (result) => {
this.isWideScreen = result.matches;
});
}
build() {
if (this.isWideScreen) {
// 平板或折叠屏展开态:分两栏显示
Row() {
Text('原文').width('50%')
Text('注释').width('50%')
}
} else {
// 手机竖屏:单栏滚动
Scroll() {
Text('原文')
}
}
}
}
七、构建配置与签名
7.1 Hvigor 构建系统
HarmonyOS 使用 Hvigor 作为构建系统(类似于 Android 的 Gradle)。核心配置文件包括:
- 根
hvigorfile.ts:导入模块级构建脚本 - 模块
hvigorfile.ts:模块级构建任务配置 hvigor/hvigor-config.json5:构建工具版本和插件配置
hvigor-config.json5 示例:
{
modelVersion: "5.0.0",
toolChains: {
hvigor: "5.5.0",
hvigor-arkts: "5.5.0",
hvigor-ohos: "5.5.0"
}
}
7.2 签名配置
在发布应用之前,必须配置签名。签名文件类型:
| 类型 | 用途 | 有效期 |
|---|---|---|
| Debug 签名 | 开发调试,自动生成 | 随时可重新生成 |
| Release 签名 | 应用市场发布 | 需要正式申请 |
签名配置在 build-profile.json5 的 signingConfigs 数组中。在 DevEco Studio 中可以通过 UI 界面生成和管理。
7.3 编译选项配置
entry/build-profile.json5 中的编译选项:
{
apiType: "stageMode",
buildOption: {
resOptions: {
copyCodeResource: {
enable: false
}
}
},
buildOptionSet: [
{
name: "release",
arkOptions: {
obfuscation: {
ruleOptions: {
enable: false,
files: ["./obfuscation-rules.txt"]
}
}
}
}
]
}
关键配置解释:
apiType:选择 Stage 模型copyCodeResource.enable:是否复制源码资源到产物中obfuscation:代码混淆配置,Release 构建时建议开启obfuscation-rules.txt:混淆规则文件,可指定哪些类/方法不混淆
八、性能优化与最佳实践
8.1 长文本渲染优化
对于长文本阅读器,性能优化的主要考虑点是:
1. 避免使用过多的布局嵌套
每个 Column/Row 都是独立的布局节点。对于非常长的内容,可以将段落文本组合到一个 Text 中(用 \n 分隔),而不是每个段落一个 Text 组件。
✅ 推荐做法:
Text('段落1\n段落2\n段落3\n...') // 一个 Text 组件渲染全部
.fontSize(20)
.lineHeight(40)
❌ 不推荐的做法:
Column() {
Text('段落1').fontSize(20).lineHeight(40)
Text('段落2').fontSize(20).lineHeight(40)
Text('段落3').fontSize(20).lineHeight(40)
// 每个段落都需要独立布局计算
}
2. 懒加载(LazyForEach)
如果内容非常长(例如数万字的小说),可以使用 LazyForEach 实现懒加载渲染:
class ParagraphSource extends DataSource {
// 实现数据源
}
LazyForEach(new ParagraphSource(), (item: string) => {
Text(item)
.fontSize(20)
.lineHeight(40)
}, (item: string) => item)
LazyForEach 只渲染可视区域内的组件,滚动时回收不可见组件,大幅降低内存占用。
8.2 状态更新的最小化
在 ArkTS 中,@State 变量的修改会触发组件重新渲染。为了性能,应该:
1. 将状态尽量向下传递
// ✅ 推荐:只有需要的地方才使用 @State
@Entry
@Component
struct Index {
@State fontSize: number = 20;
build() {
Column() {
TextReader({ fontSize: this.fontSize })
FontSizeChanger({ onChange: (v) => { this.fontSize = v } })
}
}
}
@Component
struct TextReader {
@Prop fontSize: number; // 仅接收,不修改
build() {
Text('内容').fontSize(this.fontSize)
}
}
2. 避免频繁修改 @State
如果需要连续修改多个状态,尽量减少修改次数:
// ❌ 多次触发渲染
this.count++;
this.name = 'new';
this.visible = true;
// ✅ 合并状态(或将相关状态合并为一个对象)
this.state = { count: 1, name: 'new', visible: true };
// 但注意:对象整体赋值才会触发
8.3 资源复用
对于固定的字符串常量(如兰亭序全文),声明为 private 成员变量而非 @State:
// ✅ 正确:常量数据,不需要响应式
private lantingxuText: string = '全文内容...';
// ❌ 错误:数据不变化,不需要 @State
@State lantingxuText: string = '全文内容...';
二者的区别:
- 普通成员变量:只读,不触发 UI 更新
- @State 变量:变化时触发 UI 渲染
对于不会变化的数据,使用普通成员变量可以避免不必要的渲染检查。
8.4 图片资源优化
虽然我们的阅读器没有使用图片,但作为扩展建议:
- 格式选择:优先使用 PNG(支持透明),非透明图片用 JPEG
- 分辨率适配:提供不同 DPI 的图片(ldpi/mdpi/hdpi/xhdpi/xxhdpi)
- 矢量图:对于简单图标,使用 SVG 而非位图
- 压缩:使用 TinyPNG 等工具压缩,减小包体积
九、测试与调试
9.1 DevEco Studio 调试工具
DevEco Studio 提供了完善的调试支持:
1. Previewer(预览器)
在编码过程中,可以实时预览 UI 效果:
右侧面板 → Previewer 标签页
Previewer 支持:
- 实时预览代码变更
- 多设备模拟(手机、平板、折叠屏等)
- 交互操作模拟(点击、滑动等)
2. HiLog(日志系统)
import { hilog } from '@kit.PerformanceAnalysisKit';
const DOMAIN = 0x0000;
// 打印日志
hilog.info(DOMAIN, 'Reader', '页面加载完成');
hilog.error(DOMAIN, 'Reader', '加载失败: %{public}s', errMsg);
3. Profiler(性能分析器)
用于分析应用性能:
- CPU profiling:分析函数调用耗时
- Memory profiling:检测内存泄露
- Frame profiling:分析帧率
9.2 单元测试
HarmonyOS 支持基于 TestNG 的单元测试框架:
// src/ohosTest/ets/test/LocalUnit.test.ets
import { describe, it, expect } from '@ohos/hypium';
describe('LantingxuReaderTest', () => {
it('text_should_not_be_empty', 0, () => {
const text = '永和九年';
expect(text.length).assertGreater(0);
});
});
测试代码放在 ohosTest 源码集下:
entry/src/ohosTest/ets/test/
├── Ability.test.ets # Ability 生命周期测试
├── List.test.ets # 测试集列表
└── LocalUnit.test.ets # 本地单元测试
9.3 常见的编译错误与排查
错误 1:类型不匹配
error: Type 'string' is not assignable to type 'number'
解决方案:检查参数类型,ArkTS 对类型要求严格。
错误 2:找不到模块
error: Cannot find module '@kit.AbilityKit'
解决方案:检查 oh-package.json5 中是否声明了依赖,或者 SDK 版本是否正确。
错误 3:API 在目标 SDK 中不可用
error: Property 'xxx' does not exist on type 'TextAttribute'
解决方案:确认使用的 API 在目标 SDK(API 24)中是否可用,或检查 SDK 平台版本是否正确。
十、发布与上架
10.1 应用包生成
HarmonyOS 应用的发布格式为 .app 文件。生成流程:
- 配置 Release 签名:生成正式签名证书
- 构建 Release 包:Build → Build Hap(s)/App(s) → Build App(s)
- 产物路径:
entry/build/default/outputs/default/xxx-release-app.app
10.2 上架华为应用市场
发布到华为应用市场的基本流程:
- 注册开发者账号:在 AppGallery Connect 注册
- 创建应用:填写应用信息、上传图标截图
- 上传应用包:上传 .app 文件
- 填写隐私政策:说明数据收集和使用规则
- 审核与发布:等待审核通过
10.3 版本更新策略
应用版本号在 AppScope/app.json5 中配置:
{
app: {
bundleName: "com.example.myapplication",
vendor: "example",
version: {
code: 1000000, // 版本号(数字),用于系统判断版本新旧
name: "1.0.0" // 版本名称(字符串),展示给用户
}
}
}
版本号规范建议:
version.code使用大数字:主版本(1) × 百万 + 次版本(0) × 千 + 补丁(0) = 1000000version.name使用语义化版本:主版本.次版本.补丁
十一、总结与扩展方向
11.1 本文总结
通过「兰亭序」阅读器的开发实践,我们系统地掌握了:
- HarmonyOS API 24 的项目结构与配置方式
- ArkTS 声明式 UI 的编程范式
- ArkUI 组件体系:Container、Text、Scroll、Line、Divider
- 资源管理系统:多语言、多模式、多设备的资源管理
- 构建与签名:从开发到发布的完整流程
- 性能优化基础:长文本渲染、状态管理最佳实践
11.2 扩展方向
「兰亭序」阅读器虽然功能简单,但可以在此基础上扩展出大量有价值的功能:
1. 字体切换功能
@State fontFamily: string = 'default';
// 支持切换为楷体、宋体等书法字体
Text('内容').fontFamily(this.fontFamily)
2. 阅读设置面板
- 字号调节(滑动条 + 预览)
- 背景主题(宣纸/羊皮纸/护眼绿/深夜黑)
- 行距调节
3. 书签与进度保存
import { preferences } from '@kit.ArkData';
// 使用 Preferences 持久化阅读进度
let pref = await preferences.getPreferences(context, 'reader_pref');
await pref.put('scrollPosition', scrollPosition);
4. 白日依山尽自动滚动
使用 animateTo 实现自动滚屏阅读:
this.scroller.scrollTo({
yOffset: targetPosition,
duration: 3000 // 3秒内滚动到目标位置
})
5. 多语言支持
通过资源目录 resources/en_GB/ 添加英文版本简介:
// resources/en_GB/element/string.json
{
"string": [
{
"name": "app_title",
"value": "Preface to Orchid Pavilion"
}
]
}
6. 注释与翻译
将兰亭序逐句拆分,配合注释弹窗,做成学习工具:
Text('永和九年')
.onClick(() => {
// 弹出注释弹窗
this.showAnnotation('晋穆帝永和九年(公元353年)')
})
11.3 最后的思考
从一篇千年古文到一个鸿蒙应用,这本身就是一个非常"中国"的故事。技术是载体,文化是灵魂。
我们在开发这个应用时所做的每一个技术选择——暖色背景模拟宣纸质感、行高字距模拟古籍排版、简洁界面避免干扰阅读体验——都是在用现代技术语言去诠释古典精神。
正如王羲之在《兰亭序》中所说:"后之览者,亦将有感于斯文。"一千六百多年后,我们用数字技术让更多人能够"览"到这篇美文,而技术的温度,就在于它能够成为文化传承的桥梁。
愿这段代码,也成为一座桥梁。
附录:参考资料
本文使用 DevEco Studio 5.x + HarmonyOS SDK API 24 完成开发与验证
博客代码示例均取自实际运行的项目工程
所有评论(0)