1. Introdução muito breve ao Swing e ao AWT
Se o Java fosse um carro, então Swing e AWT seriam o seu “painel de instrumentos” e o “volante”. AWT (Abstract Window Toolkit) — a primeira biblioteca para criar aplicativos de janelas em Java. Ela usa os controles nativos do SO, portanto tem aparência diferente no Windows, Linux e Mac.
Swing — uma camada mais moderna sobre o AWT, escrita inteiramente em Java. O Swing é mais bonito, mais flexível, oferece um monte de recursos extras e tem a mesma aparência em todas as plataformas (bem, quase). Em ambos os casos, todos os elementos de interface (botões, campos de texto, checkboxes etc.) suportam trabalho com eventos.
Nesta aula usaremos o Swing: ele é mais simples para iniciantes e aparece com mais frequência em exemplos didáticos. Vamos entender como funciona o tratamento de eventos com o exemplo da tarefa mais popular — reagir ao clique de um botão.
Criando um botão
No Swing, um botão é criado assim:
JButton button = new JButton("Clique em mim!");
Aqui, “Clique em mim!” é o texto do botão.
Adicionando um ouvinte
Para reagir ao clique, é preciso registrar um ouvinte no evento:
button.addActionListener(new MyActionListener());
Mas o que é esse MyActionListener? É uma classe que implementa a interface ActionListener:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class MyActionListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("O botão foi clicado!");
}
}
Quando o usuário clica no botão, o Swing chama o método actionPerformed de todos os ouvintes registrados via addActionListener.
Exemplo completo: janela simples com botão
Vamos juntar tudo e criar um aplicativo funcional:
import javax.swing.*;
import java.awt.event.*;
public class SimpleButtonApp {
public static void main(String[] args) {
JFrame frame = new JFrame("Exemplo de evento");
JButton button = new JButton("Clique em mim!");
// Adicionamos um ouvinte ao botão
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("O botão foi clicado!");
JOptionPane.showMessageDialog(frame, "Oba! Botão clicado!");
}
});
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(button);
frame.setSize(300, 200);
frame.setVisible(true);
}
}
O que está acontecendo aqui:
- Criamos uma janela (JFrame).
- Criamos um botão (JButton).
- Adicionamos um ouvinte (classe anônima, para não criar arquivos separados).
- Dentro de actionPerformed, imprimimos uma mensagem no console e mostramos uma janela pop-up (JOptionPane).
- Adicionamos o botão na janela e exibimos a janela ao usuário.
Tente você mesmo copiar este código, executar — e clicar no botão!
2. Classes anônimas e expressões lambda
No código Java moderno, escrever classes separadas por causa de um único manipulador é como usar um tanque para ir comprar pão. É muito mais conveniente usar classes anônimas ou expressões lambda.
Classe anônima
Já vimos essa abordagem acima:
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// código de tratamento
}
});
Aqui, a classe é declarada no próprio local, sem nome.
Expressão lambda (Java 8+)
Se a interface do ouvinte for funcional (ou seja, contiver apenas um método abstrato), você pode usar uma lambda:
button.addActionListener(e -> {
System.out.println("Botão clicado via lambda!");
});
Ou até mais curto, se o corpo for de uma única linha:
button.addActionListener(e -> System.out.println("Lambda: botão clicado!"));
Isso não apenas reduz o código, como também o torna mais legível. Fato para os curiosos: a interface ActionListener é funcional porque tem apenas um método, actionPerformed.
4. Outros tipos de eventos
O mundo não se limita a botões! No Swing e no AWT, quase todo elemento de interface tem seus próprios eventos. Aqui estão os mais populares:
Eventos do mouse: MouseListener e MouseAdapter
Se você quiser reagir a cliques do mouse, use MouseListener:
button.addMouseListener(new MouseListener() {
@Override
public void mouseClicked(MouseEvent e) {
System.out.println("Clique do mouse no botão!");
}
// Os demais métodos podem ficar vazios
@Override public void mousePressed(MouseEvent e) {}
@Override public void mouseReleased(MouseEvent e) {}
@Override public void mouseEntered(MouseEvent e) {}
@Override public void mouseExited(MouseEvent e) {}
});
Escrever cinco métodos vazios por causa de um único manipulador não é muito conveniente. Para isso existe o MouseAdapter:
button.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
System.out.println("Clique do mouse no botão (via adaptador)!");
}
});
MouseAdapter implementa todos os métodos da interface, e você sobrescreve apenas os necessários.
Eventos de teclado: KeyListener/KeyAdapter
Se você precisa capturar pressionamentos de tecla:
button.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
System.out.println("Tecla pressionada: " + e.getKeyChar());
}
});
Eventos de alteração de texto: DocumentListener
Para campos de texto (JTextField, JTextArea) há ouvintes específicos:
JTextField textField = new JTextField();
textField.getDocument().addDocumentListener(new DocumentListener() {
@Override
public void insertUpdate(DocumentEvent e) {
System.out.println("Texto alterado: " + textField.getText());
}
@Override public void removeUpdate(DocumentEvent e) {}
@Override public void changedUpdate(DocumentEvent e) {}
});
Aliás, para a maioria dos eventos há adaptadores, para que você não precise escrever métodos vazios.
5. Prática: mini-exemplo com janela e botão
Vamos fazer um aplicativo GUI bem simples: uma janela com um botão que, ao ser clicado, incrementa um contador e exibe uma mensagem.
Exemplo: contador de cliques
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class ClickCounterApp {
public static void main(String[] args) {
JFrame frame = new JFrame("Contador de cliques");
JButton button = new JButton("Clique em mim!");
JLabel label = new JLabel("Cliques: 0");
// Contador de cliques (deve ser final ou efetivamente final)
final int[] count = {0};
button.addActionListener(e -> {
count[0]++;
label.setText("Cliques: " + count[0]);
});
frame.setLayout(new FlowLayout());
frame.add(button);
frame.add(label);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(250, 100);
frame.setVisible(true);
}
}
Explicação:
- Criamos a janela, o botão e o rótulo para exibir o contador.
- Usamos um array de um único elemento (final int[] count = {0};) para contornar a restrição de lambda a variáveis final/efetivamente final.
- Cada vez que o botão é clicado, incrementamos o contador e atualizamos o texto do rótulo.
Ideia para prática: tente mudar a cor do botão a cada quinto clique!
6. Como o modelo de eventos funciona por dentro
Vamos dar uma olhada rápida sob o capô. Quando você chama:
button.addActionListener(listener);
o botão (objeto JButton) adiciona seu ouvinte a uma lista interna. Quando o usuário clica no botão, acontece o seguinte:
- O botão cria um objeto de evento (ActionEvent).
- O botão percorre a lista de ouvintes e chama em cada um o método actionPerformed.
- Seu manipulador executa as ações necessárias (por exemplo, atualiza o rótulo).
O principal a lembrar: cada evento pode ter qualquer quantidade de ouvintes — você pode adicionar vários manipuladores para um único botão; eles serão chamados em sequência.
7. Esquema visual: processamento de evento
graph TD
A[Usuário clicou no botão] --> B{JButton}
B --> C[Criar ActionEvent]
C --> D[Chamar actionPerformed em todos os ouvintes]
D --> E[O manipulador executa as ações]
7. Erros comuns ao trabalhar com eventos no Swing e no AWT
Erro nº 1: esqueceu de registrar o ouvinte. O botão não reage a cliques porque você esqueceu de chamar addActionListener.
Erro nº 2: trabalho pesado no manipulador. Se no manipulador você iniciar um laço demorado ou baixar algo da internet, a interface “congelará”. Para tarefas longas, use threads separados ou SwingWorker.
Erro nº 3: tentar alterar uma variável a partir da lambda sem final/efetivamente final. Dentro de uma lambda, você só pode usar variáveis final ou “efetivamente final”. Para contadores, use arrays ou classes específicas (AtomicInteger, se quiser algo mais profissional).
Erro nº 4: esqueceu de remover o ouvinte. Se você remove um componente, mas não remove o ouvinte, pode ocorrer vazamento de memória. Cancele as inscrições quando o componente não for mais necessário.
Erro nº 5: nem todos os métodos da interface foram implementados. Se você implementar, por exemplo, MouseListener diretamente, não se esqueça de todos os métodos! É melhor usar adaptadores (MouseAdapter, KeyAdapter).
GO TO FULL VERSION