带有计时器/延迟的JPanel上的Java绘图

时间:2011-11-15 14:37:11

标签: java swing graphics applet

我正在尝试制作一个小型Mosaic程序,在整个JPanel中随机绘制正方形。我想这样做,它每0秒吸收1平方(不是一次全部),但到目前为止我只能用while循环一次绘制所有。我已经尝试使用ActionListener和Timer,但我发现,我无法将相同的Graphics g传递给ActionListener。然后我尝试使用Thread.Sleep(200)然后应用程序冻结了。现在我尝试使用System.currentTimeMillis();但它与线程相同...在互联网上搜索但没有找到任何有用的东西。

主:

import javax.swing.JApplet;


public class Main extends JApplet{
public void init(){
    setSize(500, 300);
    Mosaic mosaic = new Mosaic();

    setContentPane(mosaic);

}
}

应用:

import java.awt.Color;
import java.awt.Graphics;
import java.util.Random;
import javax.swing.JPanel;

public class Mosaic extends JPanel{

private int width, height;
private int ruut; // square 
private int w = width / 2, h = height / 2; // middle of the app 

Random rand = new Random();
Color color;

public Mosaic(){
    this(500, 300, 10);     
}

public Mosaic(int width, int height, int ruut){
    this.width = width;
    this.height = height;
    this.ruut = ruut;       
    setBackground(Color.BLACK);     
}

public void paintComponent(Graphics g){
    super.paintComponent(g);    
    //draws random squares
    while (true) {
        moveNext(g);
        wait(200);
    }
}

//delay n millisec
public void wait(int n){
    long t0, t1;
    t0 = System.currentTimeMillis();

    do {
        t1 = System.currentTimeMillis();            
    } while ((t1 - t0) < n);
}   

//next square
public void moveNext(Graphics g){       

    int r = rand.nextInt(4);        

    switch (r) {
    case 1:
        h += ruut;
        wallTest();
        break;
    case 2:
        h -= ruut;
        wallTest();
        break;
    case 3:
        w -= ruut;
        wallTest();
        break;
    case 4:
        w -= ruut;
        wallTest();
        break;
    }

    color = new Color(0, rand.nextInt(255-50)+50, 0);
    g.setColor(color);
    g.fillRect(w, h, ruut, ruut);       
}

public void wallTest(){
    if (h > height){
        h = 0;
    }
    if (h < 0){
        h = height;
    }
    if (w > width){
        w = 0;
    }
    if (w < 0){
        w = width;
    }
}   

}

4 个答案:

答案 0 :(得分:2)

javax.swing.Timer类与重写paintComponent方法结合使用。您 想要在EDT中执行长时间运行的任务,因为这会导致GUI冻结。

示例 -

new javax.swing.Timer(DELAY_IN_MILLIS, new ActionListener(){
    @Override
    public void actionPerformed(ActionEvent e){
        // do stuff
        repaint();
    }
});

答案 1 :(得分:2)

  

我尝试过ActionListener和Timer,但我发现,我无法将相同的图形g传递给ActionListener。

当然不是,Graphics对象是暂时的。相反,要么做以下任何一种:

  1. 在可展开的列表中添加一个矩形并调用repaint()(在循环中),一次绘制列表中的每个对象。
  2. BufferedImage放入JLabel,并一次向BufferedImage添加一个“矩形”。

答案 2 :(得分:2)

您需要维护一个屏幕外缓冲区,并在计时器事件的控制下将图块绘制到该缓冲区中。

然后paintComponent只是将该缓冲区复制到屏幕上。

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.util.Random;

import javax.swing.JPanel;
import javax.swing.Timer;

public class Mosaic extends JPanel{

    private int width, height;
    private int ruut; // square 
    private int w = width / 2, h = height / 2; // middle of the app 
    private BufferedImage buffer;

    Random rand = new Random();
    Color color;

    public Mosaic(){
        this(500, 300, 10);     
    }

    public Mosaic(int width, int height, int ruut){
        this.width = width;
        this.height = height;
        this.ruut = ruut;
        this.buffer = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
        setBackground(Color.BLACK); 
        setPreferredSize(new Dimension(width, height));
        setDoubleBuffered(false);
        new Timer(200, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                moveNext(buffer.getGraphics());
            }
        }).start();
    }

    public void paintComponent(Graphics g){
        super.paintComponent(g);
        g.drawImage(buffer, 0, 0, this);
    }

    //next square
    public void moveNext(Graphics g){       

        int r = rand.nextInt(4);        

        switch (r) {
        case 1:
            h += ruut;
            wallTest();
            break;
        case 2:
            h -= ruut;
            wallTest();
            break;
        case 3:
            w -= ruut;
            wallTest();
            break;
        case 4:
            w -= ruut;
            wallTest();
            break;
        }

        color = new Color(0, rand.nextInt(255-50)+50, 0);
        g.setColor(color);
        g.fillRect(w, h, ruut, ruut);
        repaint();
    }

    public void wallTest(){
        if (h > height){
            h = 0;
        }
        if (h < 0){
            h = height;
        }
        if (w > width){
            w = 0;
        }
        if (w < 0){
            w = width;
        }
    }   
}

答案 3 :(得分:1)

您可以使用计时器方法,因为这只是不冻结应用程序的方法。您可以在计时器触发时调用重绘或某种等效方法,然后根据要求在绘制方法中绘制。