Cannot repaint new elements without repainting old ones

65 views Asked by At

Here is my problem :

I have made a program that draws squares at random locations, its a tad crude but it works. However the problem is that it will not repaint properly, I don't know where but somewhere in the code I made a mistake.

This causes the following to happen : I tell the application to draw 5 squares it does so but then when I tell it to draw 6 it will draw the previous 5 + 6.

The code is listed below in two parts RandomSquares and DrawField :

public class RandomSquares extends JPanel {

private static JFrame frame = new JFrame("Random Squares");
private static DrawField f;
private static JButton button = new JButton("Make squares");
private static final JTextField field = new JTextField(10);
private static int amount = 0;

private static void prepareFrame() {
    //knoppen

    button.addActionListener(new ActionListener()
    {
        @Override
        public void actionPerformed(ActionEvent e)
        {
                System.out.println(amount);
                amount = Integer.parseInt(field.getText());
                f = new DrawField(amount);
                frame.add(f, BorderLayout.CENTER);

                frame.repaint();


        }

    });

    frame.add(button, BorderLayout.NORTH);
    frame.add(field, BorderLayout.SOUTH);

    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.pack();
    Listener l = new Listener(); 
    frame.addKeyListener(l);
    frame.setSize(640, 480);

}

public static class Listener implements KeyListener {

        @Override
        public void keyTyped(KeyEvent ke) {
            //unused
        }

        @Override
        public void keyPressed(KeyEvent ke) {
            if (ke.getKeyCode() == KeyEvent.VK_R) {
                System.out.println("woot!");
            }
        }

        @Override
        public void keyReleased(KeyEvent ke) {
            //unused
        }       

}


public static void run() {
    f = new DrawField(amount);
    prepareFrame();

    frame.setVisible(true);
}
}

public class DrawField extends JComponent {

private int amount;

public int getAmount() {
    return amount;
}

public void setAmount(int amount) {
    this.amount = amount;
}

public DrawField(int amount) {
    this.amount = amount;
    this.setSize(540, 380);
    this.setBackground(Color.GREEN);
}

@Override
public void paintComponent(Graphics g) {
    super.paintComponent(g);
    Random r = new Random();
    for (int i = 0; i < amount; i++) {


                g.fillRect(r.nextInt(getWidth()), r.nextInt(getHeight()),
                        20, 20);
        }

    }
}
2

There are 2 answers

6
camickr On BEST ANSWER

Painting methods should only be used for painting, not setting properties of your class.

g.fillRect(r.nextInt(getWidth()), r.nextInt(getHeight()), 20, 20);

For example if you resize the frame a whole new set of rectangles will be painted at new random locations.

When you invoke super.paintComponent(), the old drawing will be lost and new random rectangle will be created.

Instead your painting code should be based on properties of the class. That is once you create the objects they should be fixed so a repaint of the component does not change the painting, unless you change the properties.

See Custom Painting Approaches for examples of how to painting with a random number of objects by:

  1. keeping a List of objects to paint
  2. add objects to a BufferedImage and then just paint the image.

In your case you would invoke the addRectangles(...) method for the specified number of rectangle you want to paint. Then the painting code will do the rest.

Edit:

To answer your basic question. In general you need to invoke super.paintComponent() to clear the background of a component before doing the custom painting of the component.

The problem is that you are extending JComponent, which doesn't not have any default painting code, so the background doesn't get cleared. Two solutions:

  1. Extend JPanel instead (JPanel does clear the background).
  2. Add custom painting code to your class to clear the background:

Something like:

g.setColor( getBackground() );
f.fillRect(0, 0, getWidth(), getHeight());
// paint rectangle here
4
copeg On

I tell the application to draw 5 squares it does so but then when I tell it to draw 6 it will draw the previous 5 + 6.

The Component is not being cleared of any previous painting calls - be sure to always call the parent painting method to clear any previous painting. For example:

@Override
protected void paintComponent(Graphics g){
    super.paintComponent(g);
    //custom painting here.
}