第22次:每日一题功能

本文讲解项目里的 QuizPage.ets,也就是“每日一题”页面。这个页面虽然不长,但它是一个很典型的轻量练习场景:只做一题、快速反馈、降低学习门槛。


为什么要做每日一题

一个学习应用如果每次都要求用户进入完整课程、完整模块或者完整题库,用户很容易因为“今天没时间”而直接放弃。每日一题的价值就在于:

  • 进入成本低
  • 完成速度快
  • 每天都有一个明确的小目标
  • 能帮助用户保持学习连续性

当前项目里的每日一题功能虽然不复杂,但思路非常清楚:由 TutorialService.getDailyQuestion() 选出当天题目,再由 QuizPage 负责展示、作答和反馈。

QuizPage.aboutToAppear

TutorialService.getDailyQuestion

dailyQuestion 写入页面状态

展示题目、代码片段、选项

用户选择答案

submitAnswer

展示对错结果和解析


一、这个页面为什么不直接复用完整题库页面

这是产品设计上很重要的一点。每日一题和完整题库虽然都属于答题功能,但它们的目标不一样:

  • 每日一题:轻量、快速、鼓励坚持
  • 面试题库:系统练习、统计分析、错题沉淀

因此当前项目给它们做了不同页面,是很合理的。QuizPage 只保留最必要的结构:

  • 顶部标题
  • 一道题
  • 选项列表
  • 提交按钮
  • 结果反馈

页面短小、节奏快,这正是“每日一题”应该有的感觉。


二、页面初始化时做了哪两件事

aboutToAppear() 中主要有两个动作:

  1. this.dailyQuestion = TutorialService.getDailyQuestion()
  2. this.startTime = Date.now()

第一步解决“今天出哪道题”,第二步为未来统计答题用时预留基础数据。虽然当前页面没有像完整练习页那样做复杂统计,但这种先把数据点记上的习惯非常好,因为后续扩展会容易很多。

这也提醒我们:在做功能时,哪怕当前版本不一定立即用到所有字段,只要它们是明显有价值的行为数据,就可以提前规划。


三、题目展示结构为什么很适合手机端

QuestionContent(question) 的布局很清楚:

  • 题干
  • 可选代码片段
  • 选项列表
  • 提交按钮

其中有一个细节尤其值得学:如果题目带 codeSnippet,页面不会另开复杂组件,而是直接用等宽字体加浅色背景展示。这种处理对每日一题很合适,因为:

  • 代码片段一般不会太长
  • 用户只需要快速观察
  • 不需要像课时详情那样进入完整代码阅读模式

也就是说,页面设计要和场景匹配。不是所有代码展示都必须上 CodeBlock,轻量场景就用轻量方案。


四、选项状态为什么要分三层颜色

在每日一题页面中,选项视觉状态主要由三个方法控制:

  • getOptionTextColor
  • getOptionBgColor
  • getOptionCardBg

虽然逻辑简单,但体验上很重要,因为它帮助用户清楚知道:

  • 我有没有选中某个答案
  • 当前按钮是否可以提交
  • 答案提交后我要关注哪里

这类“通过颜色反馈当前交互状态”的处理,是移动端练习页非常基础但非常重要的一步。很多初学者会只顾着把选项列出来,却忽略选中反馈,最终页面可用性会明显下降。


五、提交后为什么直接展示解析

submitAnswer() 在当前版本中非常轻,只做了一件事:把 showResult 设为 true。随后页面进入结果视图,展示:

  • 回答正确还是错误
  • 正确答案
  • 题目解析
  • 返回首页按钮

这种即时反馈特别适合每日一题场景,因为用户做完一题后最关心的不是复杂统计,而是:

  • 我做对了吗
  • 为什么对 / 为什么错

所以结果页把“解析”放在很显眼的位置,是非常正确的。


六、每日一题和进度系统是什么关系

虽然 QuizPage 当前没有直接把结果写入 ProgressService,但从产品角度看,它仍然和学习系统有关。因为:

  • 首页的 HeroBanner 会通过“每日一题入口”把用户带到这里。
  • 用户完成每日一题,本质上是一种学习行为。
  • 未来完全可以把每日一题答题记录接入统计系统。

这也说明项目当前处于一个很典型的阶段:主链路已经建立,部分能力还可以继续扩展。写教程时不应该把它讲成“已经是一套完整考试系统”,而是应该诚实说明当前实现边界。


七、自己动手走一遍最有效

建议你按下面步骤操作:

  1. 打开首页,点击 HeroBanner 中的每日一题入口。
  2. 观察题目、代码片段、选项是否都正常渲染。
  3. 先故意答错一次,看结果页如何显示正确答案与解析。
  4. 再重新进入,答对一次,看图标和文案变化。
  5. 思考如果你要增加“下一题”或“换一题”功能,页面状态要怎么调整。

只要这几步自己过一遍,这个页面的设计思路就很容易吃透。


八、本篇常见坑

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())

按这个顺序动手

  1. 打开 entry/src/main/ets/pages/QuizPage.ets
  2. aboutToAppear(),确认每日一题来自 TutorialService
  3. 再找 submitAnswer()ResultContent(),看提交后页面如何切到结果态。

课后练习

  1. 思考如果要为每日一题增加“已答过”标识,你会把状态存到哪里。
  2. 对比 QuizPageQuizPracticePage,总结两者在目标和复杂度上的区别。
  3. 设想一个“错题加入错题本”的扩展方案,写出你会新增的服务调用点。
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐