Skip to main content

Swing / AWT

Invoking changes to your Swing UI

All Swing events happen on one thread (The EDT, or Event Dispatch Thread). Doing processing on this thread in your normal code logic will cause your UI to freeze up while your processing is done, since all Swing rendering is waiting for that to finish.

Use the javax.swing.SwingUtilities class to solve this problem, by using its InvokeLater method:

SwingUtilities.invokeLater(() -> {
  someButton.setText("I am a button");
});

This adds whatever code you put inside the anonymous function to the end of the swing queue, and then continues with your code as normal. Use this to update the UI without making it freeze.

Doing background work and showing progress indicators

For things where you want to perform some background operation, and also show the user the progress of that operation, make use of the javax.swing.SwingWorker class. A basic use looks like this: it doesn't show the progress, but doesn't take up the event dispatch thread running the long operation.

SwingWorker worker = new SwingWorker() {
	@Override
	protected Object doInBackground() {
        // the doInBackground() method does NOT run on the Event Dispatch Thread.
		// Do some operations.
		Object yourResult = doSomeOperation();
		return yourResult;
	}

	@Override
	public void done() {
        // the done() method DOES run on the Event Dispatch Thread.
		Object result;
		try {
			results = (Object) get();
		} catch (Exception e) {
			String msg = "Failed to perform operation.";
			JOptionPane.showMessageDialog(null, msg, "Error", JOptionPane.ERROR_MESSAGE);
			return;
		}
		// Perform any tasks you want to do after the operation finished.
		doSomeOtherOperation();
	}
};
worker.execute();

To show the operation progress as it's happening, the example from the Java docs is useful:

// Extend the SwingWorker class.
class PrimeNumbersTask extends SwingWorker<List<Integer>, Integer> {

  PrimeNumbersTask(JTextArea textArea, int numbersToFind) {
	 //initialize
  }

  @Override
  public List<Integer> doInBackground() {
	 while (! enough && ! isCancelled()) {
			 number = nextPrimeNumber();
             // Publish sends the result to the process() method.
			 publish(number);
             // Set progress invokes the PropertyChangeListener,
             // allowing the progress bar to be updated.
			 setProgress(100 * numbers.size() / numbersToFind);
		 }
	 }
	 return numbers;
  }

  @Override
  protected void process(List<Integer> chunks) {
	 for (int number : chunks) {
		 textArea.append(number + "\n");
	 }
  }
  
}

JTextArea textArea = new JTextArea();
final JProgressBar progressBar = new JProgressBar(0, 100);
PrimeNumbersTask task = new PrimeNumbersTask(textArea, N);
task.addPropertyChangeListener(
 new PropertyChangeListener() {
	 public  void propertyChange(PropertyChangeEvent evt) {
		 if ("progress".equals(evt.getPropertyName())) {
			 progressBar.setValue((Integer)evt.getNewValue());
		 }
	 }
 });

task.execute();
System.out.println(task.get()); //prints all prime numbers we have got