Guia Completo de Programação Gráfica com Java: Containers, Layouts, Componentes e Desenho

Este documento é um guia técnico abrangente sobre a construção de interfaces gráficas com o Java AWT (Abstract Window Toolkit). A cobertura inclui gerenciamento de containers, diversos gerenciadores de layout, componentes comuns, tratamento de eventos, desenho de gráficos e manipulação de imagens.

  1. Containers no AWT

1.1 Hierarquia de Herança

  • Janela (Window): Um container de nível superior que pode existir independentemente. Utiliza o BorderLayout como gerenciador de layout padrão.
  • Painel (Panel): Um container genérico que não pode existir de forma isolada. Deve ser inserido dentro de outro container. Seu layout padrão é o FlowLayout.
  • Painel de Rolagem (ScrollPane): Um container com barras de rolagem integradas, também dependetne. O BorderLayout é seu layout padrão.

1.2 API Comum para Componentes

A classe base Component fornece métodos para controlar a aparência e o posicionamento dos elementos da interface.

Método Descrição
setLocation(int x, int y) Define a posição do componente.
setSize(int width, int height) Define as dimensões do componente.
setBounds(int x, int y, int width, int height) Define posição e tamanho simultaneamente.
setVisible(boolean b) Controla a visibilidade do componente.

A classe Container adiciona métodos para gerenciar seus componentes filhos.

Método Descrição
Component add(Component comp) Adiciona um componente ao container.
Component getComponentAt(int x, int y) Retorna o componente na posição especificada.
int getComponentCount() Retorna o número total de componentes.
Component[] getComponents() Retorna um array com todos os componentes.

1.3 Exemplos de Uso de Containers

Exemplo de Janela (Frame)


import java.awt.*;

public class JanelaBasica {
    public static void main(String[] args) {
        Frame minhaJanela = new Frame("Minha Primeira Janela");
        minhaJanela.setBounds(200, 150, 600, 400);
        minhaJanela.setVisible(true);
    }
}

Exemplo de Painel (Panel)


import java.awt.*;

public class PainelSimples {
    public static void main(String[] args) {
        Frame janelaPrincipal = new Frame("Uso de Painel");
        Panel painelDeConteudo = new Panel();
        
        painelDeConteudo.add(new Label("Etiqueta de teste"));
        painelDeConteudo.add(new Button("Botão de teste"));
        
        janelaPrincipal.add(painelDeConteudo);
        janelaPrincipal.setBounds(200, 150, 600, 400);
        janelaPrincipal.setVisible(true);
    }
}

Observação sobre Codificação: Em alguns sistemas, caracteres acentuados podem aparecer incorretamente no console. Isso geralmente pode ser resolvido adicionando o parâmetro de JVM -Dfile.encoding=GBK ou -Dfile.encoding=UTF-8 conforme o sistema operacional.

Exemplo de Painel com Rolagem (ScrollPane)


import java.awt.*;

public class PainelComRolagem {
    public static void main(String[] args) {
        Frame janela = new Frame("Testando ScrollPane");
        ScrollPane painelRolagem = new ScrollPane(ScrollPane.SCROLLBARS_ALWAYS);
        
        painelRolagem.add(new Label("Texto dentro do painel de rolagem"));
        painelRolagem.add(new Button("Outro componente"));
        
        janela.add(painelRolagem);
        janela.setBounds(200, 150, 600, 400);
        janela.setVisible(true);
    }
}

Nota Importante: O ScrollPane utiliza o BorderLayout. Portanto, se múltiplos componentes forem adicionados diretamente a ele, apenas o último será visível na região central. Para exibir vários componentes, é necessário agrupá-los em um Panel interno.

  1. Gerenciadores de Layout (LayoutManager)

Definir manualmente as posições e tamanhos dos componentes torna a aplicação pouco portável, pois as dimensões ideais variam entre sistemas operacionais. Os gerenciadores de layout automatizam este processo, garantindo que a interface se adapte corretamente ao ambiente.

2.1 FlowLayout

Organiza os componentes em uma sequência, como um fluxo. Quando atinge a borda do container, os componentes são movidos para a próxima linha. A orientação padrão é da esquerda para a direita.


import java.awt.*;

public class LayoutFluxo {
    public static void main(String[] args) {
        Frame janela = new Frame("Layout FlowLayout");
        janela.setLayout(new FlowLayout(FlowLayout.CENTER, 15, 15));
        
        for (int contador = 0; contador < 15; contador++) {
            janela.add(new Button("Item " + (contador + 1)));
        }
        
        janela.pack();
        janela.setVisible(true);
    }
}

Dica: O método pack() ajusta o tamanho da janela para acomodar perfeitamente seus componentes filhos.

2.2 BorderLayout

Divide o container em cinco regiões fixas: Norte, Sul, Leste, Oeste e Centro. Se um componente não for designado a uma região, ele será colocado no Centro por padrão. A região Central se expande para preencher o espaço disponível.


import java.awt.*;

public class LayoutBorda {
    public static void main(String[] args) {
        Frame janela = new Frame("Layout BorderLayout");
        janela.setLayout(new BorderLayout(10, 10));
        
        janela.add(new Button("Norte"), BorderLayout.NORTH);
        janela.add(new Button("Sul"), BorderLayout.SOUTH);
        janela.add(new Button("Leste"), BorderLayout.EAST);
        janela.add(new Button("Oeste"), BorderLayout.WEST);
        janela.add(new Button("Centro"), BorderLayout.CENTER);
        
        janela.pack();
        janela.setVisible(true);
    }
}

2.3 GridLayout

Cria uma grade de células de tamanho igual. Os componentes são adicionados da esquerda para a direita e de cima para baixo, expandindo-se para preencher completamente sua célula.


import java.awt.*;

public class LayoutGrade {
    public static void main(String[] args) {
        Frame janela = new Frame("Calculadora com GridLayout");
        
        Panel painelSuperior = new Panel();
        painelSuperior.add(new TextField(30));
        janela.add(painelSuperior, BorderLayout.NORTH);
        
        Panel painelBotoes = new Panel(new GridLayout(4, 4, 5, 5));
        String[] rotulos = {"7","8","9","/","4","5","6","*","1","2","3","-","C","0",".","+"};
        for (String rotulo : rotulos) {
            painelBotoes.add(new Button(rotulo));
        }
        
        janela.add(painelBotoes, BorderLayout.CENTER);
        janela.pack();
        janela.setVisible(true);
    }
}

2.4 GridBagLayout

O gerenciador de layout mais flexível e complexo. Permite que componentes ocupem múltiplas células da grade e controle preciso do redimensionamento. Requer o uso da classe auxiliar GridBagConstraints para definir as restrições de cada componente.


import java.awt.*;

public class LayoutGradeFlexivel {
    public static void main(String[] args) {
        Frame janela = new Frame("GridBagLayout Demo");
        GridBagLayout layoutGrade = new GridBagLayout();
        janela.setLayout(layoutGrade);
        
        GridBagConstraints restricoes = new GridBagConstraints();
        restricoes.fill = GridBagConstraints.BOTH;
        restricoes.weightx = 1.0;
        
        Button botao1 = new Button("Botão 1");
        Button botao2 = new Button("Botão 2");
        Button botao3 = new Button("Botão 3");
        Button botao4 = new Button("Botão 4 (Ocupa 2 colunas)");
        
        adicionarComponente(janela, botao1, layoutGrade, restricoes);
        adicionarComponente(janela, botao2, layoutGrade, restricoes);
        
        restricoes.gridwidth = GridBagConstraints.REMAINDER; // Termina a linha
        adicionarComponente(janela, botao3, layoutGrade, restricoes);
        
        restricoes.gridwidth = 2; // Ocupa 2 colunas
        restricoes.weighty = 1.0;
        adicionarComponente(janela, botao4, layoutGrade, restricoes);
        
        janela.pack();
        janela.setVisible(true);
    }
    
    private static void adicionarComponente(Container container, Component comp,
                                            GridBagLayout layout, GridBagConstraints gbc) {
        layout.setConstraints(comp, gbc);
        container.add(comp);
    }
}

2.5 CardLayout

Gerencia uma pilha de componentes, onde apenas um é visível por vez. Permite a navegação entre os "cards".


import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class LayoutCartas {
    public static void main(String[] args) {
        Frame janela = new Frame("CardLayout Demo");
        CardLayout gerenciadorCartas = new CardLayout();
        Panel painelCartas = new Panel(gerenciadorCartas);
        
        String[] nomes = {"Painel A", "Painel B", "Painel C"};
        Color[] cores = {Color.RED, Color.GREEN, Color.BLUE};
        
        for (int i = 0; i < 3; i++) {
            Panel card = new Panel();
            card.setBackground(cores[i]);
            card.add(new Label(nomes[i]));
            painelCartas.add(card, nomes[i]);
        }
        
        Panel painelControle = new Panel();
        ActionListener acao = e -> {
            String cmd = e.getActionCommand();
            if ("Proximo".equals(cmd)) {
                gerenciadorCartas.next(painelCartas);
            } else if ("Anterior".equals(cmd)) {
                gerenciadorCartas.previous(painelCartas);
            }
        };
        
        Button btnAnt = new Button("Anterior");
        Button btnProx = new Button("Proximo");
        btnAnt.addActionListener(acao);
        btnProx.addActionListener(acao);
        painelControle.add(btnAnt);
        painelControle.add(btnProx);
        
        janela.add(painelCartas, BorderLayout.CENTER);
        janela.add(painelControle, BorderLayout.SOUTH);
        janela.setSize(400, 300);
        janela.setVisible(true);
    }
}

2.6 BoxLayout

Organiza componentes em uma única linha (horizontal) ou coluna (vertical). É útil para criar interfaces lineares simples. Pode-se usar Box.createHorizontalBox() ou Box.createVerticalBox() para criar contêineres com este layout pré-configurado.


import java.awt.*;
import javax.swing.*;

public class LayoutCaixa {
    public static void main(String[] args) {
        Frame janela = new Frame("BoxLayout com Espaçamento");
        Box caixaVertical = Box.createVerticalBox();
        
        caixaVertical.add(new Button("Botão Superior"));
        caixaVertical.add(Box.createVerticalStrut(20)); // Espaço fixo
        caixaVertical.add(new Button("Botão do Meio"));
        caixaVertical.add(Box.createVerticalGlue()); // Espaço elástico
        caixaVertical.add(new Button("Botão Inferior"));
        
        janela.add(caixaVertical);
        janela.setSize(300, 300);
        janela.setVisible(true);
    }
}
  1. Componentes Comuns do AWT

Além dos containers, o AWT fornece uma variedade de componentes para entrada e saída de dados.

  • Label: Exibe texto estático.
  • Button: Botão clicável.
  • TextField: Campo de entrada de texto de linha única.
  • TextArea: Campo de entrada de texto multi-linha.
  • Checkbox: Caixa de seleção (pode ser usada como rádio com CheckboxGroup).
  • Choice: Lista suspensa (dropdown).
  • List: Lista de itens com seleção simples ou múltipla.
  • Canvas: Área bruta para desenho personaliazdo.
  • Scrollbar: Barra de rolagem para valores numéricos.
  1. Tratamento de Eventos

A interação do usuário é tratada por um modelo de eventos. Os conceitos chave são:

  • Fonte do Evento: O componente que gerou o evento (ex: um botão).
  • Evento: Um objeto que encapsula a informação sobre o que aconteceu (ex: um clique).
  • Ouvinte (Listener): Uma interface que define métodos para tratar um tipo específico de evento.
  • Registro: O processo de vincular um ouvinte a uma fonte de evento.

Exemplo Básico de Evento


import java.awt.*;
import java.awt.event.*;

public class TratadorDeEventos {
    public static void main(String[] args) {
        Frame janela = new Frame("Eventos");
        Button botaoAcao = new Button("Clique Aqui");
        Label mensagem = new Label("Aguardando ação...");
        
        botaoAcao.addActionListener(e -> {
            mensagem.setText("Botão foi clicado!");
        });
        
        janela.add(mensagem, BorderLayout.NORTH);
        janela.add(botaoAcao, BorderLayout.CENTER);
        janela.pack();
        janela.setVisible(true);
    }
}

Evento de Janela

Para fechar a janela ao clicar no "X", é necessário tratar o evento de janela.


import java.awt.*;
import java.awt.event.*;

public class FecharJanela {
    public static void main(String[] args) {
        Frame janela = new Frame("Fechar Janela");
        janela.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                System.exit(0);
            }
        });
        janela.setSize(300, 200);
        janela.setVisible(true);
    }
}
  1. Componentes de Menu

Menus são construídos com MenuBar, Menu e MenuItem.


import java.awt.*;

public class MenuSimples {
    public static void main(String[] args) {
        Frame janela = new Frame("Exemplo de Menu");
        
        MenuBar barraDeMenu = new MenuBar();
        Menu menuArquivo = new Menu("Arquivo");
        MenuItem itemNovo = new MenuItem("Novo");
        MenuItem itemSair = new MenuItem("Sair");
        
        itemSair.addActionListener(e -> System.exit(0));
        
        menuArquivo.add(itemNovo);
        menuArquivo.addSeparator(); // Linha divisória
        menuArquivo.add(itemSair);
        barraDeMenu.add(menuArquivo);
        
        janela.setMenuBar(barraDeMenu);
        janela.setSize(400, 300);
        janela.setVisible(true);
    }
}
  1. Desenho com Graphics

O desenho personalizado é realizado sobrescrevendo o método paint(Graphics g) de um componente (geralmente um Canvas ou Panel). A classe Graphics oferece métodos como drawRect, fillOval, drawLine, drawString e setColor.

Exemplo: Desenhando Formas


import java.awt.*;

public class AreaDeDesenho extends Canvas {
    @Override
    public void paint(Graphics pincel) {
        // Configura a cor e desenha
        pincel.setColor(Color.BLUE);
        pincel.fillRect(50, 50, 100, 80);
        
        pincel.setColor(Color.RED);
        pincel.fillOval(200, 50, 80, 80);
        
        pincel.setColor(Color.BLACK);
        pincel.drawString("Exemplo de Desenho AWT", 100, 200);
    }
    
    public static void main(String[] args) {
        Frame janela = new Frame("Desenho");
        AreaDeDesenho canvas = new AreaDeDesenho();
        canvas.setPreferredSize(new Dimension(400, 300));
        
        janela.add(canvas);
        janela.pack();
        janela.setVisible(true);
    }
}

BufferedImage e Pintura Suave

Para evitar cintilação (flickering) durante a repintura, pode-se desenhar primeiro em uma imagem buffer (BufferedImage) e depois transferir a imagem completa para o componente.


import java.awt.*;
import java.awt.image.BufferedImage;

public class DesenhoBufferizado extends Canvas {
    private BufferedImage imagemBuffer;
    
    public DesenhoBufferizado(int largura, int altura) {
        imagemBuffer = new BufferedImage(largura, altura, BufferedImage.TYPE_INT_ARGB);
    }
    
    public void redesenhar() {
        Graphics2D g2d = imagemBuffer.createGraphics();
        // Limpa a imagem buffer
        g2d.setColor(Color.WHITE);
        g2d.fillRect(0, 0, imagemBuffer.getWidth(), imagemBuffer.getHeight());
        // Desenha novos elementos na imagem buffer
        g2d.setColor(Color.GREEN);
        g2d.fillOval(50, 50, 100, 100);
        g2d.dispose();
        repaint(); // Solicita que o componente seja repintado
    }
    
    @Override
    public void paint(Graphics g) {
        // Apenas desenha a imagem buffer no componente
        g.drawImage(imagemBuffer, 0, 0, null);
    }
    
    public static void main(String[] args) {
        Frame janela = new Frame("Desenho Bufferizado");
        DesenhoBufferizado canvas = new DesenhoBufferizado(400, 300);
        canvas.setPreferredSize(new Dimension(400, 300));
        janela.add(canvas);
        janela.pack();
        janela.setVisible(true);
        
        // Exemplo: redesenhar a cada 100ms (simulação de animação)
        // javax.swing.Timer timer = new javax.swing.Timer(100, e -> canvas.redesenhar());
        // timer.start();
    }
}

Tags: java AWT swing LayoutManager BorderLayout

Publicado em 6-25 01:49