简体   繁体   中英

Making a countdown timer that doesn't interrupt the entire program?

So I'm trying to make a reaction game where you press start button, a hidden timer that will countdown to zero from a random number between 1 & 10 seconds. Most answer regarding java timers recommend using

Thread.sleep(1000);

However this interrupts the entire program while I just wants it to countdown. How should I solve this?

After pressing start and the program has counted down from a random number. The blue icon (entire code below) will turn red and then you're supposed to press it and it will display the time it took for you to press it.

Code is focus:

 public void countDown() throws InterruptedException {

    int random = r.nextInt((10000 - 1000) + 1) + 1000;

    while(random >= 0){
        Thread.sleep(1000);
        random -= 1000;
    }

    if (random <= 0) {
        button_1.setIcon(img_react);
        repaint();
    }

}

Image files used:

http://imgur.com/DjI8Udr

http://imgur.com/XKQW6DI

Entire code:

 import java.awt.*;
 import java.awt.event.*;
 import javax.swing.*;
 import javax.swing.Timer;
 import java.util.*;

public class Interface extends JFrame implements ActionListener {

private static final long serialVersionUID = 1L;

ImageIcon img_idle = new ImageIcon("img_idle.png");
ImageIcon img_react = new ImageIcon("img_react.png");

JButton button_1 = new JButton(img_idle);
JButton start = new JButton("Start");

Random r = new Random();

public Interface() {
    super("Simple Reaction Game");
    setSize(180, 350);
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    setLocationRelativeTo(null);
    setVisible(true);

    Container contentArea = getContentPane();
    contentArea.setBackground(Color.white);

    FlowLayout flowManager = new FlowLayout();
    contentArea.setLayout(flowManager);

    button_1.addActionListener(this);
    start.addActionListener(this);

    contentArea.add(button_1);
    contentArea.add(start);

    setContentPane(contentArea);
}

public void actionPerformed(ActionEvent e) {
    if (e.getSource() == start) {
        try {
            countDown();
        } catch (InterruptedException e1) {
            e1.printStackTrace();
        }
    }
}

public void countDown() throws InterruptedException {
    Thread t = new Thread();
    int random = r.nextInt((10000 - 1000) + 1) + 1000;

    while(random >= 0){
        t.sleep(1000);
        random -= 1000;
    }

    if (random <= 0) {
        button_1.setIcon(img_react);
        repaint();
    }

}

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

}

You can't just use Thread.sleep() , it won't work as you think it does. Instead, you can use javax.swing.Timer which is made to do what you're trying to do.

Some notes from the docs (if you didn't bother reading it):

  • Timers perform their waiting using a single, shared thread.
  • Timers can safely perform operations on Swing components.
  • Timers can safely perform operations on Swing components.
  • The javax.swing.Timer has two features that can make it a little easier to use with GUIs.

I've modified an example from here to show how you can adapt it to your needs. It's using your random generated number which is generated each time the timer is finished and you press "start".

import java.awt.*;
import java.awt.event.*;
import java.text.SimpleDateFormat;
import javax.swing.*;
import javax.swing.UnsupportedLookAndFeelException;
import java.util.Random;

public class Interface {

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

    public Interface() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }

        });
    }

    public class TestPane extends JPanel {

        private Timer timer;
        private long startTime = -1;
        private long duration;

        private JLabel label;
        private JButton start;

        public TestPane() {
            start = new JButton("Start");
            setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
            timer = new Timer(10, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    if (startTime < 0) {
                        startTime = System.currentTimeMillis();
                    }

                    long now = System.currentTimeMillis();
                    long clockTime = now - startTime;
                    if (clockTime >= duration) {
                        clockTime = duration;
                        timer.stop();
                    }
                    SimpleDateFormat df = new SimpleDateFormat("mm:ss:SSS");
                    label.setText(df.format(duration - clockTime));
                }
            });
            timer.setInitialDelay(0);
            start.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    if (!timer.isRunning()) {
                        duration = new Random().nextInt((10000 - 1000) + 1) + 1000;
                        startTime = -1;
                        timer.start();
                    }
                }
            });
            label = new JLabel("...");
            add(label);
            add(start);
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(200, 250);
        }

    }

}

I would suggest a different architecture. Why do you think you need to "count down" anything?!

Meaning: all you care about is that you want to do "something" after a random period of time; so something like this (using pseudo-code) might be easier in the end:

now = ... get current time
then = now + random value

schedule the Swing timer to sent some Event X "then"

And simply have some code that that reacts to incoming X events.

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