简体   繁体   中英

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

So I'm making this game which gives the player 30 seconds to choose an answer. If they choose an option, the timer resets back to the 30 seconds.

@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(); 

        

        }

This is my runner() method:

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

My problem is, the timer does run when the button is clicked however it creates a new "timer" so on my GUI it just flickers between the two different times. For example if the already existing timer is at 5 seconds (left) and I click the button, the JLabel text flickers between 5/30, 4/29, 3/28, 2/27.. ect but in reality I just want it to stop the existing timer.

Not sure what I can do and I could really appreciate any help since i'm very new to swing and gui stuff.

Thanks!

Start by separating your concerns.

You have a mechanism which needs to keep track of when the button was trigged and the amount of time it should be counting down from AND you have some mechanism which updates the UI.

The first part is pretty simple and you can simply take advantage of the java.time API, for example...

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

So, based on a "anchor" time, you can calculate the "run time" from which you can then calculate the "remaining time", simple. Now, to reset it, you just call stop and start again.

Next, we need some way to update the UI. Swing is single threaded AND is not thread safe, this means you can't run long running or blocking operations from within the context of the Event Dispatching Thread and you shouldn't update the UI, or any state the UI relies on, from out side of the Event Dispatching Thread.

The best option is to make use ofSwing Timer , for example...

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

Runnable example...

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM