开发者

Java Swing + Threads

This code draws two lines but waits a second.. I am looking how to do that in a separate thread so it wont freeze the application.. To draw one line and display it to the user and then the second.. Sorry but i am confused.. found too many solut开发者_StackOverflowions

public class Askisi2_3 extends JFrame {

    private class LineJPanel extends JPanel {

        public LineJPanel() {                    
            setSize(500,500);
        }

        private void drawRandomLines(Graphics g) {
            g.drawLine(5, 4, 50, 100);
            try{
                Thread.sleep(1000);
            } catch(InterruptedException ex) {

            }
            g.drawLine(5, 4, 50, 200);                  
        }

        @Override        
        public void paint(Graphics g) {
            super.paint(g);
            drawRandomLines(g);                   
        }

    }


    public Askisi2_3() {
        initialiseComponents();
    }

    private void initialiseComponents() {
        JPanel panel = new LineJPanel();

        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        add(panel);
        setSize(500, 500);
        setVisible(true);
    }
}

EDIT

Thank you for your responses!!! A requirement for this is to use

try{
    Thread.sleep(1000);
}

Is this possible ?

Here is my updated code

@Override
public void paint(Graphics g) {
    super.paint(g);
    for (int i = 0; i < lines.length; i++) {
        try{
            Thread.sleep(1000);
        }catch(InterruptedException e) {

        }
        g.drawLine(lines[i].getX1(),lines[i].getY1(), lines[i].getX2(), lines[i].getY2());

    }
}

Before opening this thread googled it and found about Timer.. But i am forced to use Thread.Sleep().. so is there a solution or not?

So pst you are suggesting to put the sleep outside somehow?


Short answer

You can use Thread.sleep but not from the paint method. Use it from outside and just reapaint your panel.

Long answer

As it is now, your code paints the panel, and until the pause is finish it returns. Visually it would look like the paint took too much time to finish.

What you need, is to have a "model" to paint. Your component would just paint that model and finish.

Then you add more "things" to your model every second, and that's it.

For instance. Let's say your model is an array of lines:

class Line {
    int x1, y1, x2, y2;
}

class LineJPanel extends JPanel {
// this is the private model
private Line[] lines = new Line[10];
.....

What you need to do in your paint method is to draw those lines:

// exactly as you have them:
@Override
public void paint(Graphics g) {
    super.paint(g);
    drawRandomLines(g);
}
// Changed. Do no "sleep" here, or you'll freeze the GUI
// just draw whatever your model is/has.
private void drawRandomLines(Graphics g) {
    for( Line line : lines ){
        if( line != null ){ 
            g.drawLine( line.x1, line.y1, line.x2, line.y2 );
        }
    }
}

And that's it. That way you won't freeze the GUI.

To add the effect of having more and more lines, you'll create a separate thread and add lines to it.

To keep things simple, you can add that thread in the constructor:

public LineJPanel() {
    setSize(500,500);
    Thread t = new Thread(){
         public void run(){
             while( true ) {
                 // add random lines and repaint
                 // sleep for a while
                 // and repeat.
            }
        }
    };
    t.start();
 }

That should as simple as adding more lines into the "model" ( the array ) and let the component re-paint them.

So to complete the code we could add a addRandomLine method that creates a line, set some random values, and put it in the array:

private void addRandomLine(){
    Line line = new Line();
    line.x1  = random.nextInt(500);
    line.y1  = random.nextInt(500);
    line.x2  = random.nextInt(500);
    line.y2  = random.nextInt(500);
    lines[count++] = line;//put it in the next position
    // if we reach the limit, start all over again 
    // from 0
    if( count == lines.length ){
        count = 0;
    } 
}

So, wrapping up your new thread would look like:

   Thread t = new Thread(){
        public void run(){
            while( true ){
                addRandomLine();
                repaint();
                // and as per the requiement: 
                try{
                    Thread.sleep( 1000 );
                }catch( InterruptedException ie ){}
            }
        }
    };

Be aware that, this would invoke repaint in other thread different to the EDT. To fix this we would use: SwingUtilities.invokeLater which let us define a method to be invoked "eventually" in the EDT:

So, the final code ( with some formatting enhancements from my part ) would be:

import javax.swing.*;
import java.awt.*;
import java.util.Random;
public class Askisi2_3 extends JFrame {

    public Askisi2_3() {
        initialiseComponents();
    }

    private void initialiseComponents() {
        JPanel panel = new LineJPanel();
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        add(panel);
        setSize(500, 500);
        setVisible(true);
    }
    public static void main( String [] args ) {
        new Askisi2_3();
    }
}
// line abstraction
class Line {
    int x1, y1, x2, y2;
}
class LineJPanel extends JPanel {
    // this is the private model
    private Line[] lines = new Line[10];// fixed of size 10 by now.
    // private internal index position
    private int count = 0;

    // generates "random" numbers
    private Random random = new Random();

    // create the panel and start adding more lines in a separate thread.
    public LineJPanel() {
        setSize(500,500);

        Thread t = new Thread(){
            public void run(){
                // forever:
                while( true ){
                    //add another line
                    addRandomLine();
                    // rapaint it
                    SwingUtilities.invokeLater( new Runnable(){
                        public void run(){
                            repaint();
                        }
                    });
                    // sleep for while
                    try{
                        Thread.sleep( 1000 );
                    }catch( InterruptedException ie ){}
                }
            }
        };
        t.start();
    }
    // just draw the model
    private void drawRandomLines(Graphics g) {
        for( Line line : lines ){
            if( line != null ){ 
                g.drawLine( line.x1, line.y1, line.x2, line.y2 );
            }
        }
    }
    @Override
    public void paint(Graphics g) {
        super.paint(g);
        drawRandomLines(g);
    }
    // add another line to the "model"
    private void addRandomLine(){
        Line line = new Line();
        line.x1  = random.nextInt(500);
        line.y1  = random.nextInt(500);
        line.x2  = random.nextInt(500);
        line.y2  = random.nextInt(500);
        lines[count++] = line;
        if( count == lines.length ){
            count = 0;
        } 
    }

} 

The result is a very nice "line animated" panel:

Java Swing + Threads


Wrap the second drawLine with an if-statement for some volatile boolean (e.g. drawSecondLine):

if ( drawSecondLine ) {
   g.drawLine(5, 4, 50, 200);
}

Then schedule a java.util.Timer to run a timer-task which sets that boolean to true after 1000 ms. From that timer task, call repaint() on your panel.

    new Timer().schedule(new TimerTask() {
        public void run() {
            drawSecondLine = true;
            panel.repaint();
        }
    }, 1000);

Optionally, use a Swing timer so that the toggle happens on the EDT, so you don't need a volatile boolean.

Response to asker's "answer":

You can avoid the Timer task and still use Thread.sleep by setting the boolean from the main thread (not the Swing event dispatch thread!). For instance, you could put the logic of run() above at the end of initializeComponents as one example, after a Thread.sleep(1000).


You need to separate out the paint mechanism from the setting of the lines.

Create LineJPanel so that it stores the coordinates of the lines it is supposed to be drawing. Write the paint method so that it draws the lines stored: e.g.

class LineJPanel extends JPanel {
  int x1,y1,x2,y2;
  void setLine(int newX1,int newY1,newX2,newY2) {
    x1=newX1; ///...etc
    repaint();
  }
  void paint(Graphics g) {
    g.drawLine(x1,y1,x2,y2);
  }
}

Then create a separate thread that calls setLine at 1s intervals on the LineJPanel. That will change the line every second, but won't change it if you do something else like expose the window or resize it.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜