如何在按下同一个按钮时重置倒数计时器

How to reset a countdown timer when the same button is pressed

所以我正在制作这款游戏​​,让玩家有 30 秒的时间来选择答案。如果他们选择一个选项,计时器将重置为 30 秒。

@Override
public void actionPerformed(ActionEvent e) {

    //the idea is that when this button is clicked, the timer starts.
    if(e.getSource() == button) {

        //random gui stuff which i won't include



        //calls the method for timer
        runner(); 

        

        }

这是我的 运行ner() 方法:

public void runner() {

    final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);

    final Runnable runnable = new Runnable() {
        
        int countdownStarter = 30;

        public void run() {

            

            //this is my JLabel that prints out the timer
            label.setText("" + countdownStarter);
            
            countdownStarter--;

            if (countdownStarter < 0) {


                label.setText("");

                /*Random GUI stuff here I won't show*/ 

                scheduler.shutdown();
            }
        }

我的问题是,当单击按钮时计时器会 运行 但是它会创建一个新的“计时器”,所以在我的 GUI 上它只是在两个不同的时间之间闪烁。例如,如果已经存在的计时器为 5 秒(左)并且我单击按钮, JLabel 文本在 5/30、4/29、3/28、2/27 之间闪烁,但实际上我只是想让它停止现有的计时器。

不确定我能做什么,我真的很感激任何帮助,因为我对 swing 和 gui 东西还很陌生。

谢谢!

首先分离您的顾虑。

你有一个机制需要跟踪按钮何时被触发以及它应该倒计时的时间量并且你有一些机制可以更新 UI。

第一部分非常简单,您可以简单地利用 java.time API,例如...

public class CountdownTimer {
    private Instant startedAt;
    private Duration duration;

    public CountdownTimer(Duration duration) {
        this.duration = duration.plusSeconds(1);
    }

    public boolean isRunning() {
        return startedAt != null;
    }

    public void start() {
        if (startedAt != null) {
            return;
        }
        startedAt = Instant.now();
    }

    public void stop() {
        startedAt = null;
    }
    
    public Duration getTimeRemaining() {
        return duration.minus(getRunTime());
    }

    public Duration getRunTime() {
        if (startedAt == null) {
            return null;
        }
        return Duration.between(startedAt, Instant.now());
    }
}

所以,基于一个“锚定”时间,你可以计算出“运行时间”,然后你可以从中计算出“剩余时间”,很简单。现在,要重置它,您只需再次调用 stopstart

接下来,我们需要一些方法来更新 UI。 Swing 是单线程的并且不是线程安全的,这意味着你不能 运行 长 运行ning 或阻塞事件调度线程上下文中的操作,你不应该更新 UI,或 UI 依赖的任何状态,来自事件调度线程的外部。

最好的选择是利用Swing Timer,例如...

public class CountdownPane extends JPanel {

    private CountdownTimer countdownTimer;
    private Timer timer;

    public CountdownPane() {
        countdownTimer = new CountdownTimer(Duration.ofSeconds(5));
        JButton btn = new JButton("Destory the world");
        timer = new Timer(500, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                Duration duration = countdownTimer.getTimeRemaining();
                long seconds = duration.toSecondsPart();
                if (seconds <= 0) {
                    countdownTimer.stop();
                    timer.stop();
                    btn.setText("Boom");
                } else {
                    btn.setText(format(duration));
                }
            }
        });
        timer.setInitialDelay(0);
        setLayout(new GridBagLayout());
        setBorder(new EmptyBorder(36, 36, 36, 36));
        btn.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                if (countdownTimer.isRunning()) {
                    countdownTimer.stop();
                    timer.stop();
                    btn.setText("Destory the world");
                } else {
                    countdownTimer.start();
                    timer.start();
                }
            }
        });
        add(btn);
    }

    protected String format(Duration duration) {
        long seconds = duration.toSecondsPart();
        return String.format("%02d seconds", seconds);
    }
}

可运行示例...

import java.awt.EventQueue;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.time.Duration;
import java.time.Instant;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.border.EmptyBorder;

public class Main {
    public static void main(String[] args) {
        new Main();
    }

    public Main() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame();
                frame.add(new CountdownPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class CountdownPane extends JPanel {

        private CountdownTimer countdownTimer;
        private Timer timer;

        public CountdownPane() {
            countdownTimer = new CountdownTimer(Duration.ofSeconds(5));
            JButton btn = new JButton("Destory the world");
            timer = new Timer(500, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    Duration duration = countdownTimer.getTimeRemaining();
                    long seconds = duration.toSecondsPart();
                    if (seconds <= 0) {
                        countdownTimer.stop();
                        timer.stop();
                        btn.setText("Boom");
                    } else {
                        btn.setText(format(duration));
                    }
                }
            });
            timer.setInitialDelay(0);
            setLayout(new GridBagLayout());
            setBorder(new EmptyBorder(36, 36, 36, 36));
            btn.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    if (countdownTimer.isRunning()) {
                        countdownTimer.stop();
                        timer.stop();
                        btn.setText("Destory the world");
                    } else {
                        countdownTimer.start();
                        timer.start();
                    }
                }
            });
            add(btn);
        }

        protected String format(Duration duration) {
            long seconds = duration.toSecondsPart();
            return String.format("%02d seconds", seconds);
        }
    }

    public class CountdownTimer {
        private Instant startedAt;
        private Duration duration;

        public CountdownTimer(Duration duration) {
            this.duration = duration.plusSeconds(1);
        }

        public boolean isRunning() {
            return startedAt != null;
        }

        public void start() {
            if (startedAt != null) {
                return;
            }
            startedAt = Instant.now();
        }

        public void stop() {
            startedAt = null;
        }

        public Duration getTimeRemaining() {
            return duration.minus(getRunTime());
        }

        public Duration getRunTime() {
            if (startedAt == null) {
                return null;
            }
            return Duration.between(startedAt, Instant.now());
        }
    }
}