第15篇:答题交互组件——Exam 与 SelectComponent 全解析

img

一、引言

答题交互是驾考应用最核心的用户交互场景。DriverLicenseExam 项目的 Exam 组件和 SelectComponent 组件共同实现了完整的答题交互体验,包括试题展示、选项选择、答案判断、答题卡、倒计时等。本文将深入解析这两个组件的实现。

二、Exam 组件架构

2.1 组件职责

Exam 组件是考试页面的核心容器,负责:

  • 管理试题列表(Swiper 滑动切换)
  • 展示倒计时(模拟考试)
  • 管理答题卡浮层
  • 处理模拟考试结束弹窗

2.2 组件接口

// components/exam/src/main/ets/components/Exam.ets
@Entry
@ComponentV2
export struct Exam {
  @Param @Require appPathStack: NavPathStack;  // 路由栈
  @Param @Require examManager: ExamManager;     // 试卷数据
  @Param mockExamCall: (score: number) => void = (score: number) => {};  // 模拟考试回调
  
  private examController: ExamController = ExamController.instance;
  @Local remainTime: number = 0;               // 剩余时间
  @Local isShowProblemListSheet: boolean = false;  // 答题卡显示
  @Local currentIndex: number = 0;             // 当前第几题
  @Local isStop: boolean = false;              // 暂停状态
  private readonly renderWindowSize: number = 2;  // 渲染窗口大小
}

三、答题交互流程

3.1 试题渲染

build() {
  Column() {
    // 模拟考试倒计时
    this.showRemainTime();

    // 试题列表(Swiper 滑动切换)
    Swiper(examController.swiperController) {
      ForEach(this.examManager.examDetails, (item: ExamDetail, examIndex: number) => {
        Column({ space: 12 }) {
          if (this.shouldRenderQuestion(examIndex)) {
            SelectComponent({
              item: item,
              changeCorrectNum: (isAdd: boolean) => {
                isAdd ? this.examManager.correctNumber++ : this.examManager.correctNumber--;
              },
            });
          }
        }
      });
    }
    // ... 底部导航(答题卡、上一题、下一题)
  }
}

3.2 渲染窗口优化

// 只渲染当前题附近的题目,提高大题库性能
private readonly renderWindowSize: number = 2;

shouldRenderQuestion(examIndex: number): boolean {
  return Math.abs(examIndex - this.currentIndex) <= this.renderWindowSize;
}

四、模拟考试倒计时

4.1 倒计时实现

aboutToAppear(): void {
  this.currentIndex = this.examManager.currentQuestionId + 1;

  if (this.examManager.timeLimit !== 0) {
    this.remainTime = this.examManager.timeLimit * 60;
    let showDialog: boolean = true;
    
    setInterval(() => {
      if (showDialog && !this.isStop) {
        if (this.remainTime >= 1) {
          this.remainTime--;
        } else {
          showDialog = false;
          this.isStop = true;
          this.examSubmitDialog.open();
        }
      }
    }, 1000);
  }
}

4.2 时间格式化

@Builder
showRemainTime() {
  if (this.examManager.timeLimit !== 0) {
    Row() {
      Image($r('app.media.ic_time')).width(16).height(16);
      Text(`${Math.floor(this.remainTime / 60)}:${String(this.remainTime % 60).padStart(2, '0')}`)
        .fontSize(14)
        .fontColor('#FF6B6B');
    }
  }
}

五、答题卡实现

5.1 答题卡数据

// 生成答题卡数据
get answerCardData() {
  return this.examManager.examDetails.map((item, index) => ({
    index: index + 1,
    status: item.isCorrect === undefined ? 'unanswered' : 
            item.isCorrect ? 'correct' : 'wrong',
    isCurrent: index === this.currentIndex
  }));
}

5.2 答题卡交互

// 底部打开答题卡浮层
Button('答题卡')
  .onClick(() => {
    this.isShowProblemListSheet = true;
  })
  .bindSheet($$this.isShowProblemListSheet, this.answerCardSheet(), {
    height: 400,
    title: { title: '答题卡' },
  });

六、总结

Exam 和 SelectComponent 组件的设计体现了 ArkUI 组件化的最佳实践:

  1. 职责分离:Exam 管理流程,SelectComponent 管理交互
  2. 性能优化:渲染窗口机制减少不必要的渲染
  3. 状态管理@Local 管理内部状态,@Param 接收外部数据
  4. 用户体验:倒计时、答题卡、弹窗等交互细节

关键源码文件:

  • components/exam/src/main/ets/components/Exam.ets — 考试主组件
  • components/exam/src/main/ets/components/SelectComponent.ets — 选择组件
  • components/exam/src/main/ets/controller/ExamController.ets — 考试控制器
Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐