Modelo de Eventos
•
Em uma GUI, quando o usuário realiza uma ação qualquer, tal como clicar
sobre um botão ou preencher uma caixa de texto, um
evento
é disparado
pelo componente com o qual o usuário interagiu.
•
Eventos são
objetos
que descrevem o que aconteceu na interação entre o
usuário e o componente. Os tipos de evento estão definidos nos pacotes
java.awt.event
e
javax.swing.event
.
•
Cada componente a partir do qual um evento se originou, deve propagar o
evento para uma ou mais classes chamadas ouvidoras (
listeners
). As
classes ouvidoras contêm os tratadores de eventos que recebem e
processam o evento.
•
Os
objetos tratadores de eventos
devem ser registrados aos componentes
da interface gráfica que desejam ouvir (e tratar). Quando ocorre um evento
em um componente, somente os objetos registrados a este componente
recebem a informação de que o evento ocorreu podendo, portanto, tratá-lo
adequadamente.
•
Quando um evento ocorre, o componente GUI com o qual o usuário
interagiu notifica todos os seus ouvidores registrados, chamando o método
de tratamento de evento apropriado de cada ouvidor.
•
A maioria dos componentes de GUI pode disparar mais de um tipo de
evento. Com as classes ouvidoras de eventos pode-se distribuir o
processamento desses eventos, ou seja, podem existir diversas classes para
processar os eventos.
•
Para processar um evento ocorrido em uma GUI deve-se:
•
Registrar um ouvidor (
listener
) de eventos. Listeners são classes que
implementam a interface
EventListener
. Existem subclasses desta classe
para cada tipo de componente.
•
Implementar um tratador (
handler
) de eventos.
•
Associado a cada evento existe uma
interface ouvidora
apropriada. Esta
interface estabelece quais métodos devem ser definidos na classe tratadora
do evento (a classe que
implementa os métodos
apropriados e que
encontra-se registrada como tratadora do evento).
•
Exemplo
: classes que implementam a interface
ActionListener
devem
implementar o método
actionPerformed(ActionEvent)
e podem ser
registradas para tratar
ações
(um clique sobre um botão, por exemplo) que
ocorrem na GUI.
Interfaces para Tratamento de Eventos
•
Cada categoria de evento exige uma interface apropriada que deve ser
implementada pela classe tratadora do evento.
Hierarquia de Interfaces
•
A implementação dessas interfaces implica em implementar seus métodos
abstratos. Todos os métodos recebem como parâmetro um objeto de uma
subclasse de
java.awt.event
. Por exemplo: o método
actionPerformed
da
interface
ActionListener
recebe como parâmetro um objeto da classe
ActionEvent
.
java.util.EventListener
KeyListener
MouseListener
MouseMotionListener
TextListener
WindowListener
ActionListener
ComponentListener
AdjustmentListener
ContainerListener
FocusListener
ItemListener
Hierarquia de Eventos
java.lang.Object
java.util.EventObject
java.awt.AWTEvent
java.awt.event.AdjustmentEvent
java.awt.event.ItemEvent
java.awt.event.TextEvent
java.awt.event.ComponentEvent
java.awt.event.KeyEvent
java.awt.event.MouseEvent
java.awt.event.InputEvent
java.awt.event.InputEvent
java.awt.event.KeyEvent
java.awt.event.MouseEvent
java.awt.event.ActionEvent
Alguns Métodos Tratadores de Eventos
Evento
Interface
Métodos
Ação
ActionListener
actionPerformed(ActionEvent)
Item
ItemListener
itemStateChanged(ItemEvent)
Lista
ListSelectionListener
valueChanged(ListSelectionEvent)
Mouse
MouseListener
mousePressed(MouseEvent)
mouseReleased(MouseEvent)
mouseEntered(MouseEvent)
mouseExited(MouseEvent)
mouseClicked(MouseEvent)
Movimento
do Mouse
MouseMotionListener
mouseDragged(MouseEvent)
mouseMoved(MouseEvent)
Tecla
KeyListener
keyPressed(KeyEvent)
keyReleased(KeyEvent)
keyTyped(KeyEvent)
Foco
FocusListener
focusGained(FocusEvent)
focusLost(FocusEvent)
Evento
Interface
Métodos
Ajuste
AdjustmentListener
adjustmentValueChanged(AdjustmentEvent)
Componente
ComponentListener
componentMoved(ComponentEvent)
componentHiden(ComponentEvent)
componentResized(ComponentEvent)
componentShown(ComponentEvent)
Janela
WindowListener
windowClosing(WindowEvent)
windowOpened(WindowEvent)
windowIconnified(WindowEvent)
windowDeiconified(WindowEvent)
windowClosed(WindowEvent)
windowActivated(WindowEvent)
windowDeactivated(WindowEvent)
Container
ContainerListener
componentAdded(ContainerEvent)
componentRemoved(ContainerEvent)
Texto
TextListener
textValueChanged(TextEvent)
Exemplo 1: Botão com tratador de eventos
import java.awt.*; import javax.swing.*; import java.awt.event.*;
public class Exemplo1 extends JFrame {
public Exemplo1() {
JFrame frame = new JFrame("Botão com tratador de eventos"); frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); frame.setSize(320,120);
JPanel panel = new JPanel();
JButton botao = new JButton("OK"); panel.add(botao);
Container c = frame.getContentPane(); c.add(panel);
// Registrar o tratador de eventos
botao.addActionListener(new TrataEvento()); frame.setVisible(true);
}
}
Não esquecer do método main().
TrataEvento é a classe tratadora de eventos. Esta classe deve implementar o método
actionPerformed(ActionEvent e). Note que o
tratador de evento registrado para esse botão é um
objeto desta classe tratadora de evento.
Classe Tratadora de Eventos
Reparar que:
•
A classe tratadora de eventos deve implementar a interface
ActionListener
, ou
seja, implementar o método
actionPerformed(ActionEvent e)
. O parâmetro
desse método possui diversos métodos que ajudam a identificar qual foi o
objeto que gerou o evento.
•
O método
getActionCommand()
para um botão retorna a legenda (
caption
) do
botão. Mas, e se existissem dois botões com a mesma legenda na interface
gráfica?
import java.awt.event.*;
public class TrataEvento implements ActionListener {
public void actionPerformed(ActionEvent e) {
System.out.println("Voce clicou no botao " + e.getActionCommand()); }
}
•
A própria classe que constrói a interface gráfica pode ser a classe tratadora de
eventos: basta que esta classe
implemente
a interface
ActionListener
.
import java.awt.*; import javax.swing.*; import java.awt.event.*;
public class Exemplo1a extends JFrame implements ActionListener
{
public Exemplo1a() {
JFrame frame = new JFrame("Botão com tratador de eventos"); frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); frame.setSize(320,120);
JPanel panel = new JPanel();
JButton botao = new JButton("OK"); panel.add(botao);
Container c = frame.getContentPane(); c.add(panel);
// Registrar o tratador de eventos botao.addActionListener(this); frame.setVisible(true);
} Note que, desta forma, o tratador de evento registrado
para esse botão é um objeto desta própria classe (this).
•
Note que a classe
Exemplo1a
, além de construir a interface gráfica,
implementa a interface
ActionListener
, ou seja, é também uma classe
tratadora de eventos de ação. A implementação desta interface consiste em
implementar o método
actionPerformed()
.
public void actionPerformed(ActionEvent e) {
System.out.println("Voce clicou no botao " + e.getActionCommand()); }
public static void main(String[] args) {
Exemplo1a e = new Exemplo1a();
e.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) { System.exit(0); } } ); } }
Exemplo 2: Vários botões com mesma legenda
import java.awt.*; import javax.swing.*; import java.awt.event.*;
public class Exemplo2 extends JFrame { public Exemplo2() {
JFrame frame = new JFrame("Botões com mesma legenda"); frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); frame.setSize(320,120);
JPanel panel = new JPanel(); JButton b1 = new JButton("OK"); b1.setName("Botão1");
JButton b2 = new JButton("OK"); b2.setName("Botão2");
panel.add(b1); panel.add(b2);
Container c = frame.getContentPane(); c.add(panel);
// Registrar o tratador de eventos
b1.addActionListener(new TrataEvento2()); b2.addActionListener(new TrataEvento2()); frame.setVisible(true);
}
} Precisa ainda lembrar de incluir o método main()?
Exemplo2.java
Notar que os dois botões tem o mesmo caption, mas os nomes
dos objetos são diferentes. Assim
será possível dar um tratamento diferente para cada botão.
Nova Classe Tratadora de Eventos
•
Portanto, não é preciso construir uma classe tratadora de eventos para cada
componente da GUI. Uma única classe pode tratar os eventos originados por
import javax.swing.*; import java.awt.event.*;
public class TrataEvento2 implements ActionListener {
public void actionPerformed(ActionEvent e) {
JButton b = (JButton)e.getSource(); String nome = b.getName();
if (nome.equals("Botão1")) {
System.out.println("Voce clicou no Botão 1"); }
if (nome.equals("Botão2")) {
System.out.println("Voce clicou no Botão 2"); }
} }
TrataEvento2.java
O método getSource() retorna o objeto que deu origem ao evento (no caso, um botão).
Exemplo 3: Tratamento de eventos na própria classe da aplicação
import java.awt.*; import javax.swing.*; import java.awt.event.*;
public class Exemplo3 extends JFrame implements ActionListener {
public Exemplo3() {
JFrame frame = new JFrame("Tratamento na própria classe"); frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); frame.setSize(320,120);
JPanel panel = new JPanel(); JButton b1 = new JButton("OK"); b1.setName("Botão1");
JButton b2 = new JButton("OK"); b2.setName("Botão2"); panel.add(b1); panel.add(b2); Container c = frame.getContentPane(); c.add(panel); b1.addActionListener(this); b2.addActionListener(this); frame.setVisible(true); } Exemplo3.java
Notar que o objeto tratador de eventos é um objeto da própria classe (this).
Para ser uma classe tratadora de eventos, deve implementar a interface ActionListener.
•
A classe, portanto, deve implementar o método
actionPerformed
...
public void actionPerformed(ActionEvent e) {
JButton b = (JButton)e.getSource(); String nome = b.getName();
if (nome.equals("Botão1")) {
System.out.println("Voce clicou no Botao 1"); }
if (nome.equals("Botão2")) {
System.out.println("Voce clicou no Botão 2"); }
}
public static void main(String[] args) {
Exemplo3 e = new Exemplo3();
e.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent we) {
System.exit(0); }});
Exemplo 4: Múltiplos tratadores de eventos
•
A estrutura de eventos de Java permite que diversos ouvidores sejam
definidos para um único componente. Se um evento for gerado pelo
componente todos os tratadores registrados serão chamados.
import java.awt.*; import javax.swing.*; import java.awt.event.*;
public class Exemplo4 extends JFrame
implements MouseListener, MouseMotionListener {
public Exemplo4() {
JFrame frame = new JFrame("Diversos tratadores de eventos"); frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); frame.setSize(320,120);
JLabel label = new JLabel("Este frame tem dois ouvidores"); Container c = frame.getContentPane();
c.add(label);
// Registrar os tratadores para o frame frame.addMouseListener(this);
frame.addMouseMotionListener(this); frame.setVisible(true);
}
Exemplo4.java
A classe implementa duas interfaces. Todos os métodos dessas 2 interfaces devem ser implementados.
Note que existem dois tratadores registrados para o objeto frame.
// Métodos da interface MouseListener
public void mousePressed(MouseEvent e) {} public void mouseReleased(MouseEvent e) {} public void mouseEntered(MouseEvent e) {
System.out.println("O mouse está na janela!"); }
public void mouseExited(MouseEvent e) {
System.out.println("O mouse saiu da janela!"); }
public void mouseClicked(MouseEvent e) {
System.out.println("O mouse foi clicado!"); }
// Métodos da interface MouseMotionListener
public void mouseDragged(MouseEvent e) {
System.out.println("Movendo: X=" + e.getX() + " Y=" + e.getY()); }
public void mouseMoved(MouseEvent e) {} public static void main(String[] args) { Exemplo4 janela = new Exemplo4();
janela.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0);
}}); }
Note que todos os métodos das interfaces precisam ser
implementados, mesmo
Exemplo 5: Alguns componentes Swing e seus eventos
import java.awt.*; import javax.swing.*; import java.awt.event.*;
public class Exemplo5 extends JFrame {
public Exemplo5() {
JFrame frame = new JFrame("Componentes Swing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); Container c = frame.getContentPane(); c.setLayout(new GridLayout(3,2)); c.add(montarPainelLabels()); c.add(montarPainelCaixas()); c.add(montarPainelAreas()); c.add(montarPainelBotoes()); c.add(montarPainelListaComboBox()); c.add(montarPainelCheckRadio()); frame.setSize(800,400); frame.setVisible(true); } Exemplo5.java Os métodos montarPainelXxxx retornam um painel, que será incluído na área de conteúdo do frame.
private JPanel montarPainelLabels() {
JPanel p = new JPanel();
p.setBackground(Color.white);
JLabel lab1 =
new JLabel("Label contendo apenas texto (posicione o mouse)"); lab1.setToolTipText("Sou um JLabel");
UIManager.put("ToolTip.background", new Color(255,255,213)); Icon micro = new ImageIcon("micro.gif");
JLabel lab2 =
new JLabel("Aqui, o label contém texto azul com um ícone à esquerda", micro, SwingConstants.LEFT);
lab2.setForeground(Color.blue); JLabel lab3 = new JLabel();
lab3.setText("Ícone com texto embaixo"); lab3.setIcon(micro); lab3.setHorizontalTextPosition(SwingConstants.CENTER); lab3.setVerticalTextPosition(SwingConstants.BOTTOM); p.add(lab1); p.add(lab2); p.add(lab3);
O método setToolTipText especifica a
dica exibida quando o usuário posiciona
o mouse sobre o componente GUI. A classe UIManager permite alterar a cor de fundo da dica, de azul (o padrão) para uma cor qualquer.
A classe ImageIcon aceita dois formatos de imagem: GIF (Graphics Interchange Format) e JPEG ou JPG (Joint Photographic Experts Group)
private JPanel montarPainelCaixas() {
JPanel p = new JPanel();
JTextField tf1 = new JTextField(10);
JTextField tf2 = new JTextField("Texto não editável", 30); tf2.setEditable(false);
JPasswordField pf = new JPasswordField("Uma senha"); p.add(tf1);
p.add(tf2); p.add(pf); return p; }
Mesmo um campo não editável pode ter um tratador de evento.
private JPanel montarPainelAreas() {
JPanel p = new JPanel();
String s = "Selecione algum texto daqui " +
"e clique no botão Copia.\nO texto selecionado " + "será copiado na área da direita.";
JTextArea areaEsq = new JTextArea(s,5,10); JTextArea areaDir = new JTextArea(5,10); areaEsq.setWrapStyleWord(true);
areaDir.setEditable(false);
JButton copia = new JButton("Copia >"); p.add(new JScrollPane(areaEsq));
p.add(copia);
p.add(new JScrollPane(areaDir)); return p;
}
A classe JTextArea não fornece barras de
rolagem, que precisam portanto ser anexadas. A classe JScrollPane fornece tanto rolagem vertical quanto horizontal. Note que para incluir uma barra de rolagem a um componente, este componente deve ser passado como parâmetro do construtor de
ScrollPane.
Constantes da classe JScrollPane:
VERTICAL_SCROLLBAR_ALWAYS, HORIZONTAL_SCROLLBAR_ALWAYS,
VERTICAL_SCROLLBAR_AS_NEEDED, HORIZONTAL_SCROLLBAR_AS_NEEDED,
Para fornecer a funcionalidade de mudança automática de linha para um JTextArea, invoque o método
private JPanel montarPainelBotoes() {
JPanel p = new JPanel();
p.setBackground(Color.white);
JButton b1 = new JButton("Um botão bem simples");
Icon img1 = new ImageIcon("micro.gif"); Icon img2 = new ImageIcon("borrao.gif");
JButton b2 = new JButton("Botão com imagem (passe o mouse)",img1); b2.setRolloverIcon(img2);
p.add(b1); p.add(b2); return p; }
Um botão pode incluir uma imagem e uma imagem rollover (exibida quando o mouse é posicionado sobre o botão).
private JPanel montarPainelListaComboBox() {
JPanel p = new JPanel();
p.setBackground(Color.white);
String itens[] = {"Amarelo","Azul","Branco","Cinza"};
Color cores[] = {Color.yellow,Color.blue,Color.white,Color.lightGray}; JList lista = new JList(itens);
lista.setVisibleRowCount(3);
lista.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); String nomes[] = {"borrao.gif","cadeado.gif","engrenagem.gif",
"escreve.gif","micro.gif","pasta.gif","rede.gif","servidor.gif"}; ImageIcon imagens[] = new ImageIcon[nomes.length];
for (int i = 0; i < nomes.length; i++) {
imagens[i] = new ImageIcon(nomes[i]); }
JComboBox combo = new JComboBox(nomes); combo.setMaximumRowCount(3);
JLabel mostra = new JLabel(imagens[0]); p.add(new JScrollPane(lista));
p.add(combo); p.add(mostra); return p;
Em uma lista, pode-se selecionar
apenas um item (SINGLE_SELECTION) ou, pressionando Ctrl, vários itens (MULTIPLE_INTERVAL_SELECTION).
O método setMaximumRowCount estabelece o número máximo de itens exibidos pelo ComboBox. Note que, para o caso acima, como o ComboBox acima possui 8 itens, uma barra de rolagem será fornecida automaticamente pelo objeto ComboBox. Note que isso não ocorre automaticamente no caso de listas.
private JPanel montarPainelCheckRadio() {
JPanel p = new JPanel();
JCheckBox cb1 = new JCheckBox("Negrito",false); JCheckBox cb2 = new JCheckBox("Itálico",false); JRadioButton rb1 = new JRadioButton("Nada",false); JRadioButton rb2 = new JRadioButton("Negrito",false); JRadioButton rb3 = new JRadioButton("Itálico",false); JRadioButton rb4 = new JRadioButton("Ambos",false); ButtonGroup bg = new ButtonGroup();
bg.add(rb1); bg.add(rb2); bg.add(rb3); bg.add(rb4);
JPanel grupo = new JPanel(); grupo.add(rb1); grupo.add(rb2); grupo.add(rb3); grupo.add(rb4); p.add(cb1); p.add(cb2); p.add(grupo); return p; }
Note que os botões de rádio precisam ser agrupados para se tornarem
mutuamente exclusivos. Um objeto
ButtonGroup não é um componente
GUI e portanto, não pode ser
acrescentado a um container.
Caixas de seleção (checkbox) e botões de rádio (radiobutton) são semelhantes no sentido de que possuem apenas dois estados: selecionado (SELECTED) ou
public static void main(String[] args) { Exemplo5 janela = new Exemplo5();
janela.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e)
{
System.exit(0); }});
} }
•
Vamos implementar tratamento para os seguintes eventos:
•
O clique no botão com imagem deve exibir os conteúdos das caixas (de
texto e de senha);
•
O clique no botão Copia deve copiar o texto selecionado na área da
esquerda para a área da direita;
•
A seleção de um item da lista deve alterar a cor de fundo do painel;
•
A seleção de um item do ComboBox deve exibir a imagem
correspondente a este item;
•
A escolha de um dos botões de rádio deve deixar as caixas de
verificação em um estado consistente.
•
Vamos imaginar que a própria classe Exemplo5 irá tratar os eventos. Para
isso, a classe deverá implementar as seguintes interfaces:
•
ActionListener: método actionPerformed
•
ItemListener: método itemStateChanged
•
ListSelectionListener: método valueChanged
•
Exercício
: Incluir na classe Exemplo5 os
ouvidores
de eventos necessários e
identificar todos os objetos que deverão ser declarados como
atributos
da
classe.
// Tratadores de eventos
public void actionPerformed(ActionEvent e) {
if (e.getSource() == b2) {
JOptionPane.showMessageDialog(null,
"Caixa de texto contém: " + tf1.getText() +
"\nCaixa de senha contém: " + String.copyValueOf(pf.getPassword())); } if (e.getSource() == copia) { areaDir.setText(areaEsq.getSelectedText()); } }
public void valueChanged(ListSelectionEvent e) { if (e.getSource() == lista) { pcor.setBackground(cores[lista.getSelectedIndex()]); } }
public void itemStateChanged(ItemEvent e) { if (e.getSource() == combo) { mostra.setIcon(imagens[combo.getSelectedIndex()]); } if (e.getSource() == rb1) { cb1.setSelected(false); cb2.setSelected(false); } if (e.getSource() == rb2) { cb1.setSelected(true); cb2.setSelected(false); } if (e.getSource() == rb3) { cb1.setSelected(false); cb2.setSelected(true); } if (e.getSource() == rb4) { cb1.setSelected(true); cb2.setSelected(true); } }
Exercícios
1.
Implementar as funcionalidades da GUI
Calculadora
da aula passada.
2.
Implementar as funcionalidades das aplicações a seguir:
O usuário deverá fornecer os valores do espaço inicial, velocidade inicial e aceleração nas caixas
correspondentes.
O valor de tempo deverá ser gerado aleatoriamente como um inteiro do intervalo [1, 20] quando o usuário clicar no botão Gerar Tempo. O valor gerado deverá ser escrito na caixa correspondente no painel inferior. Use o método Math.random(), que gera um valor aleatório (do tipo double) no intervalo [0.0, 1.0).
Para o desenho do gráfico, utilize a função g.fillOval(x, y, 3, 3), onde:
g é o contexto gráfico do painel p, obtido pela instrução: Graphics g = p.getGraphics(); x é um valor inteiro que deve variar de 0 a p.getWidth() de 1 em 1;
y é um valor inteiro calculado por:
O botão Limpa deverá limpar as caixas de texto e o painel que contém o gráfico. Para limpar um painel p basta pintar o painel com sua cor de fundo, com os métodos:
g.setColor(Color.white);
g.fillRect(0, 0, p.getWidth(), p.getHeight()); onde g é o contexto gráfico do painel p.