Reputation: 17
*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!
Upvotes: 1
Views: 180
Reputation: 15418
Swing's event handling code runs in EDT(even dispatch thread). The ActionEvent
is also no exception. You can't do any long running task inside EDT. Put your dice rolling loop inside a new thread:
new Thread(){
public void run(){
// your dice rolling code
}
}.start();
and then update the GUI using SwingUtilities.invokdeLater()
.
Note: Instead of conditioning with switch-case you just could use an array of ImageIcon
and access it with random generated index.
int x = generator.nextInt(6) + 1;
die.setIcon(imageIconArr[x]); // array of image icon
Upvotes: 0
Reputation: 10994
You run your actionPerformed(ActionEvent event)
method in EDT because of that you can't update UI. For updating UI from your code try to use SwingWorker, it can update UI while background process running. You can found a lot of examples of it in the internet.
Or you can try to use Executors for background process and updating UI from EDT.
Upvotes: 0
Reputation: 79877
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:
ImageIcon
variables, instead of six separate variables. sleep
method of the Thread
class instead of the "busy sleep" that you're using.Upvotes: 3
Reputation: 1266
Your UI is locked because everything is in the same thread so it has to wait until the for loop is done executing.Your going to need to run your logic in a separate thread.
Upvotes: 1