POO – AULA 09 Objetivos da aula Reforçar conceitos sobre interfaces gráficas com componentes Swing Introduzir o conceito de evento Apresentar mais alguns componentes Swing Compreender o funcionamento do tratamento de eventos Recordando... Na aula passada, vimos como criar uma interface gráfica usando componentes do pacote Swing. Entretanto a aplicação que desenvolvemos somente exibia informações, não possuindo entrada de dados. Hoje vamos estender esse conceito, usando outros componentes típicos de aplicações gráficas, como botões e caixas de edição. Antes, entretanto, vamos desenvolver o conceito de tratamento de eventos. Tratamento de eventos As tarefas que um aplicativo gráfico deve realizar são comandadas pelo usuário através da interação deste com a GUI. É comum clicar sobre um botão (“OK”, “Enviar” ou “Gravar”, por exemplo) para instruir o aplicativo a realizar uma tarefa. As aplicações com GUIs são, portanto, baseadas em eventos, que geralmente são gerados pelo usuário. Quando um usuário interage com um componente GUI, ele gera um evento, que resulta numa tarefa a ser executada. Alguns eventos comuns são o clicar em um botão, o digitar numa caixa de texto ou o selecionar uma opção de uma lista. O código que realiza uma tarefa em resposta ao evento é chamado de handler de evento, e o processo total de responder ao evento é conhecido como tratamento do evento. Tratando eventos de componentes Swing Vamos conhecer dois componentes do pacote javax.swing que possuem eventos associados: JTextField e JPasswordField. A classe JTextField estende a classe JTextComponent (javax.swing.text), e possui recursos comuns aos componentes baseados em texto do Swing. Este componente serve, normalmente, para a entrada de dados. A classe JPasswordField estende JTextField adicionando métodos para o processamento de senhas. Este componente mostra “caracteres eco” à medida que o usuário digita, escondendo os caracteres digitados. Quando o usuário, após digitar um texto em um dos componentes acima, pressionar “Enter”, será gerado um evento que poderá acionar uma tarefa. 1 Antes que um aplicativo possa responder a um evento, é necessário: 1. Criar uma classe que represente o handler do evento e instanciar um objeto dessa classe (o handler do evento). 2. Implementar uma interface apropriada, conhecida como interface listener de evento na classe do handler do evento; 3. Registrar o handler do evento, indicando que o objeto de handler do evento deve ser notificado quando o evento ocorrer. O componente GUI acionado pelo usuário gera um ActionEvent (pacote java.awt.event), que é processado pelo objeto handler do evento, que implementa uma interface ActionListener (pacote java.awt.event). O aplicativo a seguir utiliza as classes apresentadas para criar e manipular quatro campos de texto. Quando o usuário digita em um dos campos e pressiona “Enter”, o aplicativo exibe um caixa de diálogo com o texto digitado. Observe que o texto só pode ser digitado no componente que estiver sob “foco”. Um componente recebe o “foco” quando o usuário clica sobre ele ou quando “passeia” pelos objetos utilizando a tecla “tab”. import java.awt.FlowLayout; import java.awt.event.ActionListener; import java.awt.event.ActionEvent; import javax.swing.JFrame; import javax.swing.JTextField; import javax.swing.JPasswordField; import javax.swing.JOptionPane; public class JanelaTexto extends JFrame{ private JTextField campo1; private JTextField campo2; private JTextField campo3; private JPasswordField campo4; public JanelaTexto(){ super("Testa caixas de texto"); setLayout(new FlowLayout()); // layout do frame campo1 = new JTextField(10); //caixa com 10 colunas add( campo1 ); // adiciona campo1 à janela campo2 = new JTextField("Campo editável"); add(campo2); campo3 = new JTextField("Não editável", 10); campo3.setEditable(false); // desativa edição add(campo3); campo4 = new JPasswordField("Senha",10); add(campo4); //instancia o objeto handler do evento TrataEvento handler = new TrataEvento(); 2 campo1.addActionListener(handler);//registra handler campo2.addActionListener(handler);//registra handler campo4.addActionListener(handler);//registra handler } // fim do construtor // classe que implementa o objeto handler do evento private class TrataEvento implements ActionListener{ public void actionPerformed(ActionEvent evento){ String s = ""; if (evento.getSource() == campo1) s=String.format("Texto 1: %s",campo1.getText()); else if (evento.getSource() == campo2) s=String.format("Texto 2: %s",campo2.getText()); else if (evento.getSource() == campo4) s = String.format("Texto: %s", new String(campo4.getPassword())); JOptionPane.showMessageDialog(null, s); } } // fim da classe TrataEvento } // fim da classe JanelaTexto A largura em pixels de uma coluna de texto depende o tamanho da fonte atual do campo de texto. Se o texto digitado for mais largo que o campo de 10 colunas do objeto, a parte do texto à direita não ficará visível. O tratamento de eventos nesse exemplo é realizado por um objeto da classe TrataEvento. Quando o usuário pressionar “Enter” em um JTextField ou no JPasswordField, o componente gera um ActionEvent (pacote java.awt.event), que é processado por um objeto que implemente a interface ActionListener (pacote java.awt.event). Para que o evento no objeto GUI seja atendido é necessário registrar previamente o objeto handler no objeto GUI. Na classe acima, foi utilizado o método actionPerformed de um objeto de tratamento de evento. No caso, a origem dos eventos é o “Enter” nos campos de texto da janela e, quando isto ocorrer, o sistema cria um objeto ActionEvent único que contém informações sobre o evento que acabou de ocorrer, tais como a origem do evento e o texto do campo de texto e passa este objeto em uma chamada para o método actionPerformed do “ouvinte” de eventos, ou listener de eventos. O método getSource() da classe ActionEvent retorna uma referência à origem do evento e o método getActionCommand, da mesma classe, retorna o texto do campo de texto. 3 A seguir temos a classe TestaJanelaTexto que utiliza a classe acima. import javax.swing.JFrame; public class TestaJanelaTexto { public static void main( String args[] ) { JanelaTexto jan = new JanelaTexto (); jan.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); jan.setSize(250, 120); jan.setVisible(true); } } Tipos comuns de eventos Existem diferentes tipos de eventos em uma interface GUI. Ao ocorrer um evento, as informações sobre o evento são armazenadas em um objeto de uma classe que estende a classe AWTEvent. A hierarquia abaixo contém várias classes de eventos do pacote java.awt.event. Estes tipos de eventos são utilizados tantos em objetos AWT como Swing. Eventos específicos para objetos Swing são declarados no pacote javax.swing.event. Object ActionEvent EventObject AdjustmentEvent AWTEvent ContainerEvent ItemEvent FocusEvent TextEvent ComponentEvent PaintEvent WindowEvent KeyEvent InputEvent MouseEvent O modelo de tratamento que vimos aqui é conhecido como modelo de delegação de WindowListener ActionListener eventos, pois o processamento de um AdjustmentListener evento é passado a um objeto particular ComponentListener no aplicativo, o objeto listener ou o ouvinte ContainerListener de eventos. FocusListener Para cada tipo de evento, existe uma ItemListener interface ouvinte de eventos. Um ouvinte KeyListener de evento é um objeto que implementa MouseListener uma ou mais interfaces do pacote MouseMotionListener java.awt.event ou javax.swing.event. Veja TextListener algumas interfaces na caixa ao lado. Cada interface ouvinte de eventos especifica os métodos de tratamento de evento que interessem. Quando ocorre um evento, o componente da interface com o qual o usuário interagiu informa seus ouvintes registrados, chamando o método de tratamento de evento adequado de cada ouvinte. 4 Entendendo o funcionamento de eventos Registrando eventos Cada objeto JComponent, por exemplo JTextField, tem uma variável de instância chamada listenerList que referencia um objeto da classe EventListenerList do pacote javax.swing.event. Podemos entender listenerList como um array . Quando a instrução campo1.addActionListener (handler) é executada, uma nova entrada que contém uma referência ao objeto TextFieldHandler é colocada na listenerList do JTextField campo1. Ou seja, cada componente GUI mantém sua própria lista de ouvintes que foram registrados para tratar os eventos do componente. Chamando o handler de evento: Cada componente GUI suporta vários tipos de eventos: de mouse, de teclado, etc. Quando um evento ocorre, somente os métodos apropriados dos ouvintes de eventos são afetados. Cada tipo de evento tem uma ou mais interfaces ouvintes de eventos correspondentes. ActionEvents são tratados por ActionListeners, MouseEvents são tratados por MouseListeners e MouseMotionListeners, e KeyEvents são tratados por KeyListeners. Quando ocorre um evento, um componente GUI recebe da JVM um ID do evento que especifica o tipo de evento. O componente GUI utiliza o ID para decidir o tipo de ouvinte a ser acionado e o método a ser chamado no objeto ouvinte. Para que isto aconteça, é necessário registrar um handler de evento no componente GUI para o tipo particular de evento que seu aplicativo exige, e o componente GUI fará com que o método apropriado do handler seja chamado. O componente JButton Um botão é um componente que o usuário clica com o mouse para disparar uma ação específica. Um aplicativo pode utilizar vários tipos de botões: botões de comando, caixas de seleção e botões de opção. A classe JanelaBotoes cria 2 JButtons com ícones, e realiza o tratamento de eventos dos botões em uma única instância da classe TrataBotao. import import import import import import import import java.awt.FlowLayout; java.awt.event.ActionEvent; java.awt.event.ActionListener; javax.swing.JFrame; javax.swing.JButton; javax.swing.Icon; javax.swing.ImageIcon; javax.swing.JOptionPane; 5 public class JanelaBotoes extends JFrame { private JButton botao1; private JButton botao2; public JanelaBotoes() { super("Testando Botões"); setLayout(new FlowLayout()); botao1 = new JButton("Botão simples"); add(botao1); Icon bug1 = new ImageIcon( getClass().getResource("bug1.gif")); Icon bug2 = new ImageIcon( getClass().getResource("bug2.gif")); botao2 = new JButton("Botão com ícone", bug1); botao2.setRolloverIcon(bug2); //icone de rollover add(botao2); TrataBotao handler = new TrataBotao(); botao2.addActionListener(handler); // registra handler botao1.addActionListener(handler); // registra handler } private class TrataBotao implements ActionListener{ public void actionPerformed(ActionEvent evento){ JOptionPane.showMessageDialog (JanelaBotoes.this, String.format("Você cliclou no : %s", evento.getActionCommand())); } } } Os components JButtons, assim como os JTextFields, geram ActionsEvents que podem ser processados por qualquer objeto ActionListener. A classe TrataBotao declara o método actionPerformed que exibe numa caixa de mensagem o rótulo do botão clicado pelo usuário. A classe TestaJanelaBotoes a seguir completa a aplicação. import javax.swing.JFrame; public class TestaJanelaBotoes{ public static void main(String args[]) { JanelaBotoes Jan = new JanelaBotoes(); Jan.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); Jan.setSize( 300, 200 ); Jan.setVisible(true); } } As caixas de mensagens aparecem centralizadas na janela do aplicativo devido à chamada do método showMessageDialog, utilizar como primeiro argumento JanelaBotoes.this em vez de null. Este primeiro argumento 6 indica o componente que irá conter (container) a caixa de diálogo. Desafio 1. Usando a interface KeyListener e o evento KeyEvent, implemente um handler para detectar e emitir uma mensagem quando uma tecla for apertada enquanto o foco estiver no componente botao2. 7