鸿蒙 Next 碎片时间学习 App 开发实战:知识卡片 + 学习追踪



鸿蒙 Next 碎片时间学习 App 开发实战:知识卡片 + 学习追踪
作者:duluo
SDK 版本:HarmonyOS API 24 (Next)
开发工具:DevEco Studio
语言框架:ArkTS + ArkUI
字数:约 11000 字
目录
- 引言
- 产品概念与知识体系设计
- 三 Tab 架构回顾
- 知识卡片系统设计
- 学习追踪与连续天数算法
- 收藏系统实现
- 数据持久化与记录管理
- 编译错误全记录
- 六款 App 架构模式汇总
- HarmonyOS 开发的六个阶段
- 结语
1. 引言
1.1 微学习:利用碎片时间
“微学习”(Micro-learning)是一种通过短小精悍的学习单元,在零碎时间内获取知识的学习方式。研究表明,每次 5-10 分钟的微学习,其知识 retention rate(留存率)比传统长时学习高出 20%——因为短时专注更容易保持注意力。
“碎片时间学习” App 正是基于这一理念设计:每条知识卡片约 150-250 字,可在 1-2 分钟内读完。用户可以利用通勤、排队、午休等碎片时间,每天学习几条有趣的知识。
1.2 本 App 的定位
这是本系列的第六款应用。与前五款不同,本 App 将重点放在了内容设计和学习激励机制上:
| 维度 | 前五款 | 碎片时间学习 |
|---|---|---|
| 核心驱动力 | 功能工具 | 内容消费 + 学习激励 |
| 内容来源 | 用户生成/静态预设 | 精心设计的 16 条知识 |
| 激励设计 | 游戏化评分 | 连续学习天数 + 鼓励语 |
| 用户粘性 | 按需使用 | 每日打开的习惯养成 |
1.3 六款 App 回顾
| # | App | 核心技术点 | 代码行数 |
|---|---|---|---|
| 1 | 沉浸式白噪音 | 多媒体 + 动画 | ~767 |
| 2 | 时间胶囊 | Preferences + Builder 约束 | ~955 |
| 3 | 冰箱剩菜大作战 | Tab 架构 + 游戏化 | ~1320 |
| 4 | 尴尬粉碎机 | 静态数据 + 模式复用 | ~953 |
| 5 | 老年人防骗训练 | 适老化设计 + 情景训练 | ~1038 |
| 6 | 碎片时间学习 | 知识卡片 + 学习追踪 | ~851 |
2. 产品概念与知识体系设计
2.1 功能需求
用户故事 1:每天的碎片时间,我想学到一条有趣的新知识
用户故事 2:我想看到自己的学习记录和连续天数
用户故事 3:遇到喜欢的知识,我想收藏起来以后再看
用户故事 4:我想按分类浏览所有知识
功能清单:
├── F1: 今日知识卡片(随机展示,可换一个)
├── F2: 标记已学(记录学习进度)
├── F3: 收藏/取消收藏
├── F4: 分类浏览(6 大类)
├── F5: 连续学习天数计算
├── F6: 学习统计数据
├── F7: 收藏夹弹窗查看
└── F8: 数据持久化
2.2 知识体系
16 条知识卡片覆盖 6 大分类,每类 2-3 条:
| 分类 | 知识数量 | 设计意图 |
|---|---|---|
| 历史 | 3 条 | 满足对过去的好奇心 |
| 科学 | 3 条 | 培养科学素养 |
| 文学 | 3 条 | 提升人文底蕴 |
| 生活 | 3 条 | 实用生活技巧 |
| 哲学 | 2 条 | 启发思考 |
| 艺术 | 2 条 | 培养审美 |
2.3 数据模型
interface KnowledgeCard {
id: number;
category: string; // 分类
title: string; // 标题
content: string; // 正文(150-250 字)
source: string; // 来源
color: string; // 主题色
}
interface StudyRecord {
learnedIds: number[]; // 已学的卡片 ID
favoriteIds: number[]; // 收藏的卡片 ID
totalSessions: number; // 总学习次数
currentStreak: number; // 当前连续天数
lastStudyDate: string; // 最后学习日期
longestStreak: number; // 最长连续纪录
}
3. 三 Tab 架构回顾
3.1 Tab 概览
@State activeTab: number = 0;
buildTabContent() {
if (this.activeTab === 0) this.buildLearnPage() // 今日学习
else if (this.activeTab === 1) this.buildBrowsePage() // 分类浏览
else this.buildStatsPage() // 学习统计
}
3.2 Tab 0:今日学习
核心交互:展示知识卡片 + 底部操作按钮
┌─────────────────────────────┐
│ 🏷️ 历史 ❤️ │ ← 分类标签 + 收藏按钮
│ │
│ 万里长城的长度 │ ← 标题(20sp 加粗)
│ ───────────────── │
│ 中国万里长城的总长度 │ ← 内容(16sp,26sp 行距)
│ 超过21,000公里... │
│ │
│ 📖 中国文化遗产研究院 │ ← 来源
│ │
├─────────────────────────────┤
│ ✅ 学会了 🔄 换一个 │ ← 操作按钮
└─────────────────────────────┘
3.3 Tab 1:分类浏览
按 6 个分类分组展示所有知识卡片:
📖 分类浏览
按主题浏览所有知识卡片
🏛️ 历史
│ 01 万里长城的长度 ✅ 已学
│ 02 唐朝的"外卖"服务 ⏳ 未学
│ 03 铅笔的"铅"不是铅 ✅ 已学
🔬 科学
│ 04 光的传播速度 ⏳ 未学
│ 05 人体内的细菌数量 ✅ 已学
│ 06 蜜蜂的"8字舞" ⏳ 未学
...
3.4 Tab 2:学习统计
📊 学习统计
记录你的每一次学习
┌── 🔥 连续学习 ──┐
│ 7 天 │
│ 🌟 太棒了! │
└────────────────┘
┌──────┐ ┌──────┐ ┌──────┐
│ 📚 │ │ ❤️ │ │ 📅 │
│ 12 │ │ 5 │ │ 30天│
│ 已学 │ │ 收藏 │ │ 最长│
└──────┘ └──────┘ └──────┘
❤️ 查看收藏的知识卡片 >
4. 知识卡片系统设计
4.1 卡片组件结构
// 分类标签
Row() {
Text(card.category)
.fontSize(13).fontColor(Color.White)
.padding({ left: 10, right: 10, top: 4, bottom: 4 })
.backgroundColor(card.color)
.borderRadius(10)
Blank()
Text('\u{2665}') // 收藏按钮
.fontColor(this.isFavorited(card.id) ? favorite : textHint)
.onClick(() => { this.toggleFavorite(card.id); })
}
// 标题
Text(card.title)
.fontSize(20).fontWeight(FontWeight.Bold)
.lineHeight(28)
// 分隔线 + 正文
Divider()
Text(card.content)
.fontSize(16).lineHeight(26)
// 来源
Text(card.source)
.fontSize(12).fontColor(textHint)
4.2 卡片视觉参数
| 元素 | 参数 | 效果 |
|---|---|---|
| 卡片背景 | #FFFFFF 纯白 |
干净阅读体验 |
| 圆角 | 20px |
柔和现代 |
| 边框 | 主题色 + 33(20% 透明度) |
分类色彩暗示 |
| 阴影 | 主题色 + 12(7% 透明度) |
浮起感 |
| 高度 | 420px |
固定高度,一屏可见 |
4.3 卡片操作按钮
Row() {
// ✅ 学会了
Row() {
Text('\u{2705}').fontSize(16)
Text('学会了').fontSize(15)
.fontColor(ALL_COLORS.learned)
}
.padding({ left: 16, right: 16, top: 10, bottom: 10 })
.backgroundColor(ALL_COLORS.learned + '15')
.borderRadius(16)
.borderWidth(1)
.borderColor(ALL_COLORS.learned + '33')
.onClick(() => { this.markLearned(card.id); })
Blank()
// 🔄 换一个
Row() {
Text('\u{1F500}').fontSize(16)
Text('换一个').fontSize(15)
.fontColor(ALL_COLORS.primary)
}
.padding({ left: 16, right: 16, top: 10, bottom: 10 })
.backgroundColor(ALL_COLORS.primary + '12')
.borderRadius(16)
.onClick(() => { this.nextCard(); })
}
"学会了"按钮使用绿色主题色,周围有 10% 透明度的背景和 20% 透明度的边框——层次丰富但不抢眼。
4.4 卡片轮换
@State currentCardIndex: number = 0;
nextCard(): void {
this.currentCardIndex = (this.currentCardIndex + 1) % CARDS.length;
}
使用取模运算实现循环轮换。16 张卡片循环展示,不会重复。
4.5 5 种分类颜色
const CARDS: KnowledgeCard[] = [
{ id: 1, category: '历史', color: '#E53935' }, // 红
{ id: 2, category: '科学', color: '#1E88E5' }, // 蓝
{ id: 3, category: '文学', color: '#8E24AA' }, // 紫
{ id: 4, category: '生活', color: '#FB8C00' }, // 橙
{ id: 5, category: '哲学', color: '#43A047' }, // 绿
{ id: 6, category: '艺术', color: '#F4511E' }, // 深橙
// ...
]
每种分类有对应的主题色,贯穿卡片标签、边框和阴影。
5. 学习追踪与连续天数算法
5.1 连续天数算法
markLearned(cardId: number): void {
if (this.record.learnedIds.indexOf(cardId) >= 0) return; // 去重
this.record.learnedIds = this.record.learnedIds.concat([cardId]);
this.record.totalSessions++;
let today = this.getDateStr();
if (this.record.lastStudyDate === today) {
// 今天已经学过了,只增加学习次数
} else if (this.record.lastStudyDate === this.getYesterdayStr()) {
this.record.currentStreak++; // 连续学习
} else {
this.record.currentStreak = 1; // 中断,重新开始
}
this.record.lastStudyDate = today;
if (this.record.currentStreak > this.record.longestStreak) {
this.record.longestStreak = this.record.currentStreak;
}
this.record = this.copyRecord(this.record);
this.saveRecord();
}
算法流程:
标记已学
├─ 已存在 learnedIds 中 → 跳过(去重)
└─ 新学习 →
├─ lastStudyDate === today → 今日多次学习,不改变连续天数
├─ lastStudyDate === yesterday → 连续天数 +1
└─ 其他 → 连续天数重置为 1
然后 → 更新最长纪录
5.2 日期工具方法
getDateStr(): string {
let d = new Date();
return d.getFullYear() + '-' +
(d.getMonth()+1).toString().padStart(2,'0') + '-' +
d.getDate().toString().padStart(2,'0');
}
getYesterdayStr(): string {
let d = new Date(Date.now() - 24*60*60*1000);
return d.getFullYear() + '-' +
(d.getMonth()+1).toString().padStart(2,'0') + '-' +
d.getDate().toString().padStart(2,'0');
}
日期格式为 YYYY-MM-DD 的字符串比较,避免了时区问题和 Date 对象比较的复杂性。
5.3 连续天数可视化
Text('连续学习 ' + this.record.currentStreak + ' 天' +
(this.record.currentStreak >= 7 ? ' \u{1F31F}' : ''))
当连续天数 >= 7 天时,在文字末尾显示星星 Emoji 🌟,作为正向激励。
5.4 今日学习次数的追踪
@State todayLearned: number = 0;
// 启动时
if (this.record.lastStudyDate === this.getDateStr()) {
this.todayLearned = this.record.learnedIds.length;
}
// 标题栏显示
Text('已学 ' + this.todayLearned)
通过 todayLearned 状态变量,在标题栏右上角显示"已学 N"的实时计数。
6. 收藏系统实现
6.1 收藏/取消收藏
toggleFavorite(cardId: number): void {
let idx = this.record.favoriteIds.indexOf(cardId);
if (idx >= 0) {
// 已收藏 → 取消
this.record.favoriteIds = this.record.favoriteIds.filter(id => id !== cardId);
} else {
// 未收藏 → 添加
this.record.favoriteIds = this.record.favoriteIds.concat([cardId]);
}
this.record = this.copyRecord(this.record);
this.saveRecord();
}
6.2 收藏状态判断
isFavorited(cardId: number): boolean {
return this.record.favoriteIds.indexOf(cardId) >= 0;
}
6.3 收藏按钮视觉反馈
Text('\u{2665}') // ♥ 心形符号
.fontSize(20)
.fontColor(this.isFavorited(card.id) ?
ALL_COLORS.favorite : // 收藏态:红色 #E53935
ALL_COLORS.textHint) // 未收藏态:灰色 #BDBDBD
.onClick(() => { this.toggleFavorite(card.id); })
视觉反馈:点击后心形从灰色变为红色,给用户即时的操作确认。
6.4 收藏夹弹窗
@Builder
buildFavoriteList() {
// 蒙层 + 弹窗
let favorites = CARDS.filter(c => this.isFavorited(c.id));
if (favorites.length === 0) {
Text('还没有收藏的知识卡片~')
} else {
ForEach(favorites, (card) => {
Row() { /* 卡片标题 + 分类 + 右箭头 */ }
.onClick(() => {
// 跳转到该卡片的学习页面
this.currentCardIndex = CARDS.indexOf(card);
this.showFavoriteList = false;
this.activeTab = 0;
})
})
}
}
交互细节:点击收藏夹中的条目后,自动跳转到"今日学习"Tab 并显示该卡片。这个"跳转"的细节让收藏夹不只是静态列表,而是学习路径的快捷入口。
7. 数据持久化与记录管理
7.1 单键值存储
const STORAGE_KEY = 'study_records';
只存储 StudyRecord 一个对象,知识卡片数据是静态常量。
7.2 完整的持久化方法
async loadRecord(): Promise<void> {
try {
let context = getContext(this);
this.dataPreferences = await preferences.getPreferences(context, 'study_db');
let val = await this.dataPreferences.get(STORAGE_KEY, '');
if (val !== '') {
let data = JSON.parse(val as string) as StudyRecord;
if (data) this.record = data;
}
// 检查今天的学习状态
if (this.record.lastStudyDate === this.getDateStr()) {
this.todayLearned = this.record.learnedIds.length;
}
} catch (err) {
console.error(`Failed to load: ${JSON.stringify(err)}`);
}
}
async saveRecord(): Promise<void> {
try {
if (this.dataPreferences) {
await this.dataPreferences.put(STORAGE_KEY, JSON.stringify(this.record));
await this.dataPreferences.flush();
}
} catch (err) {
console.error(`Failed to save: ${JSON.stringify(err)}`);
}
}
7.3 复制对象方法
由于 ArkTS 不支持展开运算符 { ...obj },我们需要手动实现复制:
copyRecord(source: StudyRecord): StudyRecord {
return {
learnedIds: source.learnedIds.concat([]),
favoriteIds: source.favoriteIds.concat([]),
totalSessions: source.totalSessions,
currentStreak: source.currentStreak,
lastStudyDate: source.lastStudyDate,
longestStreak: source.longestStreak
};
}
每次修改 record 后调用此方法创建新引用,触发 @State 的变更检测。
7.4 生命周期管理
aboutToAppear(): void {
this.loadRecord();
}
aboutToDisappear(): void {
this.saveRecord();
}
8. 编译错误全记录
8.1 错误概览
本 App 出现 12 个编译错误,类型分布:
| 类型 | 数量 | 占比 |
|---|---|---|
ALL_COLORS 无类型 |
1 | 8% |
@Builder 中 let 声明 |
7 | 59% |
| 属性不存在(card 变量未定义) | 2 | 17% |
| Scroll 多子组件 | 1 | 8% |
| Row 属性不存在(borderBottomWidth) | 1 | 8% |
8.2 关键错误修复
错误类型 1:变量引用错误
'card' is possibly 'null'
场景:在 buildLearnPage 的 @Builder 中,原代码使用 let card = this.getCurrentCard() 后访问 card.title、card.content 等。移除 let 后,这些引用变成了未定义变量。
修复:将所有 card.property 替换为 this.getCurrentCard()!.property。
注意:buildBrowsePage 和 buildFavoriteList 中的 card 是 ForEach 循环变量,不需要修改。只有 buildLearnPage 中的 card 是之前 let 定义的。
错误类型 2:ForEach + if + Column 嵌套结构
The comma operator "," is supported only in "for" loops
场景:在 buildBrowsePage 中,ForEach 回调内使用 if (condition) { Column() { ... } } 结构时,编译器的解析顺序导致了误报。
修复:将结构从:
ForEach(items, (item) => {
Column() {
if (condition) { /* children */ }
}
})
改为:
ForEach(items, (item) => {
if (condition) {
Column() { /* children */ }
}
})
即在 ForEach 回调中先判断条件,再创建 Column。
错误类型 3:borderBottomWidth 在 Row 上不存在
Property 'borderBottomWidth' does not exist on type 'RowAttribute'
场景:在 buildFavoriteList 中,尝试给 Row 组件设置底部分隔线。
修复:移除 borderBottomWidth 和 borderBottomColor 属性,使用 Divider 组件或其他方式替代。ArkUI 的 Row 组件不支持单独的边框方向设置。
8.3 六款 App 错误纵向对比
| App | 错误数 | Builder | 类型 | 属性 | 其他 |
|---|---|---|---|---|---|
| 白噪音 | 16 | 8 | 1 | 0 | 7 |
| 时间胶囊 | 17 | 9 | 1 | 2 | 5 |
| 冰箱剩菜 | 22 | 15 | 1 | 6 | 0 |
| 尴尬粉碎机 | 1 | 0 | 1 | 0 | 0 |
| 防骗训练 | 12 | 6 | 1 | 5 | 0 |
| 碎片学习 | 12 | 7 | 1 | 4 | 0 |
趋势分析:
- Builder 错误始终是最大类别的错误,占总错误的 50-70%
- 类型错误(对象字面量无接口)每次必出现——因为每个 App 都有新的颜色常量
- 属性错误与代码量正相关——代码越多,越容易用错 API
9. 六款 App 架构模式汇总
9.1 数据模型演变
白噪音 : SoundItem[](6 预设) → 无持久化
时间胶囊 : TimeCapsule[](用户生成) → 单键值 Preferences
冰箱剩菜 : FoodItem[] + BattleStats → 3 键值 Preferences
尴尬粉碎机 : 静态 TopicCard[] + 用户日记 → 单键值 Preferences
防骗训练 : 静态 ScamScenario[] + 进度 → 单键值 Preferences
碎片学习 : 静态 KnowledgeCard[] + 记录 → 单键值 Preferences
趋势:从纯动态 → 纯静态 → 动态+静态混合 → 静态为主+轻量记录。
9.2 架构复杂度趋势
白噪音 时间胶囊 冰箱剩菜 尴尬粉碎机 防骗训练 碎片学习
767行 955行 1320行 953行 1038行 851行
冰箱剩菜达到峰值(1320 行)后有意控制,稳定在 850-1050 行之间。
9.3 编译错误趋势
白噪音 时间胶囊 冰箱剩菜 尴尬粉碎机 防骗训练 碎片学习
16 17 22 1 12 12
呈现"升→降→稳"的趋势,反映了从探索到成熟的完整学习周期。
9.4 设计模式清单
经过六款 App 的实践,以下是经过验证的 ArkUI 设计模式:
模式 1:Stack 三层结构
Stack() {
buildBackground() // 背景层
Column() { /* UI */ } // 内容层
if (dialog) { ... } // 弹窗层
}
模式 2:@Builder 数据获取
@Builder
buildXxx() {
// 只写 UI,不写 let
Text(this.getData())
}
getData(): string { return '...'; }
模式 3:数组引用更新
this.list = [newItem].concat(this.list); // 插入
this.list = this.list.concat([]); // 触发渲染
this.list = this.list.filter(predicate); // 删除
模式 4:条件渲染 vs 提前返回
// ✅ 正确:if 包裹 UI
@Builder build() {
if (condition) { Column() { /* UI */ } }
}
// ❌ 错误:if return
@Builder build() {
if (!condition) return; // ❌
Column() { /* UI */ }
}
模式 5:数据持久化三件套
aboutToAppear() → loadData()
每次操作后 → saveData()
aboutToDisappear() → saveData()
模式 6:Tab 切换
@State activeTab = 0;
buildTabContent() {
if (activeTab === 0) buildTab0()
else if (activeTab === 1) buildTab1()
else buildTab2()
}
9.5 弹窗模式
@Builder
buildDialog() {
if (this.showDialog) {
Column() {
// 蒙层
Column()
.width('100%').height('100%')
.backgroundColor('rgba(0,0,0,0.5)')
.onClick(() => { this.showDialog = false; })
// 浮层
Column() { /* 内容 */ }
.width('88%')
.position({ x: '6%', y: '18%' })
}
.width('100%').height('100%')
.position({ x: 0, y: 0 })
}
}
10. HarmonyOS 开发的六个阶段
基于六款 App 的开发经历,以下是 HarmonyOS 开发者可能经历的六个阶段:
10.1 阶段一:新手期(白噪音)
特征:16 个错误,基本语法不熟悉
学习重点:
- ArkTS 的基本类型系统(interface、type)
- @State 和 @Builder 的基本用法
- 组件的属性链式调用
挑战:理解声明式 UI 的思维方式——“UI 是状态的函数”
10.2 阶段二:探索期(时间胶囊)
特征:17 个错误,开始尝试新模式
学习重点:
- 数据持久化(Preferences)
- 条件渲染和弹窗系统
- 日期计算和格式化
挑战:学会在 @Builder 中避免 let 和 return
10.3 阶段三:试错期(冰箱剩菜)
特征:22 个错误,最高峰
学习重点:
- Tab 架构
- Grid 弹窗
- 复杂的表单交互
挑战:不要用闭包参数给 @Builder 传内容——这是最大的一个坑
10.4 阶段四:成熟期(尴尬粉碎机)
特征:1 个错误,最低谷
学习重点:
- 模式复用
- 静态数据管理
- 仪式感交互设计
挑战:如何在新 App 中复用已总结的模式
10.5 阶段五:收敛期(防骗训练、碎片学习)
特征:12 个错误,稳定
学习重点:
- 适老化设计
- 学习激励机制
- 代码量增长时的质量控制
挑战:当代码量超过 800 行时,如何保持 Builder 的整洁
10.6 六阶段的心得总结
| 阶段 | App | 关键教训 |
|---|---|---|
| 1. 新手期 | 白噪音 | 理解声明式思维 |
| 2. 探索期 | 时间胶囊 | 掌握 Builder 约束 |
| 3. 试错期 | 冰箱剩菜 | 不要用闭包抽象 UI |
| 4. 成熟期 | 尴尬粉碎机 | 复用模式可以减少 90% 的错误 |
| 5. 收敛期 | 防骗训练 | 大代码量需要更严谨的结构 |
| 6. 收官期 | 碎片时间学习 | 内容设计和用户体验同样重要 |
11. 结语
11.1 六款 App 的技术覆盖
六款 App 覆盖了 HarmonyOS Next 应用开发的多个核心技术领域:
| 技术领域 | 相关 App |
|---|---|
| 多媒体播放 | 白噪音 |
| 动画系统 | 白噪音、尴尬粉碎机 |
| 数据持久化 | 时间胶囊、冰箱剩菜、尴尬粉碎机、防骗训练、碎片学习 |
| 声明式 UI | 全部六款 |
| Tab 架构 | 冰箱剩菜、尴尬粉碎机、防骗训练、碎片学习 |
| 列表交互 | 时间胶囊、冰箱剩菜、尴尬粉碎机 |
| 弹窗系统 | 全部六款 |
| 适老化设计 | 防骗训练 |
| 学习激励 | 碎片时间学习 |
11.2 六篇博客的知识体系
本系列六篇博客构成了一个完整的 ArkUI 开发知识体系:
- 白噪音:初识 ArkUI + 多媒体 API
- 时间胶囊:数据持久化 + @Builder 语法
- 冰箱剩菜:Tab 架构 + 复杂交互
- 尴尬粉碎机:模式复用 + 静态数据
- 防骗训练:适老化 + 情景模拟
- 碎片时间学习:内容设计 + 学习追踪
11.3 给后来者的建议
- 从简单开始:第一款 App 不要超过 500 行,理解核心概念后再增加复杂度
- 记录错误模式:每次编译错误都是一个学习机会,记下来下次避免
- 先设计数据模型:写 UI 之前先定义好 interface
- 模式复用:新 App 优先使用已验证的模式
- Builder 不放逻辑:这是 ArkUI 最重要的规则
11.4 系列结语
从"沉浸式白噪音"到"碎片时间学习",六款 App、六篇博客、约 6000 行代码、约 80 个编译错误——这是一次完整的 ArkUI 学习之旅。
如果你是从第一篇一路读到这里的读者,恭喜你!你已经掌握了 HarmonyOS Next 应用开发的核心理念。现在,打开 DevEco Studio,开始你的第一个 ArkUI 项目吧。
附录 A:六款 App 数据全览
| 指标 | 白噪音 | 时间胶囊 | 冰箱剩菜 | 尴尬粉碎机 | 防骗训练 | 碎片学习 |
|---|---|---|---|---|---|---|
| @State 数 | 8 | 8 | 14 | 10 | 11 | 10 |
| @Builder 数 | 8 | 12 | 17 | 15 | 14 | 14 |
| 方法数 | 8 | 12 | 19 | 9 | 12 | 13 |
| 总行数 | 767 | 955 | 1320 | 953 | 1038 | 851 |
| 编译错误 | 16 | 17 | 22 | 1 | 12 | 12 |
| 修复轮次 | 2 | 2 | 3 | 1 | 2 | 2 |
| Tab 数 | 1 | 1 | 3 | 3 | 3 | 3 |
| 持久化 | 无 | ✅ | ✅ | ✅ | ✅ | ✅ |
附录 B:核心代码片段速查
弹窗模板
@Builder
buildDialog() {
if (this.condition) {
Column() {
Column().width('100%').height('100%')
.backgroundColor('rgba(0,0,0,0.5)')
.onClick(() => { this.condition = false; })
Column() { /* 内容 */ }
.width('88%').backgroundColor(Color.White)
.borderRadius(24).position({ x: '6%', y: '18%' })
}
.width('100%').height('100%').position({ x: 0, y: 0 })
}
}
数组更新
this.list = [newItem].concat(this.list); // 插入
this.list = this.list.concat([]); // 触发渲染
this.list = this.list.filter(predicate); // 删除
Tab 切换
@State activeTab: number = 0;
buildTabContent() {
if (this.activeTab === 0) this.buildTab0()
else if (this.activeTab === 1) this.buildTab1()
else this.buildTab2()
}
数据持久化
async loadData() {
let prefs = await preferences.getPreferences(ctx, 'my_db');
let val = await prefs.get('key', '');
if (val !== '') this.data = JSON.parse(val as string);
}
async saveData() {
await this.prefs.put('key', JSON.stringify(this.data));
await this.prefs.flush();
}
更多推荐



所有评论(0)