How to initialize a blank JPanel?

0

I'm starting to work with the swing and event handling package and here's what I came up with to test some program that would create rectangles of random sizes and colors. The creation of the code and execution went all out, except that the frame already initialized with a drawn rectangle.

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

OBS: The idea of the program is that the squares are drawn one above the other.

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();
    }
 }
}
    
asked by anonymous 26.06.2017 / 04:34

1 answer

5

First of all I leave an alert:

  

Always start the screen within the Event-Dispatch-Thread ,   because swing is not Thread-Safe , and the whole GUI needs to start in.   of this unique Thread. Nesta   answer better explains the   reason for this and possible problems that may occur. This Other   answer shows some   ways to start the application within this Thread.

As you are doing, you are delegating the drawing of squares to paintComponent , but this method is called every time the component needs to be redrawn, even this method is invoked as soon as you instantiate your panel. For this reason already starts with a drawing done.

The solution I propose is as follows:

  • Using BufferedImage to " store " the drawn figures, thus avoiding having to be stored in lists and then traversing loops.

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

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

In addition, I modified the button's 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 is as follows (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);
        }

    }
}

Running:

Note that what causes the screen to update immediately each time you click the button is the call of repaint() shortly after drawing a random rectangle, as it is this method that notifies the screen of changes, and it needs to be redrawn through paintComponent() .

    
26.06.2017 / 06:16