SwingUtilities.invokeLater not working as expected

1.9k views Asked by At

I am working on a concurrent canvas written in Java which will make the users think that they are drawing on the canvas in parallel.

In order to achieve the user perceived parallelism, I get them to create these Runnable objects that I go ahead and put on the EventQueue using SwingUtilities.invokeLater().

In order to test it, I simulated the users using a couple of Threads and added a bit of delay(around 50ms) between each call to invokeLater(), in order to see if it actually looks like the drawing is happening in parallel.

The problem is that while it worked fine with that added delay between the invokeLater() calls, taking out that delay results in the drawings being drawn properly sometimes, partially being drawn and vanishing sometimes and just not being drawn at other times.

I am pretty stumped about what could be going wrong so any if anyone has any ideas, please let me know.

Following is the code with the delay commented out:

public void run(){
    //add tasks on to the event queue of the EDT 
    for(int i = 0; i<numLines; i++){
        DrawLineTask task = new DrawLineTask(g, x1, y1+i, x2, y2+i, lineColor);
        SwingUtilities.invokeLater(task);
//          try {
//    Thread.sleep(new Double(Math.random()*50).longValue());//random sleeping times to             make it appear more realistic
//          } catch (InterruptedException e) {
//              e.printStackTrace();
//          }
    }

Cheers

EDIT: Here is the code for the DrawLineTask as requested. Its pretty simple as its just an extension of the Runnable class that draws a line using the standard Java function at the given parameters.

public class DrawLineTask implements Runnable {
Graphics g;
int x1 = 0;
int y1 = 0;
int x2 = 0;
int y2 = 0;
Color color = Color.BLACK;

public DrawLineTask(Graphics g, int x1, int y1, int x2, int y2){
    this.g = g;
    this.x1 = x1;
    this.y1 = y1;
    this.x2 = x2;
    this.y2 = y2;
}

public DrawLineTask(Graphics g, int x1, int y1, int x2, int y2, Color color){
    this.g = g;
    this.x1 = x1;
    this.y1 = y1;
    this.x2 = x2;
    this.y2 = y2;
    this.color = color;
}

@Override
public void run() {
    g.setColor(color);
    g.drawLine(x1, y1, x2, y2);
}

}
1

There are 1 answers

4
JB Nizet On BEST ANSWER

AFAIK, you're not supposed to keep a reference to a Graphics object and draw on it when you want to. Instead, you're supposed to wait for the paintComponent() method to be called by Swing, and do your drawings in this method.

So, your task should just change the state of your component, and ask for an asynchronous or synchronous repaint (using repaint() or paintImmediately()). Swing will then invoke the paintComponent() method with a Graphics object that you can use to paint the appropriate lines, based on the state of your component.

See http://java.sun.com/products/jfc/tsc/articles/painting/ for more details and explanations.