Olá, Jorge. Tenho um texto que elabolei para a faculdade sobre o assunto. Acho que pode ajudar... Leonardo A. S. Pacheco Ciência da Computação Universidade de Brasília - UnB ----Original Message Follows---- From: "Jorge Menezes" <[EMAIL PROTECTED]> To: <[EMAIL PROTECTED]> Subject: Sobre Eventos Date: Tue, 2 May 2000 13:02:00 -0300 Olá comunidade Javanesa; Preciso saber em Java, como faz para controlar todos os Eventos que ocorrem em uma Aplicacao ??? tentei o AWTEvent mas nao consegui o resultado Esperado . Atenciosamente, Jorge Menezes [EMAIL PROTECTED] ________________________________________________________________________ Get Your Private, Free E-mail from MSN Hotmail at http://www.hotmail.comTitle: SUMÁRIO
Eventos
Modelo de Eventos da Linguagem
O modelo de eventos empregado em componentes Java Beans é o mesmo atualmente utilizado na linguagem Java, criado na versão 1.1 para substituir o modelo inicial, que era muito pobre e não baseado em OO.
O modelo de evento de linguagens como o Visual Basic é bem simples: o usuário codifica o tratamento do evento no procedimento de evento correspondente àquele tipo de evento, usualmente encontrado após um único clique de mouse. Em compensação, este modelo é bastante limitado, porque obriga o programador a codificar o tratamento de evento dentro do próprio componente. Já em linguagens como C, o programador tem que fazer o gerenciamento da recepção do evento, checando constantemente na fila de eventos o que o sistema operacional está enviando, e verificando o tipo de evento através de longas estruturas de seleção (switch). Obviamente isto torna a programação muito difícil e carregada, mas dá ao programador liberdade total para o tratamento do evento.
Java possui uma abordagem intermediária entre o Visual Basic e o C em termos de poder e de complexidade para o programador. Na prática, fornece ao programador gama razoável de possibilidades, sem que haja a necessidade de acesso a funções e estruturas de baixo nível. Para isto, Java utiliza um modelo de delegação de evento.
Neste modelo há duas partes definidas: a fonte do evento (event source), que é o componente sobre o qual o evento ocorre, e o receptor de eventos (event listener), onde o evento é tratado. Este par fonte-receptor é definido para cada tipo e fonte de evento. O receptor e a fonte podem estar concretizados em um único objeto (abordagem similar ao Visual Basic), pode o receptor ser definido dentro de uma classe interna à classe do objeto-fonte (inner class), ou pode ser um objeto pertencente a uma outra classe independente. Pode haver vários receptores para um único tipo de evento sobre uma única fonte.
A amarração entre a fonte e o receptor de eventos é feita da seguinte forma: cada receptor de eventos é cadastrado na fonte, e quando o evento ocorre, a fonte passa o evento para o receptor através de uma interface que ele conhece e que o receptor deve implementar:
componenteFonte.addXListener(XEventListener objetoReceptor);
No modelo acima X é o tipo de evento e XEventListener é a interface que o objeto receptor deve implementar e pela qual o objeto fonte informará ao receptor sobre a ocorrência de eventos. Por exemplo:
this.addMouseListener(MouseEventListener TrataMouse);
Cada evento é tratado como um objeto, cuja classe corresponde à ao tipo do evento. Quando o evento ocorre, o componente que o sofreu envia a todos os receptores chamadas ao método apropriado determinado pela interface de recepção. Por exemplo:
mouseClicked(MouseEvent evt);
Para simplificar o trabalho de implementação da interface de recepção do evento, existem as classes adaptadoras, que fazem uma implementação padrão da interface correspondente. Pode-se estender a classe adaptadora e sobrecarregar os métodos onde se deseja implantar as rotinas de tratamento de evento. Exemplos:
class classeadaptadora extends XAdapter {código classe} //estendendo a classe adaptadora padrão
componenteFonte.addXListener(new classeadaptadora()); //repassando o evento
componenteFonte.addXListener(new XAdapter() {código classe}) //usando classe interna
Eventos pré-existentes
Como os componentes são geralmente visuais, há uma série de eventos de interface gráfica de usuário (GUI) que precisam ser tratados, tais como eventos de mouse, de teclado, entre outros. Estes eventos são causados pelo usuários, levados ao sistema operacional, que gerencia estes eventos e vai repassando-os para a aplicação. Há outros eventos que são disparados diretamente pelo sistema operacional, tais como temporizadores, por exemplo.
Como são disparados pelo sistema operacional, o projetista do componente não precisa se preocupar em chamar métodos de eventos (fire). Normalmente os componentes visuais herdam direta ou indiretamente da classe java.awt.Component. Esta classe já possui a implementação de vários tipos de eventos, com os métodos de adição e remoção do cadastro respectivos. As classes de eventos, as interfaces de recepção e as classes adaptadoras já estão definidas, retirando do projetista do componente esta responsabilidade.
A tabela mostra vários tipos de eventos suportados as classes que os modelam, a interface de recepção e a classe adaptadora com a implementação padrão da interface:
Tipo de evento |
Exemplos comuns |
Classe que modela o evento |
Interface de recepção |
Classe Adaptadora |
Ação default sobre um componente |
clique em botão; tecla Enter em texto |
ActionEvent |
ActionListener |
ActionAdapter |
Evento de Janela |
fechar a janela |
WindowEvent |
WindowListener |
WindowAdapter |
Cliques de Mouse |
clique, duplo clique |
MouseEvent |
MouseListener |
MouseAdapter |
Movimento de Mouse |
movendo o mouse |
MouseMotionListener |
MouseMotionAdapter |
|
Eventos de componentes |
componente se tornou visível |
ComponentEvent |
ComponentListener |
ComponentAdapter |
Eventos de teclado |
tecla pressionada |
KeyEvent |
KeyListener |
KeyAdapter |
Eventos de foco |
recepção ou perda de foco de teclado |
FocusEvent |
FocusListener |
FocusAdapter |
Eventos de listas |
mudança de seleção em lista ou tabela |
ListSelectionEvent |
ListSelectionListener |
ListSelectionAdapter |
JAVABEANS:
Evento de mudança de propriedades (PropertyChangeEvent)
Estes eventos são utilizados quando se deseja avisar os receptores alguma mudança no estado do bean. Os receptores são cadastrados e removidos do cadastro através dos métodos:
void addPropertyChangeListener(PropertyChangeListener receptor)
void removePropertyChangeListener(PropertyChangeListener receptor)
Estes métodos devem ser implementados pelo componente, a não ser que ele herde da classe JComponent. Para facilitar o trabalho do desenvolvedor, existe uma classe de suporte no pacote java.beans, chamada PropertyChangeSupport. Basta instanciar um objeto desta classe e delegar para ele a tarefa de adição e remoção dos receptores. Exemplo:
private PropertyChangeSupport SuporteMudanca = new PropertyChangeSupport(this)
public void addPropertyChangeListener(PropertyChangeListener receptor) {
SuporteMudanca.addPropertyChangeListener(receptor);
}
void removePropertyChangeListener(PropertyChangeListener receptor)
SuporteMudanca.removePropertyChangeListener(receptor);
}
O evento não é disparado automaticamente, devendo-se acrescentar nos métodos de alteração das propriedades (set...) o disparo do evento para os receptores:
SuporteMudanca
.firePropertyChange("nomePropriedade", valorAntigo, valorNovo);onde os valores devem ser objetos.
A classe JComponent já possui o método firePropertyChange, inclusive com assinaturas para todos os tipos básicos, simplificando o uso do método. Por exemplo:
this.firePropertyChange("quantidade", 10, 11);
Os receptores implementam a interface PropertyChangeListener, com um só método:
void propertyChange(PropertyChangeEvent evento)
O evento possui duas propriedades correspondendo aos valores antigo e novo:
Object valorAntigo = evento.getOldValue();
Object valorNovo = evento.getNewValue();
Evento de mudança de propriedade vetável (VetoableChangeEvent)
A uma propriedade podem ser impostas restrições, como por exemplo, valores sempre pares ou entre dois valores dados de limite. O evento de mudança vetável avisa ao receptor da mudança e este avalia se a mudança é ou não aceita. Caso não seja aceita, uma exceção é lançada e então capturada pelo componente, que retorna o valor antigo da propriedade e possivelmente emite uma mensagem visual ao usuário. Como se pode observar, este tipo de evento é de configuração ligeiramente mais complicada. Os receptores são cadastrados e removidos do cadastro através dos métodos:
void addVetoableChangeListener(VetoableChangeListener receptor)
void removeVetoableChangeListener(VetoableChangeListener receptor)
De forma similar ao evento descrito anteriormente, estes métodos devem ser implementados pelo componente, a não ser que ele herde da classe JComponent, e existe uma classe de suporte no pacote java.beans, chamada VetoableChangeSupport, para facilitar o trabalho do desenvolvedor. Basta instanciar um objeto desta classe e delegar para ele a tarefa de adição e remoção dos receptores. Exemplo:
private VetoableChangeSupport suporteVeto = new VetoableChangeSupport(this)
public void addVetoableChangeListener(VetoableChangeListener receptor) {
suporteVeto.addVetoableChangeListener(receptor);
}
void removeVetoableChangeListener(VetoableChangeListener receptor)
suporteVeto.removeVetoableChangeListener(receptor);
}
Os receptores implementam VetoableChangeListener, interface com um só método:
void vetoableChange(PropertyChangeEvent evento) throws PropertyVetoException
O evento manipulado é o de mudança de propriedade, explicado anteriormente. Na implementação deste método, deve-se fazer o teste de valor da propriedade em questão. Caso o novo valor seja proibido, a exceção PropertyVetoException é lançada para que o componente seja informado do veto, e a propriedade não tenha seu valor alterado.
É recomendável que receptores de mudança de propriedade sejam também receptores de mudanças vetáveis de propriedade, ou seja, que os objetos que implementem VetoableChangeListener também implementem PropertyChangeListener. A idéia é implementar as mudanças de propriedades da seguinte forma:
Os métodos de alteração das propriedades (set...) não tratam a ocorrência de veto, eles repassam para quem os chamou:
public void setValor(tipoPropriedade novoValor) throws PropertyVetoException;
Deve-se primeiramente notificar os receptores da intenção de alterar o valor da propriedade o disparo do evento para os receptores:
suporteVeto
.fireVetoableChange("nomePropriedade", valorAntigo, valorNovo);onde os valores devem ser objetos.
Caso nenhum receptor do evento proíba a mudança de valor da propriedade, ela é efetivada. Caso algum receptor proíba a mudança, ele lança uma exceção PropertyVetoException que é repassada para quem chamou o método de alteração.
No caso de mudança todos os receptores de eventos de mudança recebem a notificação. Os receptores de evento de mudança vetável devem receber também o evento de mudança para saberem que a mudança foi confirmada, ou seja, que não vetada por ninguém.
A classe JComponent já possui também o método firePropertyChange, com assinaturas para todos os tipos básicos e para Object:
this.firePropertyChange("quantidade", 10, 11);
Eventos novos (Custom Events)
Além dos eventos normalmente associados aos componentes, tais como eventos relativos a respostas do usuário, pintura de tela, ou mudança de propriedades, podem ser criados novos eventos, relativos a processamento de informações, combinações de propriedades, entre outros.
O projeto de um novo evento em Java deve envolver passos determinados:
- implementação da classe de evento, herdando da classe EventObject. Ex. BubbleEvent.
- Confecção de interface de recepção com um método de notificação. Ex. BubbleListener. Este método pode ter qualquer nome, mas deve receber como único parâmetro a classe de evento e não possuir retorno (void). Ex.: public void notifyBubble(BubbleEvent event)
- inclusão no componente de métodos relativos ao cadastro dos receptores. Ex.: public void addBubbleListener(BubbleListener listener) e public void removeBubbleListener(BubbleListener listener).
- inclusão no componente de método para disparo do evento. Ex.: public void fireBubbleEvent(BubbleEvent event).
A maior dificuldade no gerenciamento de eventos é o fato de que o processamento orientado a eventos é necessariamente multiprocessado, ou seja, cada novo evento deve gerar uma nova linha de execução independente, denominada processo leve ou thread. Quando utilizamos um evento já implementado por um vendedor, este problema não é percebido.
Outro problema despercebido para eventos padronizados mas importante para a implementação de novos eventos é o gerenciamento dos receptores do evento. Deve ser mantida uma coleção com todos os receptores cadastrados para aquele evento à qual possam ser adicionados e removidos receptores livremente em tempo de execução. Em geral isto é implementado através da classe Vector.
O gerenciamento de coleções de receptores e a possibilidade da existência de várias linhas de execução leva à necessidade de sincronização nos métodos de acesso à coleção de receptores daquele evento. A classe Vector, a partir da segunda versão da linguagem (1.2), passou a utilizar métodos sincronizados. A vantagem no uso de coleções como esta é que a preocupação com a sincronização desaparece, porque ela já existe no projeto da coleção. Além disso, a sincronização dentro do vetor traz benefício de performance em relação à sincronização de todo o componente. De qualquer forma, segue o código necessário para a implementação da sincronização no componente:
public syncronized void addCustomListener(CustomListener listener) {
listeners.add(listener);
}
public syncronized void removeCustomListener(CustomListener listener) {
listeners. remove(listener);
}
Infelizmente, o uso descuidado da sincronização pode levar a um problema conhecido como deadlock, no qual os processos ficam dependendo da liberação de certos recursos por outros processos, de forma sucessiva e circular. É o que pode ocorrer quando se dispara um evento e no tratamento dele é acrescido ou removido um receptor. Para evitar este problema, basta que no disparo evento se tire uma foto do da coleção de eventos (clonagem), e que ela seja utilizada para a notificação do evento. Isto obviamente gera o problema de que podem ser notificados objetos que já haviam sido removidos do cadastro de receptores e não serem notificados objetos recém-cadastrados como receptores, mas este problema é menor do que o provável deadlock. Exemplo de codificação:
public void fireCustomEvent(CustomEvent event) {
Vector currentListeners = null;
CustomListener listener = null;
sincronized(this)
{ currentListeners = (Vector)listeners.clone();
}
for (int i=0; i< currentListeners.size(); i++)
{ listener = (CustomListener)currentListeners.elementAt(i);
listener.notifyMethod(event);
} }