Hectoret
Hectoret

Reputation: 3583

Swing, how to properly update the UI

What is the right way to update the UI after doing some operations on Swing?

For example, after clicking a button, a method is called that may be almost instant or take some seconds. In fact, all the applicaton logic is done remotely through a web service, so it's normal to wait a little bit for the application to respond.

My eventhandler for a button may look like these:

myButton.addActionListener(new java.awt.event.ActionListener() {
   public void actionPerformed(java.awt.event.ActionEvent evt) {
       //callWebService();
       //do stuff
       //updateUI(); // <----- repaint? revalidate? what?
   }
});

My current implementation calls the updateUI method which internally call validate() and repaint() to the parent component that holds the UI. This works, but sometimes I can see the screen flickering. Am I doing it wrong? Is there a better way to do it?

Upvotes: 9

Views: 23250

Answers (4)

Olivier Gr&#233;goire
Olivier Gr&#233;goire

Reputation: 35467

The right way would be to use SwingWorker, but if you want to do it manually you'll have to implement the following pattern:

@Override public void actionPerformed(java.awt.event.ActionEvent evt) {
  new Thread() {
    @Override public void run () {
      //callWebService();
      //do stuff
      SwingUtilities.invokeLater(new Runnable(){
        @Override public void run() {
          //updateUI(); // <----- repaint? revalidate? what?
        }
      });
    }
  }.start();
}

For the repaint/revalidate question, normally call revalidate() then repaint(). this is, of course, only valid for component that you manually draw. For components that you reuse, just call their value change methods.

Upvotes: 5

Michael Berry
Michael Berry

Reputation: 72379

I'd personally use SwingWorker for this, despite some of the other comments / answers:

  • Despite the fact keeping the UI responsive isn't part of the original question, it's good practice to do this anyway (I can't think of a single good reason to lock up the EDT with lengthy processing.)
  • It provides a done() method that can be implemented which will be executed on the EDT by default when the task is complete, saving the need for manually wrapping up things in invokeLater()
  • It's more extensible, providing the framework to allow information like progress to be added easily later if it's so desired.

I've seen a lot of SwingWorker hate in general recently, and I don't understand why. It's a nicely designed, extensible class specifically for purposes such as this that works well. Yes, you could wrap things up in threads and launch them and have them wrap other methods up in invokeLater(), but why reinvent the wheel when there's a better one available for free?

Upvotes: 4

Tom Hawtin - tackline
Tom Hawtin - tackline

Reputation: 147164

Take the long running task to a different thread. Send events back to the AWT Event Dispatch Thread (EDT) to update the GUI with java.awt.EventQueue.invokeLater.

Upvotes: 1

JB Nizet
JB Nizet

Reputation: 692121

You shouldn't normally have to do anything : if you use the swing components methods, or the methods of their model, the necessary events are fired and the GUI should update itself automatically.

This won't be the case if you define your own models (and forget to fire the necessary events, but then it's a bug in your models, and it should be fixed there.

Upvotes: 0

Related Questions