news 2026/7/2 1:57:50

OpenHarmony 英语学习 App 实战:学习成就系统与数据可视化面板设计

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
OpenHarmony 英语学习 App 实战:学习成就系统与数据可视化面板设计

OpenHarmony 英语学习 App 实战:学习成就系统与数据可视化面板设计

摘要

学习 App 要让用户坚持,除了内容本身,还需要持续反馈。用户今天学了多少、连续坚持了几天、解锁了哪些成就,这些都能形成正向激励。本文以「英语视界 YingYu」项目为例,分享如何在 OpenHarmony/HarmonyOS 中设计学习成就系统和数据可视化面板。🏆

相关文件包括:

entry/src/main/ets/utils/UserDataManager.ts entry/src/main/ets/pages/ProfileContent.ets entry/src/main/ets/model/DataModels.ts

一、为什么需要成就系统?

英语学习是长期任务,很难每天都有明显进步。成就系统的价值在于:

  • 给用户阶段性反馈;
  • 强化连续学习行为;
  • 让学习数据更可见;
  • 提升打卡动力;
  • 鼓励探索不同功能模块。

「英语视界」中的成就覆盖多个维度:

  • 第一个单词;
  • 10 个、50 个、100 个单词;
  • 连续学习;
  • 连续打卡;
  • 完成每日目标;
  • 趣味英语探索。

二、成就数据模型

项目中使用Achievement描述成就:

exportinterfaceAchievement{ id:stringtitle:stringdescription:stringicon:stringunlocked:boolean unlockedDate?:string}

字段含义:

  • id:唯一标识;
  • title:成就名称;
  • description:解锁条件;
  • icon:展示图标;
  • unlocked:是否解锁;
  • unlockedDate:解锁日期。

这个模型很轻量,但足够支撑列表、徽章墙、个人中心统计等展示。

三、默认成就列表

项目在UserDataManager.ts中定义默认成就:

const defaultAchievements: Achievement[] = [ {id:'first_word', title:'初学者', description:'学习第一个单词', icon:'🌱', unlocked:false}, {id:'ten_words', title:'小试牛刀', description:'学习10个单词', icon:'📚', unlocked:false}, {id:'fifty_words', title:'词汇达人', description:'学习50个单词', icon:'🏆', unlocked:false}, {id:'hundred_words', title:'词汇专家', description:'学习100个单词', icon:'⭐', unlocked:false}, {id:'first_week', title:'一周坚持', description:'连续学习7天', icon:'📅', unlocked:false}, {id:'first_month', title:'一月坚持', description:'连续学习30天', icon:'🌙', unlocked:false}, {id:'daily_streak_3', title:'学习新星', description:'连续3天打卡', icon:'✨', unlocked:false}, {id:'daily_streak_7', title:'学习达人', description:'连续7天打卡', icon:'🌟', unlocked:false}, {id:'daily_streak_30', title:'学习传奇', description:'连续30天打卡', icon:'👑', unlocked:false}, {id:'perfect_day', title:'完美一天', description:'完成每日学习目标', icon:'🎯', unlocked:false} ]

成就命名不只是技术问题,也影响用户感受。对于学生用户,文案要积极、轻松、有鼓励感。

四、合并默认成就和本地存储

应用升级后,可能会新增成就。如果直接读取旧数据,新增成就可能丢失。因此项目使用mergeAchievementsFromStorage()合并默认成就和本地成就状态。

functionmergeAchievementsFromStorage(): Achievement[]{constbyId =newMap<string, Achievement>()conststored = yingyuPrefGet(STORAGE_KEY_ACHIEVEMENTS)if(stored) {constparsed = JSON.parse(stored)asAchievement[]for(leti =0; i < parsed.length; i++){ byId.set(parsed[i].id, parsed[i]) } }constresult: Achievement[] = []for(leti =0; i < defaultAchievements.length; i++) {constdef = defaultAchievements[i]constexisting = byId.get(def.id) result.push({ id: def.id, title: def.title, description: def.description, icon: def.icon, unlocked: existing ? existing.unlocked :false, unlockedDate: existing?.unlockedDate }) }returnresult }

这个设计非常实用:默认配置可以升级,用户解锁状态不会丢。

五、解锁成就

解锁逻辑封装为unlockAchievement()

exportfunctionunlockAchievement(achievementId:string):boolean{try{constachievements =mergeAchievementsFromStorage()constachievement = achievements.find(a=>a.id=== achievementId)if(achievement && !achievement.unlocked) { achievement.unlocked=trueachievement.unlockedDate=getTodayDateKey()persistAchievements(achievements)returntrue} }catch(e) {console.error('Failed to unlock achievement:', e) }returnfalse}

函数返回boolean,页面可以据此决定是否弹出“新成就解锁”的提示。

六、词汇数量成就

当用户学习新单词时,会检查词汇成就:

functioncheckWordAchievements(learnedCount:number):void{if(learnedCount>=1) {unlockAchievement('first_word')}if(learnedCount>=10) {unlockAchievement('ten_words')}if(learnedCount>=50) {unlockAchievement('fifty_words')}if(learnedCount>=100) {unlockAchievement('hundred_words')} }

对应调用点:

exportfunctionaddLearnedWord(wordId:number): boolean { const learned = getLearnedWords()if(!learned.includes(wordId)) { learned.push(wordId) yingyuPrefSet(STORAGE_KEY_LEARNED_WORDS, JSON.stringify(learned)) checkWordAchievements(learned.length)returntrue} returnfalse}

这样用户每学一个新词,系统都会自动判断是否达到里程碑。

七、连续学习和打卡成就

项目中区分了两种连续性:

  • 学习连续:当天学过单词;
  • 打卡连续:当天完成目标。

连续学习天数计算:

function getLearningStreakDays(): number { const map = buildProgressDateMap() let streak = 0 constcheck= newDate()check.setHours(0, 0, 0, 0)while(true) { constkey= `${check.getFullYear()}-${(check.getMonth() + 1).toString().padStart(2,'0')}-${check.getDate().toString().padStart(2,'0')}` const rec = map.get(key)if(rec&&rec.wordsLearned > 0) { streak++check.setDate(check.getDate() - 1) }else{ break } }returnstreak }

成就同步:

functionsyncStreakAndMiscAchievements():void{constlearnStreak=getLearningStreakDays()if(learnStreak>=7) {unlockAchievement('first_week')}if(learnStreak>=30) {unlockAchievement('first_month')}constcheckStreak=getCheckInStreakDays()if(checkStreak>=3) {unlockAchievement('daily_streak_3')}if(checkStreak>=7) {unlockAchievement('daily_streak_7')} }

这种设计让“学习过”和“完成目标”都有激励。

八、记录今日学习

当用户完成学习行为后,调用recordTodayLearning()

exportfunctionrecordTodayLearning(wordsLearned: number):void{constprogress =getLearningProgress()consttoday =getTodayDateKey()constsettings =getUserSettings()constgoal = settings.dailyGoalconsttodayRecord = progress.find(p=>p.date=== today)if(todayRecord) { todayRecord.wordsLearned+= wordsLearned todayRecord.completed= goal >0&& todayRecord.wordsLearned>= goal }else{ progress.push({date: today,wordsLearned: wordsLearned,minutesSpent:0,completed: goal >0&& wordsLearned >= goal }) }saveProgressList(progress)syncStreakAndMiscAchievements() }

这个函数同时完成:

  • 今日学习数累加;
  • 判断是否完成每日目标;
  • 保存进度;
  • 同步连续学习成就。

九、个人中心数据面板

ProfileContent.ets中展示用户学习数据:

loadData() {this.achievements = getAchievements()this.progress = getLearningProgress()conststats = getStatistics()this.totalWords = stats.totalWordsthis.totalDays = stats.totalDaysthis.consecutiveDays = stats.consecutiveDaysthis.achievementsCount = stats.achievementsCountconstsettings = getUserSettings()this.dailyGoal = settings.dailyGoalthis.recentProgress = getRecentProgressSorted(7) }

页面层只获取统计结果,不直接计算所有业务逻辑。

十、今日进度卡片

个人中心展示今日目标进度:

getProgressPercentage(): number {if(this.dailyGoal ===0)return0returnMath.min(100, Math.round((this.todayProgress /this.dailyGoal) *100)) }

进度条:

Row(){Column().width(this.getProgressPercentage().toString()+'%') .height(this.isTabletDevice ?14:12) .backgroundColor($r('app.color.primary_color')) .borderRadius(6)} .width('100%') .backgroundColor($r('app.color.divider_color')) .borderRadius(6)

这类进度条比单纯数字更直观,适合个人中心和首页。

十一、学习统计卡片

统计卡展示四个核心数字:

this.StatColumn(this.totalWords.toString(),'已学单词')this.StatColumn(this.totalDays.toString(),'学习天数')this.StatColumn(this.consecutiveDays.toString(),'连续打卡')this.StatColumn(this.achievementsCount.toString(),'已获成就')

对学习 App 来说,这四个指标很有代表性:

  • 总量;
  • 时间;
  • 连续性;
  • 成就。

十二、小结

本文结合「英语视界 YingYu」项目,拆解了学习成就系统和数据面板:

  • 使用Achievement表示成就;
  • 默认成就和本地状态合并,支持后续升级;
  • 词汇数量、连续学习、每日目标都能触发成就;
  • recordTodayLearning()统一记录学习行为;
  • 个人中心展示今日进度、学习统计和成就数量;
  • 通过进度条和卡片提升数据可读性。

学习坚持需要反馈,反馈需要数据,数据需要被设计成用户愿意看的样子。成就系统不是花哨功能,而是长期学习产品的激励引擎。🌟

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/2 1:57:12

车载以太网之要火系列:第74篇:为什么普通以太网没有“主从模式”,车载以太网却必须有?

学完基础协议篇再来看TC8,我发现了一个之前没注意过的“违和感”: 学普通以太网的时候,从来没听说过什么Master/Slave模式。两个电脑插上网线,自动就通了,谁管谁做主、谁做从? 但到了车载以太网这里,TC8规范里第一条就写着——DUT和Link Partner必须以相反的主从配置连…

作者头像 李华
网站建设 2026/7/2 1:51:14

BetterNCM Installer:3步解锁网易云音乐隐藏功能

BetterNCM Installer&#xff1a;3步解锁网易云音乐隐藏功能 【免费下载链接】BetterNCM-Installer 一键安装 Better 系软件 项目地址: https://gitcode.com/gh_mirrors/be/BetterNCM-Installer 你是否厌倦了网易云音乐千篇一律的界面&#xff1f;是否渴望为音乐播放器添…

作者头像 李华
网站建设 2026/7/2 1:50:30

LV3296与PIC32MZ微控制器的嵌入式数据采集系统设计

1. LV3296与PIC32MZ1024EFF144的硬件协同架构解析在嵌入式数据采集系统中&#xff0c;LV3296作为一款高性能的条形码扫描模块&#xff0c;与PIC32MZ1024EFF144微控制器的组合堪称黄金搭档。这套组合的核心优势在于LV3296通过UART接口输出的串行数据&#xff0c;能够被PIC32MZ10…

作者头像 李华
网站建设 2026/7/2 1:50:05

基于浏览器麦克风技术的环境噪声检测实现与落地应用

一、项目底层技术底座&#xff1a;Web Audio 前端音频采集架构 实时噪音检测是一套纯前端运行、依托设备内置麦克风实现的实时环境噪声检测网页工具&#xff0c;全程不依赖后端服务&#xff0c;所有音频采集、信号运算、分贝换算逻辑均在本地浏览器完成&#xff0c;核心依托 H…

作者头像 李华
网站建设 2026/7/2 1:48:42

Mysql8绿色版安装教程【全网最细致】

原文地址&#xff1a;Mysql8绿色版安装教程【全网最细致】-CSDN博客 1、什么是绿色版 MySQL绿色版是一种免安装、解压即用的数据库版本&#xff0c;通常以ZIP压缩包形式提供。其核心特性包括&#xff1a; 无需安装&#xff1a;用户只需解压文件到任意目录即可运行&#xff0c;…

作者头像 李华