对于循环停止我的swing应用程序,直到循环完成

时间:2013-10-31 17:14:40

标签: java swing jlabel imageicon

*问题解决了 - 感谢您的所有答案,他们非常有帮助!

我正在为一项学校作业制作一个小骰子游戏,我遇到了这个问题。 我想模拟一个模具的滚动,快速循环通过一些模具图标。 这本身并不是造成问题的原因。 如果我直接在JFrame中制作“动画”,它会正确显示。我在下面的代码中已经这样做了:

public class Example{

        private static ImageIcon die1 = new ImageIcon("terning1.jpg");
        private static ImageIcon die2 = new ImageIcon("terning2.jpg");
        private static ImageIcon die3 = new ImageIcon("terning3.jpg");
        private static ImageIcon die4 = new ImageIcon("terning4.jpg");
        private static ImageIcon die5 = new ImageIcon("terning5.jpg");
        private static ImageIcon die6 = new ImageIcon("terning6.jpg");

        private static JLabel die = new JLabel(die1);
        private static Random generator = new Random();

    public static void main(String[] args) {
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        frame.add(die);

        frame.pack();
        frame.setVisible(true);

        for (int i = 0; i < 100; i++) {
            int x = generator.nextInt(6) + 1;

            switch(x){
                case 1 : die.setIcon(die1);
                    break;
                case 2 : die.setIcon(die2);
                    break;
                case 3 : die.setIcon(die3);
                    break;
                case 4 : die.setIcon(die4);
                    break;
                case 5 : die.setIcon(die5);
                    break;
                case 6 : die.setIcon(die6);
                    break;
            }

            //Make the loop wait for 50 millis
            long a, b;
            a = System.currentTimeMillis();
            do {
                b = System.currentTimeMillis();
            } while ((b-a) < 50);           
        }

    }       
}

现在工作正常,但显然它只在我第一次打开JFrame时才有效。所以我想添加一个按钮,使模具滚动。 但是如果我添加一个带有actionlistener的JButton,并将for循环放在actionPerformed方法中,它会停止程序直到循环结束,并且只显示循环中的最后一个die。 例如:

public static void main(String[] args) {
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        frame.setLayout(new FlowLayout());

        frame.add(button);
        frame.add(die);

        button.addActionListener(new ButtonListener());

        frame.pack();
        frame.setVisible(true);
    }

    private static class ButtonListener implements ActionListener {

        public void actionPerformed(ActionEvent event) {
            for (int i = 0; i < 100; i++) {
                int x = generator.nextInt(6) + 1;

                switch (x) {
                    case 1:
                        die.setIcon(die1);
                        break;
                    case 2:
                        die.setIcon(die2);
                        break;
                    case 3:
                        die.setIcon(die3);
                        break;
                    case 4:
                        die.setIcon(die4);
                        break;
                    case 5:
                        die.setIcon(die5);
                        break;
                    case 6:
                        die.setIcon(die6);
                        break;
                }

                //Make the loop wait for 50 millis
                long a, b;
                a = System.currentTimeMillis();
                do {
                    b = System.currentTimeMillis();
                } while ((b - a) < 50);
            }

有关如何解决此问题的任何提示? 先谢谢!

4 个答案:

答案 0 :(得分:3)

发生的事情是所有Swing事件发生的“事件调度线程”必须等待您的代码。不要在事件调度线程上执行长时间运行的东西。这是一个着名的反模式。

您应该阅读从http://docs.oracle.com/javase/tutorial/uiswing/concurrency/index.html开始的Java教程中的课程,该教程描述了这一点。

与您的问题无关的两个小问题:

  1. 如果您使用ImageIcon变量数组而不是六个单独的变量,那么您的代码将更易于管理。
  2. 您可以使用sleep课程的Thread方法,而不是您正在使用的“忙碌睡眠”。

答案 1 :(得分:1)

您的UI被锁定,因为所有内容都在同一个线程中,所以它必须等到for循环执行完毕。您需要在一个单独的线程中运行您的逻辑。

答案 2 :(得分:0)

您在ED​​T中运行actionPerformed(ActionEvent event)方法,因为您无法更新UI。要从代码更新UI,请尝试使用SwingWorker,它可以在后台进程运行时更新UI。你可以在互联网上找到很多这样的例子。

或者您可以尝试使用Executors进行后台处理并从EDT更新用户界面。

答案 3 :(得分:0)

Swing的事件处理代码在EDT(甚至是调度线程)中运行。 ActionEvent也不例外。你不能在EDT里面做任何长时间运行的任务。把你的骰子滚动循环放在一个新线程中:

new Thread(){
   public void run(){
         // your dice rolling code
    }
  }.start(); 

然后使用SwingUtilities.invokdeLater()更新GUI。

注意:您可以使用ImageIcon数组并使用随机生成的索引访问它,而不是使用switch-case进行调节。

int x = generator.nextInt(6) + 1;
die.setIcon(imageIconArr[x]); // array of image icon

See this answer for details.