
鸿蒙 Next 老年人防骗训练 App 开发实战:适老化设计 + 情景训练 + 系列总结
作者:duluo SDK 版本:HarmonyOS API 24 (Next) 开发工具:DevEco Studio 语言框架:ArkTS + ArkUI 字数:约 12000 字
目录
1. 引言
1.1 老年人防骗的社会意义
根据中国公安部发布的数据,2023 年全国电信网络诈骗案件中,60 岁以上的老年受害人占比持续上升。常见的诈骗手段包括冒充公检法、保健品推销、投资理财骗局、亲情诈骗等。老年人群体由于信息获取渠道有限、对新技术不熟悉、且往往独居缺乏沟通,成为了诈骗分子的重点目标。
"老年人防骗训练"App 的设计初衷,是通过情景模拟训练帮助老年人提高防骗意识。它不是一款娱乐产品,而是一款具有社会价值的公益工具。
1.2 本 App 的技术定位
与前四款 App 相比,本 App 在技术上引入了新的维度——适老化设计:
| 目标用户 | 通用 | 老年人(适老化) |
| 字体大小 | 14-18sp | 18-24sp |
| 按钮尺寸 | 44px | 56px+ |
| 交互逻辑 | 灵活操作 | 简单明确 |
| 内容密度 | 中等 | 低密度、高可读性 |
| 数据模型 | 用户数据 | 静态 + 进度 |
1.3 系列回顾
这是本系列的第五款应用。前四款分别是:
每款 App 都在前一基础上引入新的技术维度。本 App 作为收官之作,将重点放在适老化设计和模式复用上,同时对所有五款 App 的架构模式做系统性总结。
2. 产品概念与需求分析
2.1 核心功能需求
用户故事 1:老人希望通过真实案例学习识别骗局
用户故事 2:老人需要大字大图的界面,方便阅读和操作
用户故事 3:老人希望能看到自己的学习进度
用户故事 4:老人遇到可疑情况时,需要快速查到求助电话
功能清单:
├── F1: 情景判断题(12 个真实诈骗场景)
├── F2: 答题反馈(正确/错误 + 详细解析)
├── F3: 防骗知识手册(6 条防骗口诀)
├── F4: 紧急求助电话(110/96110/12377/12315)
├── F5: 学习进度追踪(得分 + 完成度)
└── F6: 数据持久化(进度不丢失)
2.2 适老化设计原则
在设计过程中,我们遵循了以下适老化设计原则:
| 大字原则 | 所有字体 ≥ 16sp | 正文 18sp,标题 24sp |
| 高对比度 | 文字与背景对比鲜明 | 深色文字 #212121 配白色背景 |
| 大按钮 | 触摸区域 ≥ 48x48px | 按钮 56px 高,130px 宽 |
| 简单导航 | 减少层级,所有内容三 Tab 内直达 | 无二级页面嵌套 |
| 明确反馈 | 每次操作有清晰结果 | 正确/错误 + 解析弹窗 |
| 低信息密度 | 每屏展示内容精简 | 大间距、充足留白 |
2.3 信息架构
防骗训练营
├── Tab 0: 情景训练
│ ├── 进度概览(已完成 N/12 + 圆点进度条)
│ ├── 12 个情景条目(带状态图标)
│ └── 情景详情弹窗
│ ├── 场景描述(大字体阅读)
│ ├── 判断按钮(安全/骗局)
│ └── 结果弹窗(解析 + 得分)
│
├── Tab 1: 防骗知识
│ ├── 6 条防骗口诀(八个凡是/六个一律/…)
│ └── 知识详情弹窗(完整内容)
│
└── Tab 2: 紧急求助
├── 4 个紧急号码大按钮
└── 应急处理指南
3. 适老化 UI 设计原则
3.1 大字体系
常规 App 的字体通常在 14-16sp,而我们针对老年人将字体整体放大:
// 普通 App 的字体
Text('阅读以下情景,判断是不是骗局')
.fontSize(14) // 常规大小
// 适老化字体
Text('阅读以下情景,判断是不是骗局')
.fontSize(16) // 增大 2sp
// 标题
Text('情景判断题')
.fontSize(22) // 大标题
// 情景描述
Text(scenario.description)
.fontSize(18)
.lineHeight(30) // 1.67 倍行距
行距设计:常规行距为 1.2-1.4 倍,我们设置为 1.67 倍(lineHeight: 30,字号 18),降低阅读疲劳。
3.2 高对比度配色
const ALL_COLORS: ColorScheme = {
primary: '#1565C0', // 深蓝色主色
success: '#2E7D32', // 深绿色(正确)
danger: '#C62828', // 深红色(错误/危险)
text: '#212121', // 近黑色文字(最高对比度)
cardBg: '#FFFFFF', // 纯白卡片背景
// …
}
对比度检查:
- 正文 #212121 配白底 → 对比度 14.5:1(远超 WCAG AAA 标准 7:1)
- 主色 #1565C0 配白底 → 对比度 7.8:1(满足 AAA 标准)
3.3 大按钮设计
// 判断按钮:56px 高,130px 宽
Column() {
Text('\\u{2705} 安全')
.fontSize(20)
.fontColor(Color.White)
.fontWeight(FontWeight.Bold)
}
.width(130)
.height(56)
.backgroundColor(ALL_COLORS.success)
.borderRadius(16)
56px 的高度远超标准 44px 的最小触摸目标,确保老年人即使手部颤抖也能准确点击。
3.4 紧急号码的极致放大
在紧急求助页面,电话号码使用 40sp 的超大字体:
Text('110')
.fontSize(40) // 超大
.fontColor(Color.White)
.fontWeight(FontWeight.Bold)
整个号码卡片高 100px,确保在紧急情况下老人能够一眼看到并拨打。
4. 三 Tab 架构总览
4.1 Tab 配置
@Builder
buildTabBar() {
Row() {
this.buildTabItem(0, '\\u{1F9E0}', '情景训练')
this.buildTabItem(1, '\\u{1F4DA}', '防骗知识')
this.buildTabItem(2, '\\u{1F6A8}', '紧急求助')
}
.height(64) // 比常规 56px 更高
}
Tab 栏高度增加到 64px(前作均为 56px),提供更大的触摸目标。
4.2 Tab 内容的三个 Builder
@Builder
buildTabContent() {
if (this.activeTab === 0) {
this.buildTrainPage()
} else if (this.activeTab === 1) {
this.buildKnowledgePage()
} else {
this.buildEmergencyPage()
}
}
三个 Tab 的内容都是 Scroll 内嵌 Column,适合大量文本阅读。
4.3 标题栏中的进度展示
if (this.activeTab === 0) {
Column() {
Text(this.progress.highScore.toString())
.fontSize(18)
.fontColor(ALL_COLORS.accent)
.fontWeight(FontWeight.Bold)
Text('最高分')
.fontSize(11)
.fontColor(ALL_COLORS.textHint)
}
}
在标题栏右上角显示"最高分",让老人随时看到自己的最佳成绩,形成正向激励。
5. 情景训练模块:从需求到实现
5.1 数据模型
interface ScamScenario {
id: number;
title: string; // 情景标题
description: string; // 情景描述(200-300 字)
isScam: boolean; // 是否是骗局(所有情景都是 true)
explanation: string; // 解析
category: string; // 分类(电信诈骗/保健品诈骗/…)
}
设计决策:12 个情景的 isScam 全部为 true。为什么不加入"安全"的情景?
原因如下:
5.2 情景列表设计
ForEach(SCENARIOS, (scenario: ScamScenario) => {
Column() {
Row() {
Text(this.getScenarioIcon(scenario.id)) // ❓/✅/❌
Column() {
Text(scenario.title).fontSize(18)
Text(scenario.category) // 带颜色标签
}
Text('\\u{276F}') // 右箭头
}
}
.onClick(() => { this.openScenario(scenario); })
})
每个情景条目包含三个元素:
- 状态图标:未答 ❓、正确 ✅、错误 ❌
- 标题 + 分类标签:分类使用不同颜色(电信诈骗=红、保健品=紫、投资=橙等)
- 右箭头:指示可点击
5.3 圆点进度条
Row() {
ForEach(SCENARIOS, (scenario: ScamScenario) => {
Column() {
Circle()
.width(22).height(22)
.fill(this.getScenarioStatus(scenario.id))
}
})
}
12 个圆点代表 12 道题:
- 灰色 #E0E0E0 = 未作答
- 绿色 #2E7D32 = 回答正确
- 红色 #C62828 = 回答错误
这是一种一目了然的进度可视化,老人不需要读文字就能了解自己的完成情况。
5.4 情景详情弹窗
弹窗包含三个部分:
上部:标题 + 分类
Row() {
Text(this.getScenarioIcon(this.selectedScenario!.id))
Text(this.selectedScenario.title)
}
Text(this.selectedScenario.category + ' · 判断题')
中部:情景描述
Scroll() {
Text(this.selectedScenario.description)
.fontSize(18).lineHeight(30)
}
.height(160)
描述使用 18sp 字体 + 30sp 行距,适合老人阅读。
下部:判断按钮
Row() {
Column() { Text('✅ 安全') } // 绿色
.width(130).height(56).backgroundColor(success)
Column() { Text('⚠️ 骗局') } // 红色
.width(130).height(56).backgroundColor(danger)
}
两个大按钮并列,老人只需在二者之间选择。按钮颜色与语义一致(绿=安全,红=危险)。
5.5 答题逻辑
answerScenario(scenarioId: number, answerIsScam: boolean): void {
let scenario = SCENARIOS.find(s => s.id === scenarioId);
this.lastAnswerCorrect = (answerIsScam === scenario.isScam);
if (this.progress.completedIds.indexOf(scenarioId) < 0) {
this.progress.completedIds = this.progress.completedIds.concat([scenarioId]);
if (this.lastAnswerCorrect) this.progress.correctCount++;
this.progress.totalCount++;
let pct = Math.round(this.progress.correctCount / this.progress.totalCount * 100);
if (pct > this.progress.highScore) this.progress.highScore = pct;
this.progress = this.copyProgress(this.progress);
this.saveProgress();
}
this.showScenarioDetail = false;
this.showResult = true;
}
积分规则:
- 每题只有首次作答计分(后续进入查看不会重复计分)
- 答对 +1 正确数
- 最高分取所有答题轮次中的最高正确率
5.6 结果弹窗
答题后弹出结果页,包含正确答案和详细解析:
Text(this.lastAnswerCorrect ? '回答正确!' : '回答错误')
Text('正确答案:这是骗局')
Text('解析')
Scroll() { Text(scenario.explanation) }
Text('进度:5 / 12')
Text('得分:4 / 5')
解析部分使用 Scroll 容器,可以滚动阅读全文。每条解析约 100-150 字,用通俗语言解释为什么这是骗局,以及正确的应对方式。
6. 防骗知识库:静态内容展示
6.1 数据模型
interface KnowledgeItem {
id: number;
title: string; // 标题
content: string; // 正文内容
icon: string; // Emoji 图标
color: string; // 主题色
}
6.2 六条防骗口诀
| 八个凡是 | ⚠️ | 红 | 8 种"凡是"都是诈骗 |
| 六个一律 | 🚫 | 橙 | 6 种情况一律挂断/删除 |
| 三不一多 | 📚 | 绿 | 不点/不信/不透露/多核实 |
| 保健品骗局 | 💊 | 紫 | 5 大特征识别保健品诈骗 |
| 投资理财防骗 | 💰 | 蓝 | 5 条理财防骗原则 |
| 遇骗应急处理 | 🚨 | 粉 | 被骗后的 5 步应急措施 |
6.3 内容展示
知识条目使用卡片列表展示,点击进入详情弹窗:
ForEach(KNOWLEDGE_LIST, (item: KnowledgeItem) => {
Column() {
Row() {
Text(item.icon).fontSize(32)
Column() {
Text(item.title).fontSize(20).fontWeight(FontWeight.Bold)
Text('点击查看详细内容').fontSize(14)
}
Text('\\u{276F}')
}
}
.onClick(() => {
this.selectedKnowledge = item;
this.showKnowledgeDetail = true;
})
})
适老化细节:
- 标题使用 20sp,比常规大 4sp
- 提示文字使用 14sp,虽然略小但不是关键信息
- 整行可点击,触摸区域大
7. 紧急求助页面:大按钮设计
7.1 四个紧急号码
// 110 报警
Column() {
Text('110').fontSize(40).fontColor(Color.White).fontWeight(FontWeight.Bold)
Text('报警电话').fontSize(16).fontColor(Color.White)
}
.width('85%').height(100)
.backgroundColor(ALL_COLORS.danger) // 红色
.borderRadius(20)
// 96110 反诈专线(橙色)
// 12377 网络违法举报(蓝色)
// 12315 消费者投诉(绿色)
号码卡片 100px 高,使用 40sp 超大字体,不同颜色区分。
7.2 遇骗应急处理
Column() {
Text('如果不幸被骗:')
Text('1. 第一时间拨打110报警')
Text('2. 联系银行冻结账户')
Text('3. 保存聊天记录和转账凭证')
Text('4. 不要因羞愧而隐瞒')
Text('5. 越早报警,追回希望越大')
}
.fontSize(16)
.lineHeight(26)
应急指南采用编号列表形式,清晰易读。特别加入"不要因羞愧而隐瞒"这一条——很多老人被骗后因为觉得丢人不愿声张,错过了最佳报案时间。
8. 学习进度追踪与持久化
8.1 进度数据模型
interface TrainProgress {
completedIds: number[]; // 已完成的题目 ID 列表
correctCount: number; // 答对数量
totalCount: number; // 总答题数
highScore: number; // 最高正确率 (0-100)
}
8.2 数据持久化
async loadProgress(): Promise<void> {
let context = getContext(this);
this.dataPreferences = await preferences.getPreferences(context, 'scam_train_db');
let val = await this.dataPreferences.get(STORAGE_KEY_PROGRESS, '');
if (val !== '') {
let data = JSON.parse(val as string) as TrainProgress;
if (data) this.progress = data;
}
}
async saveProgress(): Promise<void> {
if (this.dataPreferences) {
await this.dataPreferences.put(STORAGE_KEY_PROGRESS, JSON.stringify(this.progress));
await this.dataPreferences.flush();
}
}
单键值存储:只存储 TrainProgress 一个对象,情景数据和知识数据都是静态常量。
8.3 进度的可视化
进度通过三种方式可视化:
Text('已完成 ' + this.progress.completedIds.length + ' / ' + SCENARIOS.length + ' 题')
三种可视化方式适应不同的信息获取习惯——喜欢看数字的看数字,喜欢看图形的看圆点。
8.4 防止重复计分
if (this.progress.completedIds.indexOf(scenarioId) < 0) {
// 首次作答才计分
this.progress.completedIds = this.progress.completedIds.concat([scenarioId]);
// 更新分数…
}
indexOf 检查:如果 ID 已在 completedIds 中,说明是重复查看,不重复计分。这确保了分数反映的是真实的答题次数。
9. 编译错误回顾
9.1 本 App 的错误
本次编译共出现 12 个错误,类型分布如下:
| 对象字面量无类型 | 1 | 8% |
| @Builder 中 let 声明 | 6 | 50% |
| API 不存在(flexWrap) | 1 | 8% |
| 样式错误(TextStyle.NONE) | 1 | 8% |
| 双点号(…backgroundColor) | 1 | 8% |
| 展开运算符 | 1 | 8% |
| 空值安全 | 1 | 8% |
9.2 典型错误修复
错误 1:flexWrap 不存在
// ❌ Row 组件没有 flexWrap 属性
Row() { … }.flexWrap(FlexWrap.Wrap)
// ✅ 移除即可,Row 默认不换行
Row() { … }
错误 2:TextStyle.NONE 不存在
// ❌ TextStyle 枚举没有 NONE 值
Text(' ', { style: TextStyle.NONE })
// ✅ 直接传文本,省略 style 参数
Text(' ')
错误 3:双点号 ..backgroundColor
// ❌ 输入错误
..backgroundColor('#43A047')
// ✅ 正确
.backgroundColor('#43A047')
9.3 Builder 语法约束的深度理解
在五款 App 的开发中,@Builder 的语法约束是遇到最多的错误类型。这里做一次系统性总结:
@Builder 中可以写什么?
| UI 组件声明 | ✅ | Column(), Text(), Row() |
| 属性链式调用 | ✅ | .fontSize(), .width() |
| if/else 条件渲染 | ✅ | if (x) { Text() } |
| ForEach 循环 | ✅ | ForEach(list, item => { Text() }) |
| 调用其他 @Builder | ✅ | this.buildXxx() |
| let 变量声明 | ❌ | let x = getData() |
| return 语句 | ❌ | if (!x) return; |
| 函数调用(非Builder) | ❌ | someFunction() |
| 闭包参数 | ❌ | @Builder fn(label: string, content: () => void) |
根本原因:@Builder 方法在编译时会被 ArkUI 编译器做特殊处理。它不是一个普通的函数,而是一个UI 声明块。编译器会分析 Builder 中的组件树,生成高效的渲染指令。如果在 Builder 中混入命令式逻辑,编译器无法正确分析组件树。
实践建议:
- 如果一个 @Builder 方法超过 50 行,考虑拆分为多个子 Builder
- 如果一个 @Builder 方法中有超过 3 个 if/else 分支,将分支逻辑提取为方法调用
- 如果一个 @Builder 方法需要访问的嵌套数据很深,先提取为方法
9.4 五款 App 的 Builder 模式对比
| 白噪音 | 16 | 2 轮 | 首次接触 ArkUI,API 不熟悉 |
| 时间胶囊 | 17 | 2 轮 | Builder 约束 + 展开运算符 |
| 冰箱剩菜 | 22 | 3 轮 | 闭包参数模式引入大量错误 |
| 尴尬粉碎机 | 1 | 1 轮 | 模式成熟,仅颜色接口错误 |
| 防骗训练 | 12 | 2 轮 | 主要错误来自大段的 @Builder 重构 |
趋势:整体呈下降趋势。尴尬粉碎机达到最低点(1 个错误),防骗训练有所回升(12 个错误)主要是因为情景弹窗代码量大、内部逻辑复杂。
10. 五款 App 系列总结
10.1 核心技术覆盖
五款 App 覆盖了 HarmonyOS Next 应用开发的多个核心技术领域:
| 多媒体播放 | 白噪音 | @kit.MediaKit、AudioPlayer、混音 |
| 动画系统 | 白噪音、尴尬粉碎机 | animation 属性、动态光晕 |
| 数据持久化 | 时间胶囊、冰箱剩菜、尴尬粉碎机、防骗训练 | Preferences、JSON 序列化 |
| 声明式 UI | 全部 | @State、@Builder、条件渲染 |
| Tab 架构 | 冰箱剩菜、尴尬粉碎机、防骗训练 | 底部导航、条件切换 |
| 列表交互 | 时间胶囊、冰箱剩菜、尴尬粉碎机 | List + ForEach + swipeAction |
| 弹窗系统 | 全部 | 蒙层 + 浮层、条件渲染 |
| 适老化设计 | 防骗训练 | 大字体、高对比度、大按钮 |
10.2 数据模型演变
白噪音 :SoundItem[](6 个预设)
时间胶囊 :TimeCapsule[](用户生成)
冰箱剩菜大作战 :FoodItem[] + BattleStats(用户生成 + 统计)
尴尬粉碎机 :静态 TopicCard[]/ComebackItem[] + EmbarrassmentEntry[]
防骗训练 :静态 ScamScenario[]/KnowledgeItem[] + TrainProgress
演变趋势:从纯预设 → 纯用户 → 混合 → 静态为主 + 轻量进度。
10.3 架构复杂度趋势
白噪音(单页)→ 时间胶囊(单页+弹窗)→ 冰箱剩菜(3 Tab)→ 尴尬粉碎机(3 Tab)→ 防骗训练(3 Tab)
767行 955行 1320行 953行 1038行
冰箱剩菜达到行数峰值(1320 行),后续 App 有意控制复杂度,保持在 1000 行左右。
10.4 编译错误趋势
白噪音 16个 → 时间胶囊 17个 → 冰箱剩菜 22个 → 尴尬粉碎机 1个 → 防骗训练 12个
↑ ↑ ↑ ↑ ↑
新手期 探索期 试错期 成熟期 收敛期
五款 App 的编译错误数呈现"先升后降"的趋势,反映了 ArkUI 的学习曲线:
- 新手期(1-2款):学习基础语法和 API
- 探索期(2-3款):尝试复杂模式,踩更多坑
- 试错期(3款):闭包参数模式带来大量错误
- 成熟期(4款):模式固定,错误极少
- 收敛期(5款):大代码量场景下的模式验证
10.5 核心模式总结
经过五款 App 的实践,以下模式被反复验证有效:
模式 1:Stack 三层结构
Stack() {
buildBackground() // 背景(可选)
Column() { /* UI */ } // 内容
if (dialog) { … } // 弹窗
}
模式 2:@Builder + 方法提取
@Builder
buildXxx() {
// 只写 UI 组件,不写 let/return
Text(this.getData())
}
getData(): string {
// 普通方法写逻辑
return 'xxx';
}
模式 3:数组更新
// 插入
this.list = [newItem].concat(this.list);
// 触发渲染
this.list = this.list.concat([]);
// 删除
this.list = this.list.filter(predicate);
模式 4:数据持久化三件套
aboutToAppear() → loadData()
每次操作后 → saveData()
aboutToDisappear() → saveData()
模式 5:Tab 切换
@State activeTab = 0;
buildTabContent() {
if (activeTab === 0) buildTab0()
else if (activeTab === 1) buildTab1()
else buildTab2()
}
10.6 五篇博客总览
| 第 1 篇 | 沉浸式白噪音 | 多媒体 + 动画 + Grid | 11,000 |
| 第 2 篇 | 时间胶囊 | Preferences + Builder 约束 | 11,000 |
| 第 3 篇 | 冰箱剩菜大作战 | Tab 架构 + 游戏化 + Grid 弹窗 | 10,800 |
| 第 4 篇 | 尴尬粉碎机 | 静态数据 + 仪式感交互 + 系列对比 | 9,600 |
| 第 5 篇 | 老年人防骗训练 | 适老化设计 + 情景训练 + 系列总结 | 10,000+ |
11. 给 HarmonyOS 新手的建议
11.1 学习路径推荐
基于五款 App 的开发经验,推荐以下学习路径:
第一阶段:理解基础(1-2 周)
- 学习 ArkTS 语法(接口、类型、泛型)
- 理解 @State 和 @Builder 的基础用法
- 做一个简单的单页 App(如待办事项列表)
第二阶段:掌握核心(2-4 周)
- 实现弹窗系统(蒙层 + 浮层)
- 学习 List + ForEach + swipeAction
- 掌握数据持久化(Preferences)
第三阶段:复杂场景(4-8 周)
- 实现 Tab 架构
- 理解 Builder 的语法约束
- 建立自己的模式清单
11.2 常见陷阱
| Builder 中用 let | 编译错误 10905209 | 所有数据获取用方法 |
| 数组不触发渲染 | 修改了元素 UI 不更新 | .concat([]) 创建新引用 |
| 展开运算符 | 编译错误 arkts-no-spread | 使用 .concat() |
| 对象字面量无类型 | 编译错误 10605038 | 先定义 interface |
| 闭包传参给 @Builder | 编译错误 10905204 | 直接内联 UI |
11.3 五款 App 的实用代码片段
以下是从五款 App 中提炼的最实用的代码片段:
片段 1:弹窗模板
@Builder
buildDialog() {
if (this.condition) { // 使用 if 包裹,不用 return
Column() {
// 蒙层
Column()
.width('100%').height('100%')
.backgroundColor('rgba(0,0,0,0.5)')
.onClick(() => { this.condition = false; })
// 内容
Column() { /* … */ }
.width('90%').backgroundColor(Color.White)
.borderRadius(24).position({ x: '5%', y: '20%' })
}
.width('100%').height('100%').position({ x: 0, y: 0 })
}
}
片段 2:数组更新
// 头部插入
this.list = [newItem].concat(this.list);
// 触发渲染(修改元素后)
item.property = newValue;
this.list = this.list.concat([]);
// 过滤删除
this.list = this.list.filter(predicate);
// 复制对象
this.data = this.copyData(this.data);
片段 3:数据持久化
async loadData() {
let ctx = getContext(this);
let prefs = await preferences.getPreferences(ctx, 'my_db');
let val = await prefs.get('my_key', '');
if (val !== '') this.stateData = JSON.parse(val as string);
}
async saveData() {
if (this.prefs) {
await this.prefs.put('my_key', JSON.stringify(this.stateData));
await this.prefs.flush();
}
}
片段 4:Tab 架构
@State activeTab: number = 0;
buildTabContent() {
if (this.activeTab === 0) { this.buildTab0() }
else if (this.activeTab === 1) { this.buildTab1() }
else { this.buildTab2() }
}
buildTabBar() {
Row() {
this.buildTabItem(0, '图标1', '标签1')
this.buildTabItem(1, '图标2', '标签2')
this.buildTabItem(2, '图标3', '标签3')
}
.width('100%').height(56)
.position({ x: 0, y: '100%' }).translate({ y: –56 })
}
11.4 五条心法
附录 A:适老化设计速查表
| 正文字号 | 14-16sp | 18-20sp | +4sp |
| 标题字号 | 18-20sp | 22-24sp | +4sp |
| 按钮高度 | 44px | 56px+ | +12px |
| 行距 | 1.2-1.4x | 1.5-1.7x | +0.3x |
| 卡片圆角 | 12px | 14-20px | 更柔和 |
| 对比度 | 4.5:1 | 7:1+ | 更高 |
| 信息密度 | 中高 | 低 | 更多留白 |
| 操作层级 | 2-3 层 | 1-2 层 | 更扁平 |
附录 B:五款 App 数据全览
| @State | 8 | 8 | 14 | 10 | 11 |
| @Builder | 8 | 12 | 17 | 15 | 14 |
| 业务方法 | 8 | 12 | 19 | 9 | 12 |
| 总行数 | 767 | 955 | 1320 | 953 | 1038 |
| 编译错误 | 16 | 17 | 22 | 1 | 12 |
| 修复轮次 | 2 | 2 | 3 | 1 | 2 |
| 静态数据 | 6 声音 | 6 图标 | 8 分类+13 单位 | 20 话题+12 金句 | 12 情景+6 知识 |
| 持久化 | 无 | ✅ | ✅ | ✅ | ✅ |
| Tab 数 | 1 | 1 | 3 | 3 | 3 |
附录 C:完整接口清单
interface ScamScenario {
id: number;
title: string;
description: string;
isScam: boolean;
explanation: string;
category: string;
}
interface KnowledgeItem {
id: number;
title: string;
content: string;
icon: string;
color: string;
}
interface TrainProgress {
completedIds: number[];
correctCount: number;
totalCount: number;
highScore: number;
}
interface ColorScheme {
primary: string;
primaryDark: string;
accent: string;
success: string;
danger: string;
bg1: string;
bg2: string;
cardBg: string;
text: string;
textLight: string;
textHint: string;
border: string;
safe: string;
scam: string;
}
本文是"鸿蒙 Next 应用开发实战"系列的第五篇(完结篇)。由 AtomCode 基于 HarmonyOS Next API 24 编写,记录了老年人防骗训练 App 的完整开发过程,以及对五款 App 的系统性技术总结。
(全文完,约 12000 字)







