Search code examples
javaswingjlabelimageicon

For loop stalls my swing application, until the loop completes


*Problem solved - thanks for all your answers, they were very helpful!

I am making a small dice game for a school assignment, and i came across this problem. I want to simulate the rolling of a die, by quickly cycle trough a number of die-icons. This by itself is not what is causing the problem though. If i make the "animation" directly in a JFrame it displays correctly. I have done that in the code below:

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

    }       
}

Now that works fine, but obviously it only works when i first open the JFrame. So i want to add a button, that makes the die roll. But if i add a JButton with a actionlistener, and put the for-loop in the actionPerformed method, it stalls the program until the loop has finished, and only shows the last die in the loop. Example:

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

Any tips on how to solve this? Thanks beforehand!


Solution

  • What's happening is that the "event dispatch thread", where all the Swing events happen, is having to wait for your code. Don't do long-running stuff on the event dispatch thread. This is a famous anti-pattern.

    You should read the lesson from the Java tutorials, that starts at http://docs.oracle.com/javase/tutorial/uiswing/concurrency/index.html, which describes this.

    Two small points that are unrelated to your problem:

    1. Your code will be much more manageable if you use an array of ImageIcon variables, instead of six separate variables.
    2. You could use the sleep method of the Thread class instead of the "busy sleep" that you're using.