1

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

我正在为学校作业制作一个小型骰子游戏,我遇到了这个问题。我想通过快速循环通过多个骰子图标来模拟骰子的滚动。不过,这本身并不是导致问题的原因。如果我直接在 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 方法中,它会暂停程序直到循环完成,并且只显示循环中的最后一个死。例子:

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

4 回答 4

3

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

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

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

  1. ImageIcon如果您使用变量数组而不是六个单独的变量, 您的代码将更易于管理。
  2. 您可以使用类的sleep方法Thread而不是您正在使用的“忙睡眠”。
于 2013-10-31T17:17:27.043 回答
1

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

于 2013-10-31T17:16:40.827 回答
0

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

或者您可以尝试使用Executors进行后台进程并从 EDT 更新 UI。

于 2013-10-31T17:26:02.500 回答
0

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

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

然后使用SwingUtilities.invokdeLater().

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

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

有关详细信息,请参阅此答案。

于 2013-10-31T17:29:11.493 回答