I have a program, that takes a long time to load. Because of this, I wanted to develop a splash screen that can provide feedback to the user on what is being loaded. A simple JFrame with an image, label and JProgressBar.
I have been experimenting and the best results I've had are doing this in my main()
:
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
new SplashScreen();
}
});
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
//Code to start system
new MainFrame();
//.. etc
}
});
Both SplashScreen and MainFrame are classes extending JFrame. I am also using Substance as a Library.
SplashScreen's constructor adds a JLabel and JProgressBar to itself, packs and sets visible.
The JProgressBar is setIndeterminate(true)
;
When I run my program, my SplashScreen is displayed but the ProgressBar is locked, it doesn't move, not until the rest of the program has started does it start moving as expected.
What am I missing here? All searching I've done doesn't seem to mention this problem and most "custom splash screen" implementations go about it a very similar way to myself.
Just to attempt to be a little more general than Allain's correct answer.
There is one thread in your system that can (legally) update GUIs.
If you are using that thread to initialize your system, guis will NOT update.
If you are trying to update your GUI from your main thread, nothing will go right.
Always be aware of which thread your GUI is on and be sure not to be doing work on that thread.
Think of GUI updates in this way:
Non-GUI threads are most often threads from Thread.start and the one passed to "main"
The GUI thread is the one executing your "invokeLater" and anything passed to you from a GUI event (button pressed, GUI timer, any Swing listeners.)
Because of this, most often you are already on the GUI thread when it's time to update the GUI--just watch out for startup sequencing and places where threads want to update the display.
invokeAndWait will freeze the Swing thread until the operation it is given completes. So, your progress bar will be 100% forzen until the MainFrame is created.
You should spawn a non-swing thread that updates the Splash Screen using SwingUtilities.invoke(Later|AndWait).
new Thread(new Runnable() { public void run() {
// Create MainFrame here
SwingUtilities.invokeLater(new Runnable() {
public void run() {
updateProgressHere();
}
});
// Do other initialization
SwingUtilities.invokeLater(new Runnable() {
public void run() {
updateProgressHere();
}
});
}).start();
Other answers have covered most of this but in brief your problem is that you are running the "Code to start system" in the Swing event dispatch thread. All GUI-related code (including component creation) must be run on the EDT but all other code should not be run on the EDT. Try changing your program to do this:
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
new SplashScreen();
}
});
// Code to start system (nothing that touches the GUI)
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
new MainFrame();
}
});
//.. etc
Others have mentiones why your progress bar is blocked, I will suggest an alternative solution Java6 has splash screen handling built in to the javaw and java launchers...
The splash screen is displayed even before the JVM and the application's classes are loaded, and then once started the application can update it to reflect its internal loading state, and close it when ready.
Alternatively, for windows-only solutions, the Winrun4J java launcher also has a splash screen feature.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With