How to initialize a blank Jpanel?

Asked

Viewed 448 times

0

I’m starting to work with the package swing and manipulation of events and I came up with an idea to test some program that would create rectangles of random sizes and colors. Code creation and execution all came out as planned, except for the fact that the frame already initializes with a drawn rectangle.

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

OBS: The idea of the program is that the squares are drawn on top of each 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();
    }
 }
}
  • I don’t understand, what’s wrong with the code? It’s not clear

  • I want to start the application with the blank drawing area, then each time the user clicks the button, the system will call repaint() that will draw the rectangles. But the drawing area starts already drawn.

  • The paintcomponent method will always be called, any changes on the screen it is redesigned, the way it is, until minimizing will create squares, you need to create a mechanism that avoids this.

  • You can place rectangles in a list by adding one whenever the user clicks on a button. In paintComponent() you paint the white background first, then use a foreach to scroll through the List of rectangles painting each. :)

  • @Douglas isn’t just that, he needs a mechanism to control creation as well. I would use bufferedimage, keep making loop in the paintcomponent I think very bad, when you have a class for images that controls this without weighing the application with loops.

  • Um, as I recall Bufferedimage gives you a Graphics2d that you can use to paint the rectangle directly in the Bufferedimage, so a new rectangle would be painted on it whenever the button was clicked. You would keep Bufferedimage in an instance variable and paint it in paintComponent() without using a loop. If that’s it, it looks a lot better to me than my suggestion.

  • Well, I found a way to solve the problem, but there’s an annoying bug in this code that is "mirroring" the southern boot in the northern position. I see a way to solve this bug.

  • 1

    @That’s what Douglas is. And take the responsibility of the paintcomponent to pick up these points, as he is doing, delegate the repaint and the creation to a method outside.

Show 3 more comments

1 answer

5


First of all I leave a warning:

Always start the screen inside the Event-Dispatch-Thread, because swing is not Thread-Safe, and the entire GUI needs to start in this one Thread. In this reply explains better the reason for this and any problems that may occur. This other reply shows some ways to start the application within this thread.


The way you’re doing it, you’re delegating the drawing of the squares to paintComponent, only that this method is called every time the component needs to be redesigned, including this method is invoked as soon as you instantiate your dashboard. For this reason already starts with a drawing made.

The solution I propose is this::

  • Utilise BufferedImage for "store" the figures drawn, thus avoiding that one has to keep storing in lists and then traversing links.

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

Applying the above two suggestions, extend ActionListener how you’re doing becomes unnecessary.

Also, I modified the action of the button, 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 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:

inserir a descrição da imagem aqui

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

Browser other questions tagged

You are not signed in. Login or sign up in order to post.