How to boot a blank JPanel?

I'm starting to work with the swing and event manipulation package and here I came up with an idea to test some program that would create rectangles of random sizes and colors. The creation of the code and execution came out everything in the predicted, except for the fact that the frame already initializes with a drawn rectangle.

I tried to set the panel background as opaque and white at startup but the problem persists.

Note: the idea of the program is that squares are drawn one on top of the other itself.

public class RandomSquares {

NovoPainel painel;
JFrame frame;
public static void main(String[] args) {
    RandomSquares rs = new RandomSquares();
    rs.construirGUI();
}

public void construirGUI() {

    frame = new JFrame("Squares");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setSize(450,450);
    frame.setLocationRelativeTo(null);
    frame.setResizable(false);

    painel = new NovoPainel();

    JButton botao = new JButton("CRIAR");
    botao.addActionListener(new NovoPainel());

    frame.getContentPane().add(painel);
    frame.getContentPane().add(BorderLayout.SOUTH, botao);
    frame.setVisible(true);
}

public class NovoPainel extends JPanel implements java.awt.event.ActionListener {

    public void paintComponent(Graphics g) {

        int red = (int) (Math.random() * 256);
        int green = (int) (Math.random() * 256);
        int blue = (int) (Math.random() * 256);

        int posicaoX = (int) (Math.random() * this.getWidth());
        int posicaoY = (int) (Math.random() * this.getHeight());
        int largura = (int) (Math.random() * this.getWidth() + 1);
        int altura = (int) (Math.random() * this.getHeight() + 1);

        g.setColor(new Color(red, green, blue));
        g.fillRect(posicaoX, posicaoY, largura, altura);
    }

    public void actionPerformed(java.awt.event.ActionEvent ev) {
        painel.repaint();
    }
 }
}
Author: ADayan, 2017-06-26

1 answers

First of all I leave an alert:

Always start the screen inside Event-Dispatch-Thread, because swing is not Thread-Safe , and the whole GUI needs to start inside this is a single Thread. in this answer better explains the reason for this and any problems that may occur. this other answer shows some ways how to start the application within this Thread.


The way you're doing, you you are delegating the drawing of the squares to paintComponent, only this method is called every time the component needs to be redrawn, including this method is invoked as soon as you instantiate your panel. For this reason it already begins with a drawing done.

The solution I propose is as follows:

  • Use BufferedImage to "store " the drawn figures, thus avoiding having to be storing in lists and then scrolling ties.

  • Delegate the drawing of the squares to a method the part in the panel, leaving the paintComponent only to update the component with the image of the BufferedImage.

Applying the two suggestions above, extending ActionListener as you are doing becomes unnecessary.

In addition, I modified the button action, so that it calls the method of your panel that will draw a random rectangle, whenever it is clicked.

With the modifications, the code becomes the form below (contains comments in the relevant parts):

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class RandomSquares {

    NovoPainel painel;
    JFrame frame;

    public static void main(String[] args) {
        //aplicações swing devem iniciar SEMPRE desta forma
        SwingUtilities.invokeLater(() -> {
            RandomSquares rs = new RandomSquares();
            rs.construirGUI();
        });

    }

    public void construirGUI() {

        frame = new JFrame("Squares");

        painel = new NovoPainel();

        JButton botao = new JButton("CRIAR");

        //sempre que clicado, vai chamar o método que
        //desenhará quadrados aleatórios no painel
        botao.addActionListener(e -> {
            painel.criarRetangulos();
        });

        frame.getContentPane().add(painel, BorderLayout.CENTER);
        frame.getContentPane().add(botao, BorderLayout.SOUTH);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        frame.setSize(450, 450);
        frame.setResizable(false);
        frame.setVisible(true);
    }

    public class NovoPainel extends JPanel {

        BufferedImage image;

        public NovoPainel() {
            setLayout(null);
        }

        public void criarRetangulos() {

            //cria uma instancia de BufferedImage, se ela nao existir
            if(image == null)
                image = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB);            


            int red = (int) (Math.random() * 256);
            int green = (int) (Math.random() * 256);
            int blue = (int) (Math.random() * 256);

            int posicaoX = (int) (Math.random() * this.getWidth());
            int posicaoY = (int) (Math.random() * this.getHeight());
            int largura = (int) (Math.random() * this.getWidth() + 1);
            int altura = (int) (Math.random() * this.getHeight() + 1);  

            //cria graficos da imagem para que possamos "desenhar" nela
            Graphics2D g2 = image.createGraphics();
            g2.setColor(new Color(red, green, blue));
            g2.fillRect(posicaoX, posicaoY, largura, altura);
            //dispensa os graficos, uma vez que já concluimos o desenho
            g2.dispose();
            //redesenha a tela com um novo quadrado pintado
            repaint();
        }

        public void paintComponent(Graphics g) {
            //esta chamada SEMPRE deve ser invocada
            //antes de tudo ao reescrever o método paintComponent
            super.paintComponent(g);
            g.drawImage(image, 0, 0, null);
        }

    }
}

Working:

insert the description of the image here

It is worth noting that what causes the screen to update immediately every time you click the button is the call of repaint() right after drawing a random rectangle, since it is this method that notifies the screen that there have been changes and it needs to be redrawn, through the paintComponent().

 5
Author: , 2017-07-14 17:59:00