Android开发:如何让CountDownTimer在屏幕旋转后持续运行?
解决煮蛋计时器屏幕旋转重置问题
嘿,我看了你的煮蛋计时器代码,屏幕旋转后计时器重置的问题主要是没把所有关键状态保存下来,onSaveInstanceState和onRestoreInstanceState的逻辑也有偏差,还有几个小bug影响了恢复效果。咱们一步步修正:
首先,明确要保存的所有关键状态
屏幕旋转时Activity会销毁重建,所有影响计时器运行的状态都得存到Bundle里:
- 计时器剩余时间(用毫秒数更准确)
- 计时器是否处于运行状态
- 已选择的鸡蛋尺寸
- 这些状态缺一不可,否则旋转后就会丢失关键信息导致重置
修正后的完整代码(带详细注释)
package com.dohman.boilaneggbae; import android.os.CountDownTimer; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.TextView; import java.util.Locale; import java.util.concurrent.TimeUnit; public class MainActivity extends AppCompatActivity { // 定义状态保存的Key,命名更清晰 private static final String KEY_REMAINING_MILLIS = "remaining_millis"; private static final String KEY_IS_RUNNING = "is_running"; private static final String KEY_EGG_SIZE = "egg_size"; private TextView time; private Button buttonLargeSize; private Button buttonMediumSize; private Button buttonSoft; private Button buttonMedium; private Button buttonHard; private Button buttonHellaHard; private CountDownTimer countDownTimer; private EggSize mediumOrLarge = EggSize.UNDEFINED; private boolean alreadyRunning = false; // 用剩余毫秒数保存时间,避免秒转毫秒的误差 private long remainingMillis = 0; enum EggSize { UNDEFINED, MEDIUM, LARGE; } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 初始化所有控件(之前你漏了这步,会导致空指针) time = findViewById(R.id.time); buttonMediumSize = findViewById(R.id.buttonMediumSize); buttonLargeSize = findViewById(R.id.buttonLargeSize); buttonSoft = findViewById(R.id.buttonSoft); // 绑定点击事件 buttonMediumSize.setOnClickListener(btnMediumSizeClickListener); buttonLargeSize.setOnClickListener(btnLargeSizeClickListener); buttonSoft.setOnClickListener(btnSoftClickListener); // 从保存的状态中恢复数据 if (savedInstanceState != null) { remainingMillis = savedInstanceState.getLong(KEY_REMAINING_MILLIS); alreadyRunning = savedInstanceState.getBoolean(KEY_IS_RUNNING); mediumOrLarge = EggSize.valueOf(savedInstanceState.getString(KEY_EGG_SIZE)); // 如果之前计时器在运行,就用剩余时间重启 if (alreadyRunning && remainingMillis > 0) { startTimerWithRemainingTime(remainingMillis); } else if (remainingMillis > 0) { // 没在运行但有剩余时间,显示当前剩余时长 updateTimeDisplay(remainingMillis); } } } @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); // 保存所有关键状态 outState.putLong(KEY_REMAINING_MILLIS, remainingMillis); outState.putBoolean(KEY_IS_RUNNING, alreadyRunning); outState.putString(KEY_EGG_SIZE, mediumOrLarge.name()); } // 鸡蛋尺寸选择逻辑 private View.OnClickListener btnMediumSizeClickListener = new View.OnClickListener() { @Override public void onClick(View view) { mediumOrLarge = EggSize.MEDIUM; } }; private View.OnClickListener btnLargeSizeClickListener = new View.OnClickListener() { @Override public void onClick(View view) { mediumOrLarge = EggSize.LARGE; } }; // 软煮蛋按钮逻辑 private View.OnClickListener btnSoftClickListener = new View.OnClickListener() { @Override public void onClick(View view) { if (mediumOrLarge == EggSize.UNDEFINED) { time.setText("Choose size first"); return; } if (!alreadyRunning) { // 计算基础时长:大蛋240秒,中蛋减60秒 int baseDuration = 240; if (mediumOrLarge == EggSize.MEDIUM) { baseDuration -= 60; } remainingMillis = baseDuration * 1000L; startTimerWithRemainingTime(remainingMillis); alreadyRunning = true; } else { // 停止计时器 cancelTimer(); alreadyRunning = false; } } }; // 用剩余时间启动计时器的封装方法 private void startTimerWithRemainingTime(long millis) { // 先取消之前的计时器,避免重复实例化 cancelTimer(); countDownTimer = new CountDownTimer(millis, 1000) { @Override public void onTick(long millisUntilFinished) { remainingMillis = millisUntilFinished; // 实时更新剩余时间 updateTimeDisplay(millisUntilFinished); } @Override public void onFinish() { time.setText("Done!"); alreadyRunning = false; remainingMillis = 0; } }; countDownTimer.start(); // 正确的启动位置,之前你放错了地方 } // 取消计时器的封装方法 private void cancelTimer() { if (countDownTimer != null) { countDownTimer.cancel(); countDownTimer = null; } } // 更新时间显示的工具方法,避免重复代码 private void updateTimeDisplay(long millis) { String text = String.format(Locale.getDefault(), "%02d:%02d", TimeUnit.MILLISECONDS.toMinutes(millis) % 60, TimeUnit.MILLISECONDS.toSeconds(millis) % 60); time.setText(text); } }
关键改动说明
- 补充状态保存:新增了
KEY_IS_RUNNING和KEY_EGG_SIZE,确保旋转后计时器运行状态和鸡蛋选择不丢失 - 时间存储优化:用
remainingMillis(剩余毫秒数)代替秒数,避免转换误差,更精准 - 修复启动逻辑:把
countDownTimer.start()移到了CountDownTimer实例化之后,之前的位置是错误的 - 完善恢复流程:在
onCreate中根据保存的状态,自动恢复计时器运行或显示剩余时间 - 代码封装:把计时器启动、取消、时间显示抽成独立方法,代码更清晰易维护
这样修改后,屏幕旋转时计时器的所有状态都会被正确保存和恢复,不会再出现重置的问题啦!
内容的提问来源于stack exchange,提问作者Dohman
相关产品推荐
相关产品推荐

