CountDownTimer的使用以及解决Cancel无效的问题

GPT-oss:20b

GPT-oss:20b

图文对话
Gpt-oss

GPT OSS 是OpenAI 推出的重量级开放模型,面向强推理、智能体任务以及多样化开发场景

http://blog.youkuaiyun.com/liuweiballack/article/details/46605787

您可能感兴趣的与本文相关的镜像

GPT-oss:20b

GPT-oss:20b

图文对话
Gpt-oss

GPT OSS 是OpenAI 推出的重量级开放模型,面向强推理、智能体任务以及多样化开发场景

package com.weishitech.jtwzjiaotongcx.fragment.lilv; import static android.view.View.GONE; import static android.view.View.VISIBLE; import android.app.AlertDialog; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.os.CountDownTimer; import android.os.Handler; import android.os.Looper; import android.util.Log; import android.util.TypedValue; import android.view.View; import android.widget.Toast; import androidx.core.content.ContextCompat; import androidx.recyclerview.widget.GridLayoutManager; import androidx.viewpager2.widget.ViewPager2; import com.google.gson.Gson; import com.hfd.common.net.GenericsCallback; import com.hfd.common.net.HttpBuiler; import com.hfd.common.net.JsonGenericsSerializator; import com.hfd.common.net.JsonUtil; import com.hfd.common.net.Url; import com.hfd.common.util.ToastUtil; import com.weishitech.jtwzjiaotongcx.R; import com.weishitech.jtwzjiaotongcx.adtakubase.activity.BaseBindActivity; import com.weishitech.jtwzjiaotongcx.bean.DrivingQueryReqDto; import com.weishitech.jtwzjiaotongcx.bean.Question; import com.weishitech.jtwzjiaotongcx.bean.QuestionSource; import com.weishitech.jtwzjiaotongcx.databinding.ActivityTestBinding; import com.weishitech.jtwzjiaotongcx.dialog.DialogUtils; import com.weishitech.jtwzjiaotongcx.fragment.Adapter.QuestionAdapter; import com.weishitech.jtwzjiaotongcx.fragment.Adapter.QuestionNumberAdapter; import com.weishitech.jtwzjiaotongcx.fragment.Adapter.SpaceItemDecoration; import com.weishitech.jtwzjiaotongcx.utils.LogUtil; import com.weishitech.jtwzjiaotongcx.utils.OnMultiClickListener; import java.util.ArrayList; import java.util.Date; import java.util.List; import okhttp3.Call; public class TestActivity extends BaseBindActivity<ActivityTestBinding> { private List<Question.DataBean> list; int subject = 1; private int type; private QuestionNumberAdapter adapter; private QuestionAdapter adapter1; private static final int PAGE_SIZE = 10; private int currentPageIndex = 1; // 当前显示第几页 private boolean isShowingDialog = false; private int correct, wrong; private Date examTime; private Date submitTime; private CountDownTimer countDownTimer; private long timeLeftInMillis = 45 * 60 * 1000; // 45分钟 private boolean timerRunning = false; // 添加这个字段:记录每道题是否已作答 private boolean[] answeredArray; // 初始化为 new boolean[list.size()] private int currentPageStart = 0; // 添加这个字段 private Runnable autoNextRunnable; // 可取消的延迟任务 private Handler mainHandler = new Handler(Looper.getMainLooper()); // 用于保存各类题目的索引(从0开始) private List<Integer> unansweredList = new ArrayList<>(); private List<Integer> wrongList = new ArrayList<>(); private List<Integer> correctList = new ArrayList<>(); @Override protected void init() { type = getIntent().getIntExtra("type", 0); if (type == 1){ subject = 1; }else{ subject = 4; } mBinding.cbExamSelect.setChecked(true); // 根据类型设置不同的UI模式 if (type == 11 || type == 12 || type == 13) { mBinding.rlExamNormal.setVisibility(GONE); mBinding.tvExamSubmit.setVisibility(VISIBLE); mBinding.llExamSimulation.setVisibility(VISIBLE); startTimer(); examTime = new Date(); } else { mBinding.rlExamNormal.setVisibility(VISIBLE); mBinding.tvExamSubmit.setVisibility(GONE); mBinding.llExamSimulation.setVisibility(GONE); } mBinding.ivBack.setOnClickListener(new OnMultiClickListener() { @Override public void onMultiClick(View v) { showExitConfirmDialog(); } }); //提交试卷 mBinding.tvExamSubmit.setOnClickListener(new OnMultiClickListener() { @Override public void onMultiClick(View v) { submitExam(); } }); mBinding.tvExamStart.setOnClickListener(new OnMultiClickListener() { @Override public void onMultiClick(View v) { switchToStartMode(); } }); mBinding.tvExamAnswer.setOnClickListener(new OnMultiClickListener() { @Override public void onMultiClick(View v) { switchToAnswerMode(); } }); // 初始化 RecyclerView mBinding.rvQuestionNumbers.setLayoutManager( new GridLayoutManager(this, 5) // 每行 10 个 ); // 添加间距:垂直 8dp,水平 4dp int verticalSpacing = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 2, getResources().getDisplayMetrics()); int horizontalSpacing = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 2, getResources().getDisplayMetrics()); mBinding.rvQuestionNumbers.addItemDecoration( new SpaceItemDecoration(verticalSpacing, horizontalSpacing) ); setupViewPager(); requestQuestions(); } private void setupViewPager() { list = new ArrayList<>(); adapter1 = new QuestionAdapter(list, type, new QuestionAdapter.OnAnswerSelectedListener() { @Override public void onAnswerSelected(int position, String answer) { // 可以在这里处理答案选择事件 LogUtil.info("题目 " + position + " 选择了答案: " + answer); } @Override public void onAnswerConfirmed(int position, boolean isCorrect) { // === 标记该题为已答 === if (position >= 0 && position < answeredArray.length) { answeredArray[position] = true; } if (isCorrect) { correct++; mBinding.tvExamCorrect.setText(String.valueOf(correct)); if (mBinding.cbExamSelect.isChecked() && position < list.size() - 1) { // 自动下一题 mBinding.viewPager.setCurrentItem(mBinding.viewPager.getCurrentItem() + 1); } } else { wrong++; mBinding.tvExamWrong.setText(String.valueOf(wrong)); } // 更新进度显示 updateProgress(); // === 通知 Adapter 更新题号列表 UI === if (adapter != null) { int posInPage = position - currentPageStart; if (posInPage >= 0 && posInPage < adapter.getItemCount()) { adapter.notifyItemChanged(posInPage); // 触发 onBindViewHolder } } // === 自动跳转逻辑开始 === boolean shouldAutoNext = mBinding.cbExamSelect.isChecked() && position < list.size() - 1; Log.d("AutoNext", "shouldAutoNext = " + shouldAutoNext + ", isChecked=" + mBinding.cbExamSelect.isChecked() + ", pos=" + position + ", size=" + list.size()); if (shouldAutoNext) { // 创建一个可复用的任务 final Runnable nextTask = () -> { int current = mBinding.viewPager.getCurrentItem(); if (current == position) { // 确保仍在此页 Log.d("AutoNext", "执行自动跳转到 " + (position + 1)); mBinding.viewPager.setCurrentItem(position + 1, true); } else { Log.d("AutoNext", "跳转取消:用户已手动翻页"); } }; // 清除之前的任务(防重复) if (autoNextRunnable != null) { mainHandler.removeCallbacks(autoNextRunnable); } autoNextRunnable = nextTask; // 提交延迟任务 mainHandler.postDelayed(autoNextRunnable, 6000); Log.d("AutoNext", "已设置 6 秒后自动跳转"); } } }); mBinding.viewPager.setAdapter(adapter1); mBinding.viewPager.setOffscreenPageLimit(3); // 预加载3个页面 // 添加页面切换监听 mBinding.viewPager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() { @Override public void onPageSelected(int position) { super.onPageSelected(position); // === 用户手动翻页时,取消自动跳转任务 === if (autoNextRunnable != null) { mainHandler.removeCallbacks(autoNextRunnable); } // 更新顶部计数器 updateQuestionCount(); // 计算目标页码(每20题一页) int targetPageIndex = (position / PAGE_SIZE) + 1; if (targetPageIndex != currentPageIndex) { // 如果当前页不包含该题,则切换到对应页 currentPageIndex = targetPageIndex; loadCurrentPage(); // 重新加载题号列表(如 21-40) } // 延迟一点再设置高亮,等 RecyclerView 数据刷新完成 mBinding.rvQuestionNumbers.post(() -> { if (adapter != null) { adapter.setCurrentQuestion(position); } }); } }); } /** * 更新题目计数显示 */ private void updateQuestionCount() { if (list != null && !list.isEmpty()) { String current = String.valueOf(mBinding.viewPager.getCurrentItem() + 1); String total = "/" + list.size(); mBinding.tvExamCurrNum.setText(current); mBinding.tvExamNum.setText(total); } } /** * 更新答题进度 */ private void updateProgress() { int answered = correct + wrong; int total = list.size(); LogUtil.info("答题进度: " + answered + "/" + total + ", 正确: " + correct + ", 错误: " + wrong); } private void setupQuestionNumberAdapter() { adapter = new QuestionNumberAdapter(list.size(),answeredArray, globalPosition -> { Toast.makeText(this, "跳转到第 " + (globalPosition + 1) + " 题", Toast.LENGTH_SHORT).show(); // === 计算并更新当前页码 === int targetPageIndex = (globalPosition / PAGE_SIZE) + 1; if (targetPageIndex != currentPageIndex) { currentPageIndex = targetPageIndex; loadCurrentPage(); // 加载新的题号页 } // 跳转页面 mBinding.viewPager.setCurrentItem(globalPosition, false); // 不平滑 // 这里你可以通过 ViewPager 或 Fragment 切换题目 // vpExam.setCurrentItem(globalPosition); adapter.setCurrentQuestion(globalPosition); // 判断是否是本页最后一个题 boolean isLastInCurrentPage = (globalPosition + 1) % PAGE_SIZE == 0 || globalPosition == list.size() - 1; if (isLastInCurrentPage) { maybeShowNextPageDialog(); // 智能判断是否弹框 } }); mBinding.rvQuestionNumbers.setAdapter(adapter); // 加载第一页 loadCurrentPage(); } // 检查是否需要加载下一批 private void loadCurrentPage() { if (adapter != null) { currentPageStart = (currentPageIndex - 1) * PAGE_SIZE; // 更新起始位置 adapter.loadPage(currentPageIndex, PAGE_SIZE); adapter.notifyDataSetChanged(); } } /** * 如果还有下一页,则弹出“是否进入下一组”对话框 * 否则直接提示“已完成所有题目” */ private void maybeShowNextPageDialog() { int nextPageStartIndex = currentPageIndex * PAGE_SIZE; // 下一页第一个题的 globalPosition(0-based) if (nextPageStartIndex >= list.size()) { // 已经是最后一页了(比如当前是第5页,81-100),无需弹框 Toast.makeText(this, "恭喜完成全部题目!", Toast.LENGTH_SHORT).show(); return; } // 还有下一页,才弹框询问 new AlertDialog.Builder(this) .setTitle("进入下一组") .setMessage("已完成本组题目,是否加载下一组?") .setPositiveButton("确定", (dialog, which) -> { currentPageIndex++; loadCurrentPage(); // 加载下一页题号 }) .setNegativeButton("取消", null) .show(); } /** * 请求题目数据 */ private void requestQuestions() { showDialog(); HttpBuiler.postString(Url.getDrivingQuery) .content(JsonUtil.toJson(new DrivingQueryReqDto("c1", String.valueOf(subject), "rand"))) .build() .execute(new GenericsCallback<QuestionSource>(new JsonGenericsSerializator()) { @Override public void onError(Call call, Exception e, int id) { dissmiss(); LogUtil.info("=网络请求错误=============" + e.getMessage()); ToastUtil.showShortToast("网络请求失败: " + e.getMessage()); // 加载本地缓存或默认数据 loadDefaultQuestions(); } @Override public void onResponse(QuestionSource response, int id) { dissmiss(); if (response.getCode() == Url.SuccessCode_0) { if (response.getData() != null && !response.getData().isEmpty()) { // 转换数据格式 list = convertQuestionData(response.getData()); adapter1.updateQuestions(list); updateQuestionCount(); // === 初始化答题状态数组 === answeredArray = new boolean[list.size()]; java.util.Arrays.fill(answeredArray, false); // 初始都没答 setupQuestionNumberAdapter(); // <<< 添加这一行 // ToastUtil.showShortToast("加载成功,共" + list.size() + "道题目"); } else { ToastUtil.showShortToast("未查询到题目数据"); loadDefaultQuestions(); } } else { LogUtil.info("服务器返回错误: " + response.getMessage()); ToastUtil.showShortToast(response.getMsg()); loadDefaultQuestions(); } } }); } /** * 将QuestionSource转换为Question.DataBean */ private List<Question.DataBean> convertQuestionData(List<QuestionSource.DataBean> sourceList) { List<Question.DataBean> resultList = new ArrayList<>(); for (QuestionSource.DataBean source : sourceList) { Question.DataBean target = new Question.DataBean(); // 设置基本属性 target.setTitle(source.getQuestion()); target.setOp1(source.getItem1()); target.setOp2(source.getItem2()); target.setOp3(source.getItem3()); target.setOp4(source.getItem4()); target.setAnalysis(source.getExplains()); target.setTitlePic(source.getUrl()); // 映射答案格式 String mappedAnswer = mapAnswer(source.getAnswer()); target.setAnswer(mappedAnswer); // 判断题目类型:1-判断题,2-多选题 if ("1".equals(source.getAnswer()) || "2".equals(source.getAnswer())) { target.setTitleType(1); // 判断题 } else { target.setTitleType(2); // 多选题 } resultList.add(target); } return resultList; } /** * 映射答案格式 */ private String mapAnswer(String answer) { switch (answer) { case "1": return "A"; case "2": return "B"; case "3": return "C"; case "4": return "D"; case "7": return "AB"; case "8": return "AC"; case "9": return "AD"; case "10": return "BC"; case "11": return "BD"; case "12": return "CD"; case "13": return "ABC"; case "14": return "ABD"; case "15": return "ACD"; case "16": return "BCD"; case "17": return "ABCD"; default: return answer; } } /** * 加载默认题目数据(网络失败时使用) */ private void loadDefaultQuestions() { list = new ArrayList<>(); // 添加一些默认题目 Question.DataBean defaultQuestion = new Question.DataBean(); defaultQuestion.setTitle("这是默认题目,请检查网络连接"); defaultQuestion.setOp1("选项A"); defaultQuestion.setOp2("选项B"); defaultQuestion.setOp3(""); defaultQuestion.setOp4(""); defaultQuestion.setAnswer("A"); defaultQuestion.setTitleType(1); defaultQuestion.setAnalysis("这是默认题目的解析"); list.add(defaultQuestion); adapter1.updateQuestions(list); updateQuestionCount(); // 初始化答题状态 answeredArray = new boolean[list.size()]; } /** * 切换到开始答题模式 */ private void switchToStartMode() { mBinding.tvExamStart.setTextColor(ContextCompat.getColor(mContext, R.color.white)); mBinding.tvExamStart.setSelected(true); mBinding.tvExamAnswer.setTextColor(ContextCompat.getColor(mContext, R.color.tab_select_u)); mBinding.tvExamAnswer.setSelected(false); adapter1.setAnswerMode(false); } /** * 切换到答案模式 */ private void switchToAnswerMode() { mBinding.tvExamAnswer.setTextColor(ContextCompat.getColor(mContext, R.color.white)); mBinding.tvExamAnswer.setSelected(true); mBinding.tvExamStart.setTextColor(ContextCompat.getColor(mContext, R.color.tab_select_u)); mBinding.tvExamStart.setSelected(false); adapter1.setAnswerMode(true); } /** * 提交考试 confirmSubmit(); */ /** * 提交考试 */ private void submitExam() { final int total = list.size(); final int answered = correct + wrong; int firstUnansweredIndex = -1; // 找出第一个未答的题目索引 for (int i = 0; i < list.size(); i++) { if (list.get(i).getShowAnswer().isEmpty()) { firstUnansweredIndex = i; break; } } if (firstUnansweredIndex != -1) { final int index = firstUnansweredIndex; // 明确标记为 effectively final // 有未答的题目 String message = "还有 " + (total - answered) + " 道题目未完成!\n是否跳转到第 " + (index + 1) + " 题?"; DialogUtils.showConfirmDialog(this, "提示", message, () -> jumpToUnansweredQuestion(index), // 确认:跳转 () -> confirmSubmit() // 取消:继续提交 ); } else { // 全部已答,直接提交 confirmSubmit(); } } private void confirmSubmit() { // 清空之前的数据 unansweredList.clear(); wrongList.clear(); correctList.clear(); int total = list.size(); int answered = 0; // 遍历所有题目,分类统计 for (int i = 0; i < list.size(); i++) { Question.DataBean question = list.get(i); String userAnswer = question.getShowAnswer(); // 用户提交的答案 if (userAnswer.isEmpty()) { unansweredList.add(i); // 记录未答题号(index) } else { answered++; if (userAnswer.equals(question.getAnswer())) { correctList.add(i); } else { wrongList.add(i); } } } int correctCount = correctList.size(); int wrongCount = wrongList.size(); int unAnsweredCount = unansweredList.size(); double accuracy = total > 0 ? (double) correctCount / total * 100 : 0; boolean isPass = correctCount >= 90; // 科目一/四 90分及格 String result = isPass ? "恭喜通过!" : "未通过,请继续努力"; String message = String.format( "%s\n\n" + "【最终成绩】\n" + "总题数:%d\n" + "已答:%d|未答:%d\n" + "答对:%d|答错:%d\n\n" + "得分:%d / %d\n" + "正确率:%.1f%%", result, total, answered, unAnsweredCount, correctCount, wrongCount, correctCount, total, accuracy ); DialogUtils.showConfirmDialog(this, "考试结果", message, () -> { submitTime = new Date(); // 在这里你可以保存这些数据(关键步骤) saveExamResults(correctList, wrongList, unansweredList); // 跳转到答题详情页(前面实现的 AnswerDetailActivity) Intent intent = new Intent(this, AnswerDetailActivity.class); intent.putExtra("questionList", new Gson().toJson(list)); intent.putExtra("correctCount", correctCount); intent.putExtra("wrongCount", wrongCount); intent.putExtra("totalCount", total); startActivity(intent); finish(); }); } /** * 跳转到指定未答题 */ private void jumpToUnansweredQuestion(int position) { // 设置当前题目为未答题 mBinding.viewPager.setCurrentItem(position); // 同步更新题号高亮 adapter.setCurrentQuestion(position); // 可选:让题号栏滚动到可视区域 scrollToQuestionNumber(position); } /** * 滚动到指定题号(使其可见) */ private void scrollToQuestionNumber(int globalPosition) { int targetPage = (globalPosition / PAGE_SIZE) + 1; if (targetPage != currentPageIndex) { // 如果不在当前页,先加载对应页 currentPageIndex = targetPage; loadCurrentPage(); } // 延迟一点再设置高亮和滚动 mBinding.rvQuestionNumbers.post(() -> { adapter.setCurrentQuestion(globalPosition); // 计算在当前页中的偏移 int posInPage = globalPosition % PAGE_SIZE; if (posInPage < adapter.getItemCount()) { mBinding.rvQuestionNumbers.scrollToPosition(posInPage); } }); } /** * 显示退出确认对话框 */ private void showExitConfirmDialog() { DialogUtils.showConfirmDialog(this, "提示", "确定要退出考试吗?退出后进度将不会保存。", () -> { // 确认退出 if (countDownTimer != null) { countDownTimer.cancel(); } finish(); } ); } /** * 保存考试记录 */ private void saveExamRecord() { // 这里实现考试记录的保存逻辑 // 可以保存到本地数据库或上传到服务器 LogUtil.info("考试记录保存: 正确" + correct + "题,错误" + wrong + "题,用时" + getExamDuration() + "分钟"); } /** * 获取考试用时(分钟) */ private long getExamDuration() { if (examTime != null && submitTime != null) { return (submitTime.getTime() - examTime.getTime()) / (1000 * 60); } return 0; } /** * 开始计时器 */ private void startTimer() { if (!timerRunning) { countDownTimer = new CountDownTimer(timeLeftInMillis, 1000) { @Override public void onTick(long millisUntilFinished) { timeLeftInMillis = millisUntilFinished; updateCountdownText(); } @Override public void onFinish() { timerRunning = false; mBinding.tvExamTime.setText("00:00"); // 时间到自动交卷 autoSubmitExam(); } }.start(); timerRunning = true; } } /** * 自动交卷(时间到) */ private void autoSubmitExam() { DialogUtils.showConfirmDialog(this, "时间到", "考试时间已结束,系统将自动交卷。", new DialogUtils.OnConfirmListener() { @Override public void onConfirm() { submitTime = new Date(); saveExamRecord(); finish(); } }); } /** * 更新倒计时显示 */ private void updateCountdownText() { int minutes = (int) (timeLeftInMillis / 1000) / 60; int seconds = (int) (timeLeftInMillis / 1000) % 60; String timeFormatted = String.format("%02d:%02d", minutes, seconds); mBinding.tvExamTime.setText(timeFormatted); } //添加一个方法用于保存结果 private void saveExamResults(List<Integer> correct, List<Integer> wrong, List<Integer> unanswered) { SharedPreferences sp = getSharedPreferences("exam_history", Context.MODE_PRIVATE); SharedPreferences.Editor editor = sp.edit(); Gson gson = new Gson(); // 使用 JSON 保存列表 editor.putString("last_correct_list", gson.toJson(correct)); editor.putString("last_wrong_list", gson.toJson(wrong)); editor.putString("last_unanswered_list", gson.toJson(unanswered)); editor.putLong("last_submit_time", System.currentTimeMillis()); editor.apply(); Log.d("SaveResults", "已保存答题结果: 正确=" + correct.size() + ", 错误=" + wrong.size() + ", 未答=" + unanswered.size()); } @Override protected void onDestroy() { super.onDestroy(); if (countDownTimer != null) { countDownTimer.cancel(); } } }我是做了几个题直接点击提交,到AnswerDetailActivity页面,然后就报错 java.lang.NullPointerException: Attempt to read from field 'androidx.viewpager2.widget.ViewPager2 com.weishitech.jtwzjiaotongcx.databinding.ActivityTestBinding.viewPager' on a null object reference in method 'void com.weishitech.jtwzjiaotongcx.fragment.lilv.TestActivity$5.lambda$onAnswerConfirmed$0$com-weishitech-jtwzjiaotongcx-fragment-lilv-TestActivity$5(int)'
最新发布
12-31
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值