Imagem de Fundo no JFrame ou JPanel

2020/10/09

Frequentemente surge alguém com uma dúvida de como colocar uma imagem de fundo em uma aplicação desktop feita em Swing.

Pois bem, o Swing não tem nenhum componente pronto pra isso, mas a implementação é bastante simples de se fazer, basta especializar um JPanel e sobrescrever o método paintComponent para desenhar a imagem desejada.

Portanto, criei a classe ImagePanel, disponível logo abaixo:

Classe ImagePanel:

    import java.awt.Component;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.Image;
    import java.awt.LayoutManager;
    import java.awt.MediaTracker;
    import java.awt.image.BufferedImage;
    
    import javax.swing.JPanel;
    
    public class ImagePanel extends JPanel {
    
        private static final Component OBSERVER = new Component() {
    
            private static final long serialVersionUID = 1;
        };
    
        private static final long serialVersionUID = 1;
    
        public static BufferedImage bufferize(Image img) {
            if (img instanceof BufferedImage) {
                return (BufferedImage) img;
            }
            try {
                MediaTracker tracker = new MediaTracker(OBSERVER);
                tracker.addImage(img, 0);
                tracker.waitForID(0);
                tracker.removeImage(img, 0);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            BufferedImage buffered = new BufferedImage(img.getWidth(OBSERVER), img.getHeight(OBSERVER), BufferedImage.TYPE_INT_ARGB);
            Graphics2D graphics = buffered.createGraphics();
            graphics.drawImage(img, 0, 0, null);
            graphics.dispose();
            return buffered;
        }
    
        private boolean stretched = true;
    
        private BufferedImage image;
    
        public ImagePanel() {
            super();
        }
    
        public ImagePanel(boolean isDoubleBuffered) {
            super(isDoubleBuffered);
        }
    
        public ImagePanel(LayoutManager layout) {
            super(layout);
        }
    
        public ImagePanel(LayoutManager layout, boolean isDoubleBuffered) {
            super(layout, isDoubleBuffered);
        }
    
        public BufferedImage getImage() {
            return image;
        }
    
        public boolean isSideBySide() {
            return !stretched;
        }
    
        public boolean isStretched() {
            return stretched;
        }
    
        public void setImage(BufferedImage image) {
            this.image = image;
        }
    
        public void setImage(Image image) {
            setImage(bufferize(image));
        }
    
        public void setSideBySide(boolean sideBySide) {
            stretched = !sideBySide;
        }
    
        public void setStretched(boolean stretch) {
            this.stretched = stretch;
        }
    
        @Override
        protected void paintComponent(Graphics g) {
            if (image == null) {
                super.paintComponent(g);
                return;
            }
            int w = getWidth();
            int h = getHeight();
            if (stretched) {
                g.drawImage(image, 0, 0, w, h, this);
                return;
            }
            int iw = image.getWidth();
            int ih = image.getHeight();
            int colunas = w / iw;
            int linhas = h / ih;
            if (colunas * iw < w) {
                colunas++;
            }
            if (linhas * ih < h) {
                linhas++;
            }
            int offsetX = 0;
            for (int i = 0; i < linhas; i++) {
                int y = i * ih;
                if (y > h) {
                    break;
                }
                for (int j = 0; j < colunas; j++) {
                    int x = j * iw + offsetX;
                    if (j == 0 && x > 0) {
                        g.drawImage(image, -(iw - x), y, iw, ih, this);
                    }
                    if (j == colunas - 1 && x < w) {
                        g.drawImage(image, x + iw, y, iw, ih, this);
                    }
                    if (x > w) {
                        break;
                    }
                    if (x < -iw) {
                        break;
                    }
                    g.drawImage(image, x, y, iw, ih, this);
                }
            }
        }
    }

Exemplo de uso.

Suponhamos que eu queira renderizar a seguinte imagem no fundo de uma tela Swing:

por-do-sol.jpg

por-do-sol.jpg

Tudo o que eu preciso fazer, é instanciar um ImagePanel e definir a sua propriedade image, conforme o trecho de código abaixo:

    InputStream arquivo = getClass().getResourceAsStream("/por-do-sol.jpg");
    BufferedImage imagem = ImageIO.read(arquivo);
    ImagePanel imagePanel = new ImagePanel(new GridBagLayout());
    imagePanel.setImage(imagem);

Abaixo está o fonte completo de um programa hipotético que não faz nada, mas possui uma imagem de fundo:

Classe ExemploImagePanel:

    import java.awt.BorderLayout;
    import java.awt.Component;
    import java.awt.Dimension;
    import java.awt.FlowLayout;
    import java.awt.GridBagConstraints;
    import java.awt.GridBagLayout;
    import java.awt.event.WindowAdapter;
    import java.awt.event.WindowEvent;
    import java.awt.image.BufferedImage;
    import java.io.InputStream;
    
    import javax.imageio.ImageIO;
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JLabel;
    import javax.swing.JOptionPane;
    import javax.swing.JPanel;
    import javax.swing.JPasswordField;
    import javax.swing.JTextField;
    import javax.swing.UIManager;
    
    @SuppressWarnings("serial")
    public class ExemploImagePanel extends JFrame {
    
        public static void main(String[] args) {
            try {
                UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                ExemploImagePanel telinha = new ExemploImagePanel();
                telinha.setLocationRelativeTo(null);
                telinha.setVisible(true);
            } catch (Throwable t) {
                t.printStackTrace();
            }
        }
    
        private JTextField textFieldLogin;
        private JPasswordField textFieldSenha;
    
        public ExemploImagePanel() throws Exception {
            super("Exemplo com ImagePanel");
    
            InputStream arquivo = getClass().getResourceAsStream("/por-do-sol.jpg");
            BufferedImage imagem = ImageIO.read(arquivo);
            ImagePanel imagePanel = new ImagePanel(new GridBagLayout());
            imagePanel.setImage(imagem);
    
            GridBagConstraints constraints = new GridBagConstraints();
            constraints.fill = GridBagConstraints.HORIZONTAL;
            constraints.anchor = GridBagConstraints.CENTER;
            constraints.gridx = 0;
            constraints.gridy = 0;
            imagePanel.add(criarPanelLogin(), constraints);
    
            constraints = new GridBagConstraints();
            constraints.fill = GridBagConstraints.HORIZONTAL;
            constraints.anchor = GridBagConstraints.CENTER;
            constraints.gridx = 0;
            constraints.gridy = 1;
            imagePanel.add(criarPanelSenha(), constraints);
    
            constraints = new GridBagConstraints();
            constraints.fill = GridBagConstraints.HORIZONTAL;
            constraints.anchor = GridBagConstraints.CENTER;
            constraints.gridx = 0;
            constraints.gridy = 2;
            imagePanel.add(criarPanelBotoes(), constraints);
    
            setContentPane(imagePanel);
            setMinimumSize(new Dimension(480, 320));
            setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
            addWindowListener(new WindowAdapter() {
                @Override
                public void windowClosing(WindowEvent e) {
                    sair();
                }
            });
        }
    
        private JPanel criarPanelBotoes() {
            JButton buttonLogin = new JButton("Login");
            buttonLogin.setPreferredSize(new Dimension(100, 30));
            buttonLogin.addActionListener(event -> login());
    
            JButton buttonSair = new JButton("Sair");
            buttonSair.setPreferredSize(new Dimension(100, 30));
            buttonSair.addActionListener(event -> sair());
    
            JPanel panelBotoes = new JPanel(new FlowLayout(FlowLayout.CENTER, 15, 10));
            panelBotoes.setOpaque(false);
            panelBotoes.add(buttonLogin);
            panelBotoes.add(buttonSair);
            panelBotoes.setPreferredSize(new Dimension(300, 40));
            return panelBotoes;
        }
    
        private JPanel criarPanelLogin() {
            JLabel labelLogin = new JLabel("Login:");
            labelLogin.setOpaque(false);
            textFieldLogin = new JTextField();
    
            JPanel panelLogin = new JPanel(new BorderLayout());
            panelLogin.setOpaque(false);
            panelLogin.add(labelLogin, BorderLayout.NORTH);
            panelLogin.add(textFieldLogin, BorderLayout.CENTER);
            panelLogin.setPreferredSize(new Dimension(300, 40));
            return panelLogin;
        }
    
        private JPanel criarPanelSenha() {
            JLabel labelSenha = new JLabel("Senha:");
            labelSenha.setOpaque(false);
            textFieldSenha = new JPasswordField();
    
            JPanel panelSenha = new JPanel(new BorderLayout());
            panelSenha.setOpaque(false);
            panelSenha.add(labelSenha, BorderLayout.NORTH);
            panelSenha.add(textFieldSenha, BorderLayout.CENTER);
            panelSenha.setPreferredSize(new Dimension(300, 40));
            return panelSenha;
        }
    
        private void login() {
            Component parentComponent = this;
            String user = textFieldLogin.getText();
            String pass = new String(textFieldSenha.getPassword());
    
            Object message = "Prezado(a) \"" + user + "\"\n\n" //
                    + "Você digitou a senha \"" + pass + "\"\n\n"//
                    + "Mas o login ainda não foi implementado!";
            String title = "Atenção";
            int messageType = JOptionPane.WARNING_MESSAGE;
            JOptionPane.showMessageDialog(parentComponent, message, title, messageType);
        }
    
        private void sair() {
            Component parentComponent = this;
            String message = "Deseja realmente sair?";
            String title = "Confirmação";
            int optionType = JOptionPane.YES_NO_OPTION;
            int messageType = JOptionPane.QUESTION_MESSAGE;
            int option = JOptionPane.showConfirmDialog(parentComponent, message, title, optionType, messageType);
            if (option == JOptionPane.YES_OPTION) {
                System.exit(0);
            }
        }
    }

Ao executar o programa acima, a seguinte tela será apresentada:

plano-fundo-esticado.jpg

plano-fundo-esticado.jpg

Por padrão o ImagePanel renderiza a imagem de fundo esticando ela para ocupar todo o espaço disponível (propriedade stretched), porém é possível configurá-lo para renderizar as imagens lado-a-lado, setando a propriedade sideBySide.

Suponhamos que agora eu queira a seguinte imagem como plano de fundo lado-a-lado:

camuflagem.jpg

camuflagem.jpg

Basta alterar a inicialização do ImagePanel conforme abaixo e executar novamente o ExemploImagePanel:

    InputStream arquivo = getClass().getResourceAsStream("/camuflagem.jpg");
    BufferedImage imagem = ImageIO.read(arquivo);
    ImagePanel imagePanel = new ImagePanel(new GridBagLayout());
    imagePanel.setImage(imagem);
    imagePanel.setSideBySide(true); // renderizar a imagem lado a lado

Agora o aspecto do programa em execução será este:

plano-fundo-lado-a-lado.jpg

plano-fundo-lado-a-lado.jpg

É isso galera, espero que este fonte tenha alguma utilidade.