为Swing.Timer实现的秒表添加正常恢复功能的技术咨询
解决Swing秒表暂停后无法正确恢复计时的问题
你的问题根源很明确:当前代码里的start时间点是在Timer的ActionListener初始化时就固定死的——不管你暂停多久,再次启动Timer时,它依然会从那个最初的start时间开始计算总时长,自然就把暂停的时间也叠加进去了。
Swing Timer确实没有原生的“暂停”功能,但我们可以通过记录累计时长和动态更新起始时间点来模拟正确的暂停/恢复逻辑。下面是修改后的完整代码,我会在后面拆解关键改动:
import java.awt.Color; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.time.Duration; import java.time.Instant; import javax.swing.Timer; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; public class GuiStopwatch { // 跟踪累计时长和计时状态的变量 private static long elapsedSeconds = 0; private static long elapsedNanos = 0; private static Instant lastStartTime; public static void main(String[] args) { JFrame frame = new JFrame("Stopwatch"); frame.setSize(500, 500); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); JPanel panel = new JPanel(); panel.setLayout(null); JButton startbtn = new JButton("START"); JButton pausebtn = new JButton("PAUSE"); JButton reset = new JButton("RESET"); JLabel time = new JLabel("0:000000000"); panel.add(startbtn); panel.add(pausebtn); panel.add(reset); panel.add(time); startbtn.setBounds(50, 150, 100, 35); pausebtn.setBounds(50, 200, 100, 35); reset.setBounds(50, 250, 100, 35); time.setBounds(50, 350, 200, 35); time.setBackground(Color.black); time.setForeground(Color.red); frame.add(panel); Timer timer = new Timer(1, new ActionListener() { @Override public void actionPerformed(ActionEvent e) { // 计算当前实时流逝的时间 Duration sinceLastStart = Duration.between(lastStartTime, Instant.now()); long totalSeconds = elapsedSeconds + sinceLastStart.getSeconds(); long totalNanos = elapsedNanos + sinceLastStart.getNano(); // 处理纳秒进位到秒的情况 if (totalNanos >= 1_000_000_000) { totalSeconds += totalNanos / 1_000_000_000; totalNanos = totalNanos % 1_000_000_000; } // 更新显示文本 time.setText(totalSeconds + ":" + String.format("%09d", totalNanos)); } }); startbtn.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { if (!timer.isRunning()) { // 记录当前时间为新的起始点 lastStartTime = Instant.now(); timer.start(); } } }); pausebtn.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { if (timer.isRunning()) { // 计算从上次启动到现在的时长,累加到累计值里 Duration pausedDuration = Duration.between(lastStartTime, Instant.now()); elapsedSeconds += pausedDuration.getSeconds(); elapsedNanos += pausedDuration.getNano(); // 处理纳秒进位 if (elapsedNanos >= 1_000_000_000) { elapsedSeconds += elapsedNanos / 1_000_000_000; elapsedNanos = elapsedNanos % 1_000_000_000; } timer.stop(); } } }); reset.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { timer.stop(); // 重置所有计时变量 elapsedSeconds = 0; elapsedNanos = 0; time.setText("0:000000000"); } }); } }
关键改动说明
- 新增状态变量:
elapsedSeconds、elapsedNanos用来记录暂停前已经累计的时长,lastStartTime用来跟踪每次启动计时的时间点 - 开始按钮逻辑:只有当Timer未运行时才启动,同时更新
lastStartTime为当前时间,确保恢复计时时从暂停的节点开始计算 - 暂停按钮逻辑:暂停前先把本次启动到暂停的时长累加到累计变量里,这样下次启动时就会基于这个累计值继续计算
- Timer计时逻辑:每次触发时,计算当前时间与
lastStartTime的实时差值,加上累计时长得到总时间,同时处理了纳秒进位到秒的边界情况 - 显示优化:用
String.format("%09d", totalNanos)保证纳秒部分显示为9位,看起来更规范
这样修改后,你的秒表就能实现真正的暂停/恢复功能——暂停在10.0秒,5秒后恢复会从10.0秒继续计时,而不是跳到15.0秒啦。
内容的提问来源于stack exchange,提问作者Lord Jesus
相关产品推荐
相关产品推荐

