重新绘制/刷新JFrame窗口

3

我有一个用图形g制作的“汽车”,由各种物体组成,我希望在按下按钮时移动它。关于这一点,我没有问题,但是我遇到了路径问题。当汽车移动时,旧位置不会清除。

汽车的代码(按下按钮时移动):

    static void gPostavi2(Graphics g){
    Graphics2D g2d = (Graphics2D) g;

    for(int x=500; x>89; x--){
        //risanje
        g2d.setColor(Color.blue);
        g2d.fillRect(x+10, 351, 118, 23);
        g2d.fillRect(x+12, 321, 30, 40);
        g2d.fillRect(x+45, 330, 83, 20);
        g2d.setColor(Color.black);      
        g2d.fillOval(x+19, 362, 20, 20);
        g2d.fillOval(x+90, 362, 20, 20);
        g2d.drawString("2t", x+70, 344);
        try {

            Thread.sleep(5);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }       
}

这个类中只有移动物体的方法,这个类继承了另一个没有移动物体的类,其中包括按钮、标签等和paintComponent方法。

如何在for语句执行时,每次清除旧位置?

编辑:下面是更多代码。在主类中我只有这段代码:

    public static void main(String[] args) {
    staticnaGrafika.dodajGumbe();
}

在staticnaGrafika中,我有大量的代码,但这是paintComponent的开始:
public class staticnaGrafika extends JPanel{

staticnaGrafika(){
        setBorder(BorderFactory.createLineBorder(Color.black));
    }
    public Dimension getPreferredSize(){
        return new Dimension(1100, 740);
    }

public void paintComponent(Graphics g){
    super.paintComponent(g);
        Graphics2D g2d = (Graphics2D) g;
        g2d.setColor(Color.RED);

        //opticno stikalo 
        //preveri, ce ga plosca prekriva pri max. dvigu
        g2d.setColor(Color.black);
        g2d.fillOval(21, 148, 33, 33);
        g2d.setColor(Color.yellow);
        g2d.fillOval(22, 149, 31, 31);
        g2d.setColor(Color.black);      
        g2d.fillRect(13, 159, 11, 1); //el. prikljucnice
        g2d.fillRect(13, 166, 10, 1);
        g2d.drawOval(7, 157, 5, 5);
        g2d.drawOval(7, 164, 5, 5);

        //naslon; spodnji omejevalec hoda bata
        g2d.setColor(Color.black);
        g2d.fillRect(5, 350, 13, 43);
        g2d.fillRect(5, 380, 63, 13);
        g2d.fillRect(262, 350, 408, 13);
        g2d.fillRect(262, 350, 13, 43);
        g2d.fillRect(212, 380, 63, 13);

这里只有绘画。接下来我将介绍另外一种方法,它可以添加按钮和事件监听器:

    public static void dodajGumbe() {
    final JFrame f = new JFrame();

    //dvig, stop, spust
    JButton dvig = new JButton("DVIGNI");
    dvig.setBackground(Color.WHITE);
    dvig.setFocusPainted(false);
    dvig.setBounds(850,15,120,30);

    JButton stop = new JButton("STOP");
    stop.setBackground(Color.WHITE);
    stop.setFocusPainted(false);
    stop.setBounds(850,50,120,30);

    JButton spust = new JButton("SPUSTI");
    spust.setBackground(Color.WHITE);
    spust.setFocusPainted(false);
    spust.setBounds(850,85,120,30);

    //komande bremen
    JButton postavi2 = new JButton("nalozi breme");
    postavi2.setBackground(Color.WHITE);
    postavi2.setFocusPainted(false);
    postavi2.setBounds(760,240,120,30);

    JButton odvzemi2 = new JButton("razlozi breme");
    odvzemi2.setBackground(Color.WHITE);
    odvzemi2.setFocusPainted(false);
    odvzemi2.setBounds(760,275,120,30);

    JButton postavi5 = new JButton("nalozi breme");
    postavi5.setBackground(Color.WHITE);
    postavi5.setFocusPainted(false);
    postavi5.setBounds(760,330,120,30);

    JButton odvzemi5 = new JButton("razlozi breme");
    odvzemi5.setBackground(Color.WHITE);
    odvzemi5.setFocusPainted(false);
    odvzemi5.setBounds(760,365,120,30);

    Container gumbi = f.getContentPane();

    spust.addActionListener(new ActionListener(){ 
        public void actionPerformed(ActionEvent arg0) {
        dinamicnaGrafika.gSpusti(f.getGraphics());
    }});

    dvig.addActionListener(new ActionListener(){ 
        public void actionPerformed(ActionEvent arg0) {
            dinamicnaGrafika.gDvigni(f.getGraphics());
    }});

    stop.addActionListener(new ActionListener(){ 
        public void actionPerformed(ActionEvent arg0) {
            dinamicnaGrafika.gStop(f.getGraphics());
    }});

    postavi2.addActionListener(new ActionListener(){ 
        public void actionPerformed(ActionEvent arg0) {
            dinamicnaGrafika.gPostavi2(f.getGraphics());
    }});

    odvzemi2.addActionListener(new ActionListener(){ 
        public void actionPerformed(ActionEvent arg0) {
            dinamicnaGrafika.gOdvzemi2(f.getGraphics());
    }});

    postavi5.addActionListener(new ActionListener(){ 
        public void actionPerformed(ActionEvent arg0) {
            dinamicnaGrafika.gPostavi5(f.getGraphics());
    }});

    odvzemi5.addActionListener(new ActionListener(){ 
        public void actionPerformed(ActionEvent arg0) {
            dinamicnaGrafika.gOdvzemi5(f.getGraphics());
    }});

    gumbi.add(dvig);
    gumbi.add(stop);
    gumbi.add(spust);
    gumbi.add(postavi2);
    gumbi.add(odvzemi2);
    gumbi.add(postavi5);
    gumbi.add(odvzemi5);

    f.getContentPane();
    f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    f.add(new staticnaGrafika());
    f.pack();
    f.setVisible(true);
}

除非您维护汽车图像的坐标列表,否则您的先前坐标将不会被保留。这意味着不需要清除旧位置(因为没有要清除的位置)。之前的图像很可能是因为在重新绘制之前未清除屏幕。 - user3437460
@Skynet 我下面添加了另一种完全可行的解决方案。你应该看一下。 - user3437460
@Skynet 你的问题解决了吗? - user3437460
3个回答

4
你可能忘记调用了。
super.paintComponent(g);

在你的paintComponent()方法中。
@Override
protected void paintComponent(Graphics g){
    super.paintComponent(g);  //Clear screen before redraw
    //Your codes for painting..
}

@Skynet 在这种情况下,您可能希望展示更多的代码,特别是有关绘制和更新对象坐标的部分。 - user3437460
我已经在帖子中添加了更多的代码。希望现在更清晰了。 - Skynet
@Skynet 我担心你添加的代码与你当前的问题并没有真正的关联。你是否维护了一个汽车对象的坐标列表?所有的fllRect和drawOval代码都应该是用于绘制你的汽车对象的代码吗? - user3437460
是的,我发布的原始代码是汽车的代码。它在一个for语句中,所以当按下按钮时,汽车向左移动屏幕。 fillRect是汽车上半部分的代码,而oval则是轮子的代码,因此你可以在脑海中想象出一幅图像。我认为这些坐标并没有被保存在任何地方。 - Skynet
我能够实现的是在相同位置绘制相同的椭圆和矩形,但带有背景颜色。 - Skynet
@Skynet 我已经为你编写了一个完整的工作示例.. 我将在一分钟内发布它。 - user3437460

4

您不应该在UI线程上调用sleep()方法。相反,我强烈建议您使用javax.swing.Timer和ActionListener。类似于:

 void paintCar(Graphics2D g2d, int x) {
    g2d.setColor(Color.blue);
    g2d.fillRect(x+10, 351, 118, 23);
    g2d.fillRect(x+12, 321, 30, 40);
    g2d.fillRect(x+45, 330, 83, 20);
    g2d.setColor(Color.black);      
    g2d.fillOval(x+19, 362, 20, 20);
    g2d.fillOval(x+90, 362, 20, 20);
    g2d.drawString("2t", x+70, 344);
}       

int x = 0;
public MyConstructor() {
  new Timer(5, this).start();
}

public void actionPerformed(ActionEvent ae) {
  x++;
  repaint();
}

public void paintComponent(Graphics g) {
  super.paintComponent(g);
  Graphics2D g2d = (Graphics2D) g;
  paintCar(g2d, x);
}

1
@AndrewThompson 谢谢,我有些粗心了。 - ControlAltDel

1
看起来问题出在你的实现上。你可以为你的汽车对象创建一个类,每个汽车对象都跟踪它们自己的坐标。
对于你的情况,如果你只在按钮点击时移动汽车,那么你甚至不需要一个计时器。只需在每次按钮点击的ActionListener中更新汽车的位置即可。 输出: enter image description here
class Car
{
    private Color carColor;
    private int x, y;
    private int speed;
    private static int carWidth = 100;  
    private static int carHeight = 30;

    public Car(Color carColor, int speed){
        this.carColor = carColor;
        this.speed = speed;
        x = 0;
        y = 0;
    }

    public void moveTo(int x, int y){
        this.x = x;
        this.y = y;
    }

    public void draw(Graphics g){
        //Draw a car object
        g.setColor(carColor);
        g.fillRect(x, y, carWidth, carHeight);
        g.setColor(Color.BLACK);
        //Draw Wheels
        g.fillOval(x, y+carHeight, 30, 30); 
        g.fillOval(x+carWidth-30, y+carHeight, 30, 30);     
    }

    public int getX(){return x;}
    public int getY(){return y;}    
    public int getSpeed(){return speed;}        
}

所以当您移动汽车时,只需更新它们的位置即可,这就是您需要做的全部。特别注意我的paintComponent()方法。您应该保持该方法简单和无杂物。它只负责绘画。所有汽车的移动都在其他地方完成。
class DrawingSpace extends JPanel implements ActionListener{
    Car c1, c2, c3; 

    public DrawingSpace(){
        setPreferredSize(new Dimension(800, 400));
        c1 = new Car(Color.RED, 5);
        c2 = new Car(Color.GREEN, 8);
        c3 = new Car(Color.BLUE, 10);
        c1.moveTo(10, 50);              
        c2.moveTo(10, 180);
        c3.moveTo(10, 280);                 
    }

    public void moveCars(){             
    }

    @Override
    protected void paintComponent(Graphics g){
        super.paintComponent(g);
        c1.draw(g);
        c2.draw(g);
        c3.draw(g);
    }

    @Override
    public void actionPerformed(ActionEvent e){
        //Used for timer (to animate moving cars)
        c1.moveTo((c1.getX()+c1.getSpeed()), c1.getY());
        c2.moveTo(c2.getX()+c2.getSpeed(), c2.getY());
        c3.moveTo(c3.getX()+c3.getSpeed(), c3.getY());
        if(c1.getX() > 800)
            c1.moveTo(0, c1.getY());
        if(c2.getX() > 800)
            c2.moveTo(0, c2.getY());
        if(c3.getX() > 800)
            c3.moveTo(0, c3.getY());                        
        repaint();          
    }
}

对于这样的任务,使用javax.swing.timer比使用Thread.sleep()实现自己的循环更好、更容易。

class MovingCars{
    public static void main(String[] args){

      javax.swing.SwingUtilities.invokeLater(new Runnable(){
         public void run() {        
            JFrame f = new JFrame("Moving Cars");
            DrawingSpace ds = new DrawingSpace();
            f.setVisible(true);
            f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            f.add(ds);
            f.pack();
            f.setLocationRelativeTo(null);
            Timer t = new Timer(50, ds); //Delay of 50 milliseconds
            t.start();
         }
      });       
    }
}

备注: 所有代码均由我编写,如果您对此有疑问,请告诉我。当您移动汽车时,只需更新它们的坐标即可。您目前正在通过for循环在同一辆汽车对象上进行多次绘制,这就是为什么您看到旧图像的原因。 - user3437460

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接