HarmonyOS APP<玩转React>开源教程二十二:每日一题功能
第22次:每日一题功能
本文讲解项目里的
QuizPage.ets,也就是“每日一题”页面。这个页面虽然不长,但它是一个很典型的轻量练习场景:只做一题、快速反馈、降低学习门槛。
为什么要做每日一题
一个学习应用如果每次都要求用户进入完整课程、完整模块或者完整题库,用户很容易因为“今天没时间”而直接放弃。每日一题的价值就在于:
- 进入成本低
- 完成速度快
- 每天都有一个明确的小目标
- 能帮助用户保持学习连续性
当前项目里的每日一题功能虽然不复杂,但思路非常清楚:由 TutorialService.getDailyQuestion() 选出当天题目,再由 QuizPage 负责展示、作答和反馈。
一、这个页面为什么不直接复用完整题库页面
这是产品设计上很重要的一点。每日一题和完整题库虽然都属于答题功能,但它们的目标不一样:
- 每日一题:轻量、快速、鼓励坚持
- 面试题库:系统练习、统计分析、错题沉淀
因此当前项目给它们做了不同页面,是很合理的。QuizPage 只保留最必要的结构:
- 顶部标题
- 一道题
- 选项列表
- 提交按钮
- 结果反馈
页面短小、节奏快,这正是“每日一题”应该有的感觉。
二、页面初始化时做了哪两件事
aboutToAppear() 中主要有两个动作:
this.dailyQuestion = TutorialService.getDailyQuestion()this.startTime = Date.now()
第一步解决“今天出哪道题”,第二步为未来统计答题用时预留基础数据。虽然当前页面没有像完整练习页那样做复杂统计,但这种先把数据点记上的习惯非常好,因为后续扩展会容易很多。
这也提醒我们:在做功能时,哪怕当前版本不一定立即用到所有字段,只要它们是明显有价值的行为数据,就可以提前规划。
三、题目展示结构为什么很适合手机端
QuestionContent(question) 的布局很清楚:
- 题干
- 可选代码片段
- 选项列表
- 提交按钮
其中有一个细节尤其值得学:如果题目带 codeSnippet,页面不会另开复杂组件,而是直接用等宽字体加浅色背景展示。这种处理对每日一题很合适,因为:
- 代码片段一般不会太长
- 用户只需要快速观察
- 不需要像课时详情那样进入完整代码阅读模式
也就是说,页面设计要和场景匹配。不是所有代码展示都必须上 CodeBlock,轻量场景就用轻量方案。
四、选项状态为什么要分三层颜色
在每日一题页面中,选项视觉状态主要由三个方法控制:
getOptionTextColorgetOptionBgColorgetOptionCardBg
虽然逻辑简单,但体验上很重要,因为它帮助用户清楚知道:
- 我有没有选中某个答案
- 当前按钮是否可以提交
- 答案提交后我要关注哪里
这类“通过颜色反馈当前交互状态”的处理,是移动端练习页非常基础但非常重要的一步。很多初学者会只顾着把选项列出来,却忽略选中反馈,最终页面可用性会明显下降。
五、提交后为什么直接展示解析
submitAnswer() 在当前版本中非常轻,只做了一件事:把 showResult 设为 true。随后页面进入结果视图,展示:
- 回答正确还是错误
- 正确答案
- 题目解析
- 返回首页按钮
这种即时反馈特别适合每日一题场景,因为用户做完一题后最关心的不是复杂统计,而是:
- 我做对了吗
- 为什么对 / 为什么错
所以结果页把“解析”放在很显眼的位置,是非常正确的。
六、每日一题和进度系统是什么关系
虽然 QuizPage 当前没有直接把结果写入 ProgressService,但从产品角度看,它仍然和学习系统有关。因为:
- 首页的 HeroBanner 会通过“每日一题入口”把用户带到这里。
- 用户完成每日一题,本质上是一种学习行为。
- 未来完全可以把每日一题答题记录接入统计系统。
这也说明项目当前处于一个很典型的阶段:主链路已经建立,部分能力还可以继续扩展。写教程时不应该把它讲成“已经是一套完整考试系统”,而是应该诚实说明当前实现边界。
七、自己动手走一遍最有效
建议你按下面步骤操作:
- 打开首页,点击 HeroBanner 中的每日一题入口。
- 观察题目、代码片段、选项是否都正常渲染。
- 先故意答错一次,看结果页如何显示正确答案与解析。
- 再重新进入,答对一次,看图标和文案变化。
- 思考如果你要增加“下一题”或“换一题”功能,页面状态要怎么调整。
只要这几步自己过一遍,这个页面的设计思路就很容易吃透。
八、本篇常见坑
1. 以为每日一题必须复用完整练习页
不一定。场景不同,页面复杂度也应该不同。
2. 题目解析不展示
每日一题最重要的就是快速反馈,不展示解析意义会大打折扣。
3. 把代码片段展示做得过重
每日一题讲究轻量,代码片段只要清晰可读就够了。
4. 忽略空状态和容错
如果未来某天没有生成题目,页面应当有兜底方案,这一点值得后续完善。
本篇小结
每日一题功能虽然轻,但它很能体现产品节奏感。它告诉我们一个很重要的设计原则:不是所有功能都要做成“大而全”,有些场景更适合短、平、快。
当前项目的 QuizPage 正是这样一个轻练习页:
- 一页只做一题
- 提交后立即反馈
- 用最少交互完成一次小学习闭环
跟着真实源码继续往下看
每日一题页面加载当日题目的真实代码如下:
aboutToAppear(): void {
this.dailyQuestion = TutorialService.getDailyQuestion();
this.startTime = Date.now();
}
题目渲染和提交按钮的真实代码如下:
ForEach(question.options, (option: string, index: number) => {
Row() {
Text(String.fromCharCode(65 + index))
Text(option)
}
.onClick(() => {
this.selectedAnswer = index;
})
})
Button('提交答案')
.enabled(this.selectedAnswer >= 0)
.onClick(() => this.submitAnswer())
按这个顺序动手
- 打开
entry/src/main/ets/pages/QuizPage.ets。 - 找
aboutToAppear(),确认每日一题来自TutorialService。 - 再找
submitAnswer()和ResultContent(),看提交后页面如何切到结果态。
课后练习
- 思考如果要为每日一题增加“已答过”标识,你会把状态存到哪里。
- 对比
QuizPage和QuizPracticePage,总结两者在目标和复杂度上的区别。 - 设想一个“错题加入错题本”的扩展方案,写出你会新增的服务调用点。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)