In my code, I initialize a JDialog:
dialog = new JDialog( frame, "Login", true );
dialog.setContentPane( panel );
dialog.setDefaultCloseOperation( JDialog.HIDE_ON_CLOSE );
dialog.setBounds( new Rectangle( 50, 50, 500, 500 ) );
When a button in my main application is clicked, I show the dialog and then run an expensive method with the data I get from it:
dialogWrapper.show(); // Runs dialog.setVisible( true ) directly
LoginCredentials credentials = dialogWrapper.getCredentials(); // Gets data from dialog
try {
SwingUtilities.invokeLater( new Runnable() {
@Override
public void run() {
progressBar.setIndeterminate( true );
mainWindow.getFrame().repaint();
accountModel.login( credentials );
System.out.println( "Successful login." );
mainWindow.getFrame().revalidate();
mainWindow.getFrame().repaint();
progressBar.setIndeterminate( false );
}
} );
} catch ( Exception ex ) {
// ...
}
My problem is that as soon as I click the button that runs dialog.setVisible( false )
:
accountModel.login()
appear on itHow can I make my main window components responsive as the login code is running?
As you can see, I have the entire thing wrapped in a SwingUtilities.invokeLater()
call, but that doesn't seem to be helping at all.
As you can see, I have the entire thing wrapped in a SwingUtilities.invokeLater() call,
That is the problem. The invokeLater() places code on the EDT which means the GUI can't repaint itself of respond to events until the long running task is finished executing.
So the solution is to use a separate Thread
for the long running task, then in the Thread
when you need to update the GUI you use the invokeLater()
.
Or, you can also use a SwingWorker
which will create the Thread for you and then you can add code to the done()
method of the SwingWorker to update the GUI.
Read the section from the Swing tutorial on Concurrency for more information about the Event Dispatch Thread (EDT)
and the SwingWorker
.