Wait for Swing to finish updating JProgressBar before continuing
I have a JFrame
with a CardLayout
set as its layout manager. This has two JPanel
subclasses in it. One is a panel, WordsLoadingPanel
, which displays the text "Loading words..." and has a JProgressBar
. The other has to actually load the words. This takes a while (about 10-14 seconds for 100 words; it's a pretty selective algorithm), so I want to assure the user that the program is still working. I made the loading algorithm in the panel fire a property change with firePropertyChange(String, int, int)
, and the WordsLoadingPanel
is catching the change just fine - I know this because I added a listener for this event to perform a println
, and it works. However, when I change the println开发者_如何学Go
to actually changing the JProgressBar
's value, it doesn't do anything. I know I'm changing the value right, because if I set the value before the algorithm starts, it works, and it works on the last iteration of the loop. I'm guessing this is because my algorithm is eating the computing power and won't let JProgressBar
update.
So, my question is: How do I make my algorithm wait for Swing (would this be the AWT Dispatching Thread?) to finish updating the progress bar before continuing? I've tried:
Thread.yield
in each iteration of the loopThread.sleep(1000L)
in each iteration of the loop, in a try/catch- putting everything in the loop in a
SwingUtilities.invokeLater(Runnable)
- putting only the CPU-intensive algorithm in a
SwingUtilities.invokeLater(Runnable)
EDIT: To further support my hypothesis of the CPU-eating algorithm (sounds like a children's story…), when I set the JProgressBar
to indeterminate, it only starts moving after the algorithm finishes.
Does anyone have any suggestions?
Thanks!
To do expensive operations in background, consider using the SwingWorker
class. The documentation has examples on how to use it to do tasks that interact with the user interface in a separate thread, including progress display in JProgressBar
s.
If you have trouble understanding how the class works, consider:
SwingWorker
is a generic class that takes two parameters:T
, andV
- The
doInBackground
method returnsT
and is executed in a separate thread. - Since Swing may only be manipulated in the Event Dispatch Thread, you may not manipulate Swing in
doInBackground
. - The
process
method takes aList<V>
as a parameter and is called asynchronously on the Event Dispatch Thread. - The
publish
method takesV...
arguments and sends them for processing in theprocess
method.
In conclusion:
T
is the type of the result of the computation, if any.V
is the type of the data needed to manipulate the user interface.- Your algorithm should run entirely in
doInBackground
. - The user interface should be manipulated in the
process
method. - Your algorithm should use
publish
to send data to theprocess
method.
OK, I've solved it. For anyone who may have a similar problem, my solution was to change the method which begun the algorithm from executing it synchonously to asynchronously (with new Thread(Runnable).start
). So, my code is now
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
new Thread(new Runnable () {
public void run () {
window.keyboardTrainingPanel.initialize();
}
}).start();
}
});
I hope this can help someone! However, if there is a better way to do this, feel free to notify me.
精彩评论