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
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:
ExemploImagePanel executando no Windows 10
Plano de fundo "por-do-sol.jpg" esticado
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
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:
ExemploImagePanel executando no Windows 10
Plano de fundo "camuflagem.jpg" lado-a-lado
É isso galera, espero que este fonte tenha alguma utilidade pra vocês.