HarmonyOS 应用开发《掌上英语》第50篇:题本与收藏——用户数据的本地管理
错题本与收藏——用户数据的本地管理
一、功能概述
错题本和收藏是学习应用中两个重要的辅助功能。错题本自动收集用户答错的题目,供后续复习;收藏则让用户可以手动标记重点题目,建立自己的学习素材库。
两者的数据都存储在本地 Preferences 中,使用单独的 Key:
exportclassPreferConstant{staticreadonlyERROR_RECORDS:string='ERROR_RECORDS';// 错题记录staticreadonlyEXAM_PREFER_COLLECT:string='EXAM_PREFER_COLLECT';// 收藏记录}二、错题自动记录
2.1 记录时机
错题记录在 AnswerQuestionsPage 中自动触发。当用户选择答案后系统判断:
storageWrongClick(){// 排除从错题本或收藏进入的情况if(this.sourceRouterModel?.sourceType==='我的错题'){return;}if(this.sourceRouterModel?.sourceType==='我的收藏'){return;}if(this.sourceRouterModel?.sourceType==='错题练习'){return;}if(this.currentModel){// 只有当选择答案不等于正确答案时,才记录为错题if(this.currentModel.selectQues.toString()!==this.currentModel.rightQues.toString()){letcurrentItem=this.transTopicItemType();if(currentItem){letarr:TopicItemType[]=PreferenceUtil.getInstance().get(PreferConstant.ERROR_RECORDS,[])asTopicItemType[];if(arr.length===0){arr.push(currentItem);}else{letisExit=false;arr.forEach((itemType:TopicItemType,idx:number)=>{if(itemType.keyID===currentItem?.keyID){arr[idx]=currentItem;// 更新已存在的错题isExit=true;}});if(!isExit){arr.push(currentItem);// 新增错题}}PreferenceUtil.getInstance().put(PreferConstant.ERROR_RECORDS,arr);}}}}关键设计:
- 自动记录:答错即记录,无需用户手动操作
- 去重更新:同一题多次答错,只保留最新一条记录
- 排除干扰:从错题本进入练习时不再重复记录
- 完整数据:保存
TopicItemType完整对象,包含题目、选项、解析等
2.2 收藏记录
收藏需要用户手动触发:
storageCollectClick(){if(this.currentModel){letcollectItem=this.transTopicItemType();if(collectItem){letarr:TopicItemType[]=PreferenceUtil.getInstance().get(PreferConstant.EXAM_PREFER_COLLECT,[])asTopicItemType[];if(arr.length===0){arr.push(collectItem);}else{letisExit=false;arr.forEach((itemType:TopicItemType,idx:number)=>{if(itemType.keyID===this.currentModel?.keyID){arr.splice(idx,1);// 再次点击取消收藏isExit=true;}});if(!isExit){arr.push(collectItem);}}PreferenceUtil.getInstance().put(PreferConstant.EXAM_PREFER_COLLECT,arr);}}}收藏是"开关式"操作:点击收藏、再点击取消收藏。
三、MyWrongPage——错题展示
3.1 页面结构
build(){Column(){this.topBuilder();// 顶部导航 + 全选/删除按钮if(this.wrongQues.length<=0){Text('暂无易错词汇,加油学习吧');// 空状态}else{this.wrongListBuilder();// 错题列表this.deleteBuilder();// 批量删除按钮}}}3.2 数据加载
asyncinitData(){try{letarr:TopicItemType[]=PreferenceUtil.getInstance().get(PreferConstant.ERROR_RECORDS,[])asTopicItemType[];arr.sort((a,b)=>b.jlStamp-a.jlStamp);// 按时间戳倒序this.wrongArr=arr;if(arr&&arr.length>0){arr.forEach((element:TopicItemType)=>{letmodel:WrongModel=newWrongModel();model.isDelete=false;model.keyID=element.keyID;model.title=element.title;this.wrongQues.push(model);});}}catch(e){this.wrongArr=[];this.wrongQues=[];}}数据加载要点:
- 从
PreferConstant.ERROR_RECORDS读取 - 按时间戳
jlStamp倒序排列,最新错误在最前面 - 转换为
WrongModel列表用于 UI 显示
3.3 空状态处理
if(this.wrongQues.length<=0){Text('暂无易错词汇,加油学习吧').fontSize($r('sys.float.Body_S')).fontColor($r('sys.color.icon_secondary')).height('100%');}当错题列表为空时,居中显示友好的提示文字。这是用户体验的基本要求——不因数据为空而显示空白页面。
3.4 错题列表
@BuilderwrongListBuilder(){List(){ForEach(this.wrongQues,(item:WrongModel,idx:number)=>{ListItem(){MyWrongItem({isSelectDelete:this.isSelectDelete,item:item,changeCollectionItem:()=>{/* 切换选中状态 */},idx:idx})}.swipeAction({end:{builder:()=>{this.itemEndComponent(idx);}}}).onClick(()=>{// 点击进入答题页面letrouterModel:QuestionsRouterModel=newQuestionsRouterModel();routerModel.datalist=[this.wrongArr[idx]];routerModel.sourceType='易错词汇';RouterModule.push({url:RouterMap.ANSWER_QUESTIONS_PAGE,param:routerModel});});});}}列表支持两种操作:
- 左滑删除:通过
swipeAction实现单条删除 - 点击进入练习:点击错题跳转到答题页面,
sourceType标记为"易错词汇"
3.5 批量删除
@BuilderdeleteBuilder(){Row(){Image($r('app.media.icon_delete'));Text('删除');}.opacity(this.selectCount>0?1:0.4).visibility(this.isSelectDelete?Visibility.Visible:Visibility.None).onClick(()=>{this.deleteSelectData();});}批量删除模式:点击顶部的删除图标进入选择模式,勾选要删除的错题,点击"删除"按钮批量清除。
deleteSelectData(){letarr:string[]=[];this.wrongQues.forEach((model:WrongModel)=>{if(model.isDelete){arr.push(model.keyID);}});this.wrongQues=this.wrongQues.filter(it=>!it.isDelete);this.wrongArr=this.wrongArr.filter(it=>!arr.includes(it.keyID));PreferenceUtil.getInstance().put(PreferConstant.ERROR_RECORDS,this.wrongArr);}删除时同时更新内存中的wrongQues和wrongArr,以及持久化存储。
四、收藏的展示与使用
收藏的数据结构和使用模式与错题本完全一致,只是存储 Key 不同(EXAM_PREFER_COLLECT)。在 AnswerQuestionsPage 中,从收藏列表进入答题时:
if(this.sourceRouterModel?.sourceType==='我的收藏'){RouterModule.pop();// 收藏模式点击返回直接退出}收藏列表页MyCollectionPage的 UI 结构和 MyWrongPage 基本一致,同样支持删除和点击进入答题。
五、数据管理要点
5.1 存储格式
错题和收藏都使用TopicItemType[]数组存储。每个元素包含:
interfaceTopicItemType{keyID:string// 唯一标识title:string// 题目内容type:string// 题型rightQues:string[]// 正确答案selectQues:string[]// 用户选择ques:AnswerItemType[]// 选项列表parse:string// 解析jlStamp:number// 记录时间戳// ...}5.2 数据隔离
错题和收藏使用不同的 Key,互不干扰。同一个题目可以同时出现在错题本和收藏中。
5.3 性能考虑
Preferences 适合存储较小体量的数据。对于错题和收藏这种单用户最多几百条的场景完全足够。如果将来数据量增长到数千条,可以考虑迁移到关系型数据库(如@kit.ArkData中的relationalStore)。
六、总结
错题本和收藏功能展示了 Preferences 在"用户私有数据"存储中的典型应用。自动记录 + 手动收藏的组合,覆盖了"系统推荐"和"用户自选"两种学习素材收集模式。结合空状态处理、批量操作、滑动删除等交互细节,为用户提供了完整的数据管理体验。