灯光模拟HarmonyOS应用实战-10-科二灯光基础如何按图标、上车检查、夜间场景切换

![封面

科二灯光基础覆盖图标认知、上车检查、夜间场景。源码用 subject2CategoryCategoryChip 控制筛选条件。

本文只看科二分类练习。科二灯光基础被拆成图标认知、上车检查、夜间场景三类,当前分类直接决定抽题范围。

先看这篇要解决的具体问题

科二分类 的核心不是某个 ArkTS 语法点,而是边界划分。一个练习功能至少有四个层面:入口从哪里来,数据在哪里定义,用户动作怎样改变状态,结果是否保存。只看 UI 会漏掉判题,只看题库会漏掉重置,只看存储又解释不了按钮为什么这样设计。

更稳的做法是把本文的功能拆成下面六步:默认图标认知 -> 点击分类 Chip -> 更新分类状态 -> 重启科二练习 -> 按 category 过滤题 -> 答题写记录。后面所有代码片段都服务于这条线,不额外展开无关模块。

流程图

源码边界和文件分工

源码位置作用
entry/src/main/ets/pages/Index.ets页面状态、Builder、入口分发和业务处理
entry/src/main/ets/models/QuestionBank.ets模块入口、题库、按钮动作和命令定义

这些节选不是为了贴完整文件,而是为了把当前链路看清楚。

Index.ets 维护 subject2Category 并用 Chip 切换,QuestionBank.ets 的科二种子提供分类题源,selectQuestions 执行过滤。

源码路径:D:\ProgramData\huawei\lesson\The_kemusan
阅读顺序:入口 -> 数据 -> 状态 -> UI -> 本地记录
写作边界:只解释当前源码能证实的功能,不替代真机运行和发布验证

subject2Category 决定当前题池

源码节选:entry/src/main/ets/pages/Index.ets:370

  @Builder
  SubjectTwoTabs() {
    Row({ space: 8 }) {
      this.CategoryChip('图标认知')
      this.CategoryChip('上车检查')
      this.CategoryChip('夜间场景')
    }
    .width('100%')
    .padding({ left: 20, right: 20 })
    .margin({ bottom: 12 })
  }

  @Builder
  CategoryChip(label: string) {
    Text(label)
      .fontSize(14)
      .fontWeight(FontWeight.Medium)
      .fontColor(this.subject2Category === label ? Color.White : $r('app.color.text_secondary'))
      .textAlign(TextAlign.Center)
      .padding({ left: 12, right: 12, top: 8, bottom: 8 })
      .backgroundColor(this.subject2Category === label ? $r('app.color.accent') : $r('app.color.panel_bg'))
      .borderRadius(16)
      .border({ width: 1, color: $r('app.color.border_subtle') })
      .layoutWeight(1)
      .onClick(() => {
        this.subject2Category = label;
        this.startTheoryPractice(SUBJECT_TWO, '科二灯光基础', label);
      })
  }

源码节选:entry/src/main/ets/models/QuestionBank.ets:408

const SUBJECT_TWO_SEEDS: GeneratedQuestionSeed[] = [
  {
    category: '图标认知',
    scenario: '看到仪表盘绿色近光灯图标',
    correct: '确认近光灯处于开启状态',
    wrongOne: '判断远光灯处于开启状态',
    wrongTwo: '判断双闪处于开启状态',
    wrongThree: '认为灯光系统故障',
    explanation: '科二基础认知需要能区分近光、远光、雾灯和双闪图标。'
  },
  {
    category: '图标认知',
    scenario: '看到两个转向指示箭头同时闪烁',
    correct: '通常表示危险报警闪光灯开启',
    wrongOne: '表示远光灯开启',
    wrongTwo: '表示近光灯关闭',
    wrongThree: '表示制动灯开启',
    explanation: '左右转向指示同时闪烁一般对应危险报警闪光灯。'
  },
  {
    category: '图标认知',
    scenario: '识别雾灯图标时',
    correct: '注意前后雾灯图标方向和颜色差异',
    wrongOne: '只看车速表指针',
    wrongTwo: '把雾灯图标都当作远光灯',
    wrongThree: '不需要识别雾灯图标',
    explanation: '雾灯在恶劣天气中很关键,应能通过仪表图标确认状态。'
  },
  {
    category: '上车检查',
    scenario: '上车准备开始灯光项目',
    correct: '先确认灯光开关和仪表状态',
    wrongOne: '直接开启所有灯光',
    wrongTwo: '忽略灯光状态直接起步',
    wrongThree: '只检查座椅不看仪表',
    explanation: '上车检查要先确认车辆初始状态,避免带灯或误灯操作。'
  },
  {
    category: '上车检查',
    scenario: '发现远光灯处于开启状态',
    correct: '切回近光或关闭不必要灯光',
    wrongOne: '保持远光灯继续练习',
    wrongTwo: '开启左转向灯代替处理',
    wrongThree: '开启车内照明灯即可',
    explanation: '远光误开会影响安全,应及时切换到正确状态。'
  },
  {
    category: '上车检查',
    scenario: '准备模拟夜间灯光考试',
    correct: '听到指令后再按场景操作',
    wrongOne: '提前连续点击多个灯光按钮',
    wrongTwo: '只开启双闪等待',
    wrongThree: '不看指令随意操作',
    explanation: '灯光考试重在听清指令,抢答和乱点容易造成错误。'
  },
  {
    category: '夜间场景',
    scenario: '夜间起步前需要确认车辆被看见',
    correct: '按指令开启前照灯或近光灯',
    wrongOne: '只开启远光灯照射前方',
    wrongTwo: '不开灯直接起步',
    wrongThree: '只开启倒车灯',
    explanation: '夜间起步应保证车辆照明状态正确,不能误用远光。'
  },
  {
    category: '夜间场景',
    scenario: '夜间通过人行横道前',
    correct: '减速观察并按要求远近光交替示意',
    wrongOne: '关闭灯光快速通过',
    wrongTwo: '只开启雾灯',
    wrongThree: '持续使用远光灯不变',
    explanation: '夜间通过人行横道应减速礼让,灯光示意用于提醒。'
  },
  {
    category: '夜间场景',
    scenario: '雨雪天视线变差时',
    correct: '开启近光灯并提高可见性',
    wrongOne: '关闭灯光避免耗电',
    wrongTwo: '只使用远光灯',
    wrongThree: '只开转向灯行驶',
    explanation: '雨雪天气应使用合适灯光提高车辆可见性。'
  }
];

科二题不是简单混合题池。图标认知和夜间场景训练目标不同,如果混在一起,用户很难集中补短板。

subject2Category 是题池输入,不只是 UI 选中状态。点击 Chip 后必须重新调用 startTheoryPractice

分类 Chip 触发重新抽题

源码节选:entry/src/main/ets/pages/Index.ets:1116

  private startTheoryPractice(subject: string, mode: string, category: string): void {
    this.activeSubject = subject;
    this.activeMode = mode;
    this.activeQuestions = this.selectTestQuestions(subject, category);
    this.activeQuestionIndex = 0;
    this.selectedAnswer = '';
    this.answerMessage = '请选择答案';
    this.answerCorrect = true;
  }

源码节选:entry/src/main/ets/pages/Index.ets:1200

  private selectQuestions(subject: string, category: string): QuestionItem[] {
    const source = this.getAllQuestions();
    const result: QuestionItem[] = [];
    for (let index = 0; index < source.length; index++) {
      const question = source[index];
      if (question.subject === subject && (category.length === 0 || question.category === category)) {
        result.push(question);
      }
    }
    return result;
  }

  private selectTestQuestions(subject: string, category: string): QuestionItem[] {
    const source = this.selectQuestions(subject, category);
    const shuffled: QuestionItem[] = [];
    for (let index = 0; index < source.length; index++) {
      shuffled.push(source[index]);
    }
    for (let index = shuffled.length - 1; index > 0; index--) {
      const target = Math.floor(Math.random() * (index + 1));
      const current = shuffled[index];
      shuffled[index] = shuffled[target];
      shuffled[target] = current;
    }
    const result: QuestionItem[] = [];
    const maxCount = Math.min(THEORY_TEST_COUNT, shuffled.length);
    for (let index = 0; index < maxCount; index++) {
      result.push(shuffled[index]);
    }
    return result;
  }

分类 Chip 的职责很清楚:更新分类状态,然后用该分类重启科二练习。它不需要自己过滤题目。

真正的过滤发生在 selectQuestions:科目要等于 subject2,分类为空或等于当前分类才进入结果。

结构图

过滤逻辑必须同时看科目和分类

源码节选:entry/src/main/ets/pages/Index.ets:1266

  private answerTheoryQuestion(answer: string): void {
    if (this.selectedAnswer.length > 0 || this.activeQuestions.length === 0) {
      return;
    }
    const question = this.getCurrentQuestion();
    const correctAnswer = getQuestionAnswer(question);
    const correct = answer === correctAnswer;
    const correctText = this.getOptionText(question, correctAnswer);
    const selectedText = this.getOptionText(question, answer);
    this.selectedAnswer = answer;
    this.answerCorrect = correct;
    this.answerMessage = correct ? '回答正确' : `回答错误,正确选项:${correctAnswer}. ${correctText}`;
    this.addSimpleRecord(question.subject, this.activeMode, correct, correct ? `答对:${question.title}` : `答错:${question.title}`,
      correct ? '' : question.id, correct ? '' : correctText, correct ? '' : selectedText);
  }

分类状态会影响 activeQuestions、当前题索引、已选答案和反馈文案。切换分类时这些状态都要回到新题池的初始状态。

迁移时把分类作为抽题参数,而不是作为显示标签。这样“重新开始”和“下一题”都会留在同一分类里。

迁移到自己的 HarmonyOS 项目

迁移顺序建议保持保守:先抽模型,再接入口,然后接页面,最后接本地记录。不要一上来就把所有 Builder、题库和存储混在一个文件里。当前主题 科二分类 可以按下面的最小切片实现:

1. 定义模型:Subject、QuestionItem、PracticeRecord、action 常量。
2. 定义数据:模块入口、题库、灯光命令或历史记录结构。
3. 定义状态:当前页面、当前题、当前灯光、反馈文案和记录列表。
4. 定义 UI:状态行、题面、按钮区、历史卡片或搜索结果。
5. 定义验证:正确流、错误流、重置流和边界流都要能复现。

分类输入稳定后,后续增加“灯光开关位置”或“仪表提示”也只是新增分类和题源。

验证路线

  1. 进入科二默认图标认知
  2. 切分类后题目随之变化
  3. 下一题不跳到其他分类

除了上面的业务核对,还建议记录一个最小验证表:

主题:科二分类
输入:入口点击、按钮点击、题目选择或关键词输入
状态:currentPage、lightState、timeLeft、selectedAnswer、records
输出:页面提示、状态高亮、历史记录、搜索结果
边界:返回首页、重置、重复点击、空记录、超时

这里的验证是面向源码链路的核对,不等同于真机兼容性、性能或上架流程。如果要证明设备行为,还需要单独构建安装并操作页面。

常见问题与处理方式

现象常见原因处理方式
页面能点但结果不对按钮 action 与题库 action 没有对齐先看模型常量,再看题库命令和处理函数
返回后状态残留计时器或闪烁器没有释放核对 stopTimer、stopAltFlash 和 reset 方法
历史记录不完整写记录时只保存了展示文案补齐 subject、mode、correctOption、selectedOption

科二题不变时先查 Chip 点击是否重新抽题,再查题源的 category 文案是否和筛选条件完全一致。

小结

科二分类 这条线把灯光模拟从“能点按钮”推进到“可练习、可判题、可复盘”。The_kemusan 的代码没有把所有能力做成复杂框架,而是用清晰的模型、页面状态和本地记录完成闭环。写 CSDN 文章时,把这条闭环讲清楚,比堆更多 API 名词更有价值。
](https://i-blog.csdnimg.cn/direct/ec4a4f74b93a4a018066e9706ef9fe32.png#pic_center)
在这里插入图片描述
在这里插入图片描述

共67个01.嵌入 Non-Base14 字体-批量用.eal02.使用ICC配置标记图像----.eal03.删除所有图片.eal04.删除重叠的角线.eal05.1.通用定位点.eal05.2.中号机-A3--量多用这台机-割的准.eal05.3.小号机-不干胶-A3--咬位左边20mm.eal05.4_B2.eal07.翻书线-横向.eal08.翻书线-纵向.eal09.加透明色块.eal10.取消所有叠印.eal11.局部更改色彩.eal12.建立剪切蒙版.eal12.放蒙版.eal122232132132132132.eal13.曲线.eal14.条线粗细0.15改为0.08.eal15.条线粗细0改为0.15.eal16.条线粗细0改为0.25.eal17.条线粗细xx5改为0.15.eal18.细线加粗到0.08mm.eal19.添加可变文本-中间一刀切.eal20.添加可变文本-批量用-居中超大字放中间.eal20.添加可变文本-批量用-没页码.eal20.添加可变文本-批量用.eal21.A4-5.eal21.添加复制的图形右.eal22.添加复制的图形左.eal23.添加成品框 单黑.eal24.添加成品框 白色-2.5mm.eal25.添加成品框 白色-2mm.eal26.添加红色成品框 专色.eal27.添加页尾偶数页.eal28.添加页尾奇数页.eal29.添加页码-居中.eal30.添加页码.eal31.白色做4色爆肥.eal31.移动局部内容上.eal31.移动局部内容下.eal32.移动局部内容右.eal33.局部缩小内容.eal33.更改局部图片位置.eal33.移动局部内容左.eal34.移除出血、裁剪、裁切、作品框.eal35.转3色-czl.eal36.选中单个色-移动内容.eal37.选择第个页面转180度.eal38.页面对半分割.eal39.十折页添加折位线交互.eal40.作品框设置为裁切框.eal40.将所有专色设置为裁切框.eal40.指定一个专色设置为裁切框.eal41.压缩尺寸_留出血.eal41.拼合图层中对象.eal42.只选择超大尺寸-按比例缩小到A3.eal42.只选择超大尺寸-按比例缩小到A4.eal43.裁剪至可见.eal44.旋转内容.eal45.直接加3毫米角线.eal46.移除图形.eal47.删除不可见数据.eal48.内容上下居中.eal
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值