I've been spending a great deal of time trying to understand this. I created a JApplet that used Thread.sleep() in a loop to animate the applet. But when I tried to run the Applet the screen stayed blank. When I draw the animations in a separate thread and call repaint, the applet works perfectly. I've seen many explanations of why this happens but none have been very comprehensive.
What thread calls the paint method? And why can't that thread partially draw on the canvas, pause, then continue drawing? I've noticed that some Thread.sleep() calls work as expected, particularly when they are not in a loop.
UI needs to be repainted and this happens on UI thread.
Therefore you should not seize control of the UI thread - it seems that you are doing just this (animation loop with .sleep()).
You should return control of the UI thread (exit the function that you are in) so that it can be repainted.
Take a look at Swing Painting and SwingWorker to understand how to run a background thread to do some work and then update UI on UI thread.
Related
I'm designing a game where you get to the edge of the screen the screen will shift to a new panel of the same map similar the first Zelda game. Which method would be more efficient and or effective, having a thread running using repaint() or would using javax.swing.Timer and calling super.paintComponet work better?
Usually for simple animations, you can use javax.swing.Timer But it looks like you are into a more complex game. If you continue to use swing.Timer and if your execution of the codes are not fast enough, it may affect the painting of your components causing your UI to be irresponsive.
You can construct a game loop and render according to the frame rate you wanted by invoking Thread.sleep(delay) via a worker thread remember do not run it in the Event Dispatch Thread, else your UI will also become irresponsive.
Inside the actionPerformed method of a jButton, I have the following code:
btnLogin.setText("Logging In...");
btnLogin.setPreferredSize(new Dimension(110, 29));
btnLogin.setEnabled(false);
//more stuff here, irrelevant to this
This works, however it only takes visual effect (is repainted) once the method is complete.
If in the //more stuff here area I have code that takes a long time to complete, the effects of the btnLogin changes do not take effect until that code is complete.
I have tried typing:
this.revalidate();
this.repaint();
Directly after the first 3 lines, and multiple other solutions, to try to force the damn thing to repaint DURING the method, but no matter what, it only happens at the end!
Another thing I've noticed is that if I call a JOptionPane in the middle of the method, the frame WILL repaint (in the background), so that's interesting.
What is is that's automatically happening in the end of the method that I need to call to make it happen during the method?
Thanks in advance!
You're blocking the Swing event thread with the long-running code, and this prevents Swing from drawing the text changes. The solution:
Do the long-running code in a background thread such as in a SwingWorker's doInBackground method.
But make sure to make most all Swing calls on the Swing event thread.
Read the Concurrency in Swing tutorial to learn the details on the Swing event thread and threading issues.
I am about to write an animation in which a character on a JPanel moves left/right when the user presses (and holds) the appropriate keys.
Now, since I am new to concurrency I want to ask : should the code handling the animation (namely the method that will move and repaint the character as long as the key is held) be executed on a worker thread or the event dispatch thread ?
I read the tutorials on concurrency in swing, and it seems that stuff that changes the components (in this case it's painting JPanel) should be on the event handling thread, but on the other hand that would freeze all the other events as long as a key would be held right ?
For example a user won't be able to pause the game while he's holding the go-left key, if I understand this correctly. So which approach is correct and why? Or maybe there is totally different way to make such an animation ?
Thank you in advance!
All the Drawings Paintings need to be done on EDT thread. Never on any other Thread . Use
SwingUtilities.invokeLater() .. for your painting. You can do calculations like what will be the next postion of your character on Worker Thread. You won't have any unresponsiveness if you are doing creating and using Swing Classes on EDT. just Make sure that your computation is not too long. For Animation whenever you find the next positon of your Character just call
SwingUtilties.invokeLater to update changes on UI
I'm using the java swing library to develop a board game called DAO.
The problem is that after the human player makes its move, by clicking on the JButton with the piece image that he wants to play, I call the computer AI routine but inside the mouse event function. By doing this only when the function returns, the computer ends its turn, do the JButtons refresh their Images (setIcon comes in).
I'd like to know how can I force the JButtons to change their image at the moment they are clicked and not only when the mouse event function ends (as I need to handle data inside it).
I've tried all of this
myButtons[i][j].setIcon(xIcon);
myButtons[i][j].revalidate();
myButtons[i][j].repaint();
myButtons[i][j].validate();
None worked.
Thx in advance
You may want to try putting the action performed upon clicking the JButton into a Swing worker. This will allow the task to go on in the background, while the user can still click other buttons, etc.
See http://java.sun.com/docs/books/tutorial/uiswing/concurrency/simple.html.
There is a single thread used for all Swing activity.
Here's the process.
One event appears on the event queue
it is pulled from the queue and executed by The AWT Thread
Any new events created while this is executing are placed on the queue to be held until the currently running AWT event returns.
The event executing returns and the next event on the queue is dequeued and executed.
This means that if you need to do anything that takes more than, say 1/100 of a second or so, you shouldn't do it any thread started from a swing event. Instead, spawn your own thread and return the swing thread to the system so the GUI can be updated.
Now, your thread MUST NOT update any GUI objects! If you need to update a GUI object, use invokeLater to place your code back on the AWT thread.
New Java programmers not conforming to this rule and executing tasks on the AWT thread is almost certainly the biggest reason people think Java is slow.
I've built a form with Netbeans's visual editor. When I press one of the buttons it should do the following :
set it to disabled
perform a task that takes some time
when the task finishes the button will be enabled again
However, the following happens:
the button remains in a pressed state until the task finishes
when the task finishes, the enabling/disabling of buttons will be very fast (they will happen, but you won't notice them)
This behaviour is not something I want. I tried using repaint on the JButton, on the JFrame and even on the JPanel containing the button, but I can't seem to get it to do what I want. Some hints?
When you do work in a button callback, you are stalling the GUI painting thread until it completes.
What you need to do is spawn a thread to do the long running task, and then have that thread use SwingUtilities.invokeLater() to update the UI when it completes. Not using invokeLater is not thread safe, and is generally bad mojo.
A basic example is:
button.setEnabled(false);
new Thread(new Runnable() {
public void run() {
// Do heavy lifting here
SwingUtilies.invokeLater(new Runnable() {
public void run() {
button.setEnabled(true);
}
});
}
}).start();
When you do things in a button callback, you are essentially stalling the gui painting thread - not just for the button, but for ANY gui painting. (Try covering the interface with another window and then exposing it again - it won't repaint until the task is finished!)
What you need to do is spawn a thread to do the long running task, and then have that thread use SwingUtilities.invokeLater() to do the enabling of the button. invokeLater forces the button enable to happen in the gui painting thread.
You may want to set a busy cursor or otherwise lock the interface while the long-running thread is operating.
The Concurrency in Swing tutorial from Sun is well worth a read. Excellent explanation and background reading, including the event dispatching thread, using worker threads, etc
You need to do the task that takes some time in a different thread.
The reason the button is blocking is because the work is being done in the same thread that draws the button. Once the work is done the button can do the rest of what you tell it to.
If you use a different thread the thread will go do the task while the drawing code can continue drawing the form.