制作一个不会中断整个程序的倒数计时器?

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

所以我正在尝试制作一个反应游戏,你按下开始按钮,一个隐藏的计时器会从 1 到 10 秒之间的随机数倒计时到零。大多数关于 java 计时器的答案建议使用

Thread.sleep(1000);

然而,这会打断整个程序,而我只是想让它倒计时。我应该如何解决这个问题?

按下开始后,程序从一个随机数开始倒计时。蓝色图标(下面的完整代码)会变成红色,然后你应该按下它,它会显示你按下它所花费的时间。

代码是焦点:

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

}

使用的图像文件:

http://imgur.com/DjI8Udr

http://imgur.com/XKQW6DI

完整代码:

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

}

我建议使用不同的架构。为什么你认为你需要 "count down" 任何东西?!

意思是:你只关心你想在随机的一段时间后"something"做什么;所以这样的事情(使用伪代码)最终可能会更容易:

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

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

并且只需要一些代码来响应传入的 X 事件。

你不能只使用Thread.sleep(),它不会像你想的那样起作用。相反,您可以使用 javax.swing.Timer 来完成您想要做的事情。

文档中的一些注释(如果您懒得阅读的话):

  • 计时器使用单个共享线程执行它们的等待。
  • 计时器可以安全地对Swing 组件执行操作。
  • 计时器可以安全地对Swing 组件执行操作。
  • javax.swing.Timer 有两个功能可以使其更易于使用 GUI。

我已经修改了 中的一个示例,以展示如何根据您的需要调整它。它使用您随机生成的数字,该数字是在每次计时器结束并您按 "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);
        }

    }

}