You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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);
    }
}

关键改动说明

  1. 补充状态保存:新增了KEY_IS_RUNNING和KEY_EGG_SIZE,确保旋转后计时器运行状态和鸡蛋选择不丢失
  2. 时间存储优化:用remainingMillis(剩余毫秒数)代替秒数,避免转换误差,更精准
  3. 修复启动逻辑:把countDownTimer.start()移到了CountDownTimer实例化之后,之前的位置是错误的
  4. 完善恢复流程:在onCreate中根据保存的状态,自动恢复计时器运行或显示剩余时间
  5. 代码封装:把计时器启动、取消、时间显示抽成独立方法,代码更清晰易维护

这样修改后,屏幕旋转时计时器的所有状态都会被正确保存和恢复,不会再出现重置的问题啦!

内容的提问来源于stack exchange,提问作者Dohman

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 04:40:42