sábado, 8 de agosto de 2009

O Modelo de Eventos do Flex

Esse é um assunto básico - não no sentido de ser simples, mas sim no de que todo programador Flex deveria conhecer. O Flex possui um modelo de desenvolvimento dirigido por eventos ("event driven programming model") e quase tudo dentro do framework acontece através de eventos. Um evento pode ser gerado através de entradas externas, como a interação dos usuários (eventos de mouse, de teclado, etc) ou como o retorno de uma chamada a um serviço Web. Além disso, um evento pode ser gerado quando ocorre alguma mudança na aparência de um componente ou no seu ciclo de vida - por exemplo, quando um componente é criado, destruído ou redimensionado. Em breve, farei um post falando somente sobre o ciclo de vida dos componentes visuais do Flex (que também é um assunto bem importante).

Através dos eventos (objetos derivados da classe flash.events.Event), um desenvolvedor pode saber quando aconteceu alguma coisa dentro de uma aplicação e quem disparou o evento ("target"), podendo assim fazer os tratamentos necessários nessas situações. Para isso, ele poderá registrar "listeners" em diferentes locais, para interceptarem e tratarem esses eventos. Cada listener é ligado a uma função "handler" - funções que recebem um tipo de evento como parâmetro e executam um código determinado.

Segue abaixo um exemplo simples, seguido de dois trechos de código-fonte (um utilizando MXML e outro Action Script puro):

  • Se não conseguir visualizar a aplicação, clique aqui.


<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">

 <mx:Script><![CDATA[
  import mx.controls.Alert;

  private function btnEditClickEventHandler(event: MouseEvent): void{
   Alert.show("O botão \"Editar\" foi clicado!");
  }

 ]]></mx:Script>

 <mx:Button id="btnEdit" label="Editar" horizontalCenter="0" verticalCenter="0" click="btnEditClickEventHandler(event)"/>  

</mx:Application>

package{
 import flash.events.MouseEvent;

 import mx.controls.Alert;
 import mx.controls.Button;
 import mx.core.Application;

 public class EventListenerSimpleExampleAS extends Application{

  private var _btnEdit: Button;

  public function EventListenerSimpleExampleAS(){
   super();
  }

  protected override function createChildren(): void{
   super.createChildren();
   _btnEdit = new Button();
   _btnEdit.label = "Editar";
   _btnEdit.setStyle("horizontalCenter", 0);
   _btnEdit.setStyle("verticalCenter", 0);
   _btnEdit.addEventListener(MouseEvent.CLICK, btnEditClickEventHandler);
   this.addChild(_btnEdit);
  }

  private function btnEditClickEventHandler(event: MouseEvent): void{
   Alert.show("O botão \"Editar\" foi clicado!");
  }
  
 }
}

Os eventos podem ainda levar diversas informações para outras partes da aplicação - um evento customizado de login, por exemplo, pode ter um atributo "login", do tipo String, que informará aos componentes que estiverem ouvindo o evento qual é o login do usuário que acessou o sistema. Resumidamente, os eventos atuam como "transportadores de informação" dentro de uma aplicação Flex, se beneficiando do modelo de eventos do framework, que será apresentado abaixo.


Fluxo de Eventos do Flex ("Event propagation")

O modelo de eventos da plataforma Flash (consequentemente do Flex) é baseado atualmente na especificação "Document Object Model (DOM) Level 3 Events Specification", da W3C. Dessa forma, quando um evento é disparado, ele terá um fluxo ("event flow"), que descreve a propagação do objeto evento na estrutura de dados onde o objeto que originou o evento ("target") está inserido. No Flex, todos os componentes visuais (subclasses de DisplayObject) são inseridos em uma estrutura em forma de árvore chamada "Display List" e um evento disparado em um componente pode percorrer toda a estrutura de componentes aninhados dentro dessa árvore. O topo da "DisplayList" é chamado de "Stage" - é o container principal, onde todo restante dos componentes visuais serão inseridos (incluindo os outros containers, como o "Application", que é o primeiro a ser carregado em uma aplicação Flex).

Quando o Flash Player dispara um evento, é feita uma jornada circular desde a raiz da "Display List", passando pelo nodo onde esse evento foi gerado ("target"), e retornando para o nodo raiz - verificando em cada nodo se existem listeners registrados para o evento.

A propagação de eventos no Flex passa por três estágios: fase de captura, fase do alvo e fase de borbulhamento, conforme pode ser observado na figura abaixo, que representa o ciclo envolvido ao ser disparado um evento de clique em um botão, que está inserido em um "ControlBar, que por sua vez está inserido em um "Panel":

Flex Event Propagation(imagem cedida pelo Richard R. Manzke)


Obs.: Nos componentes não visuais, como HTTPService, não existem essas três fases e o evento é disparado diretamente no target.


1-) Fase de Captura (capture phase)
A fase de captura compreendende todos os nodos - desde o nodo raiz ("Stage"), passado pelo "Application" e pelos demais componentes aninhados, e terminando no pai ("parent") do local onde foi gerado o evento. Durante essa fase o Flash Player examina cada nodo (começando pela raiz) para verificar se ele tem um listener registrado para tratar o evento. Se tiver, o Flash Player seta valores de algumas propriedades do objeto evento (como a "currentTarget" - que tem referência para o objeto corrente do fluxo de verificação) e chama a função handler resgistrada no listener encontrado. O Flash Player continua então esse processo até o nodo parent do componente que gerou o evento ("target") - a não ser que o programador interrompa programaticamente o restante do fluxo, através do método "stopPropagation()" da classe Event.

2-) Fase do alvo (target phase)
A fase do alvo, apesar do nome, acontece no componente onde foi originado o evento (quem disparou). Durante essa fase, o Flash Player examina o nodo target para verificar se ele tem um listener registrado para tratar o evento. Se tiver, seta valores de algumas propriedades do objeto evento e chama a função handler resgistrada no listener encontrado.

3-) Fase de borbulhamento (bubbling phase)
A fase de borbulhamento é o contrário da fase de captura - ela compreendende todos os nodos desde o pai (parent) do local onde foi gerado o evento até o nodo raiz (Stage). Durante essa fase o Flash Player examina cada nodo a partir do nodo pai do local que gerou o evento, para verificar se ele tem um listener registrado para tratar o evento. Se tiver, o Flash Player seta valores de propriedades do objeto evento e chama a função handler resgistrada no listener encontrado. O Flash Player continua então esse processo até o nodo nodo raiz (Stage) - processo que também pode ser interrompido pelo método stopPropagation().


Dessa forma, a função handler de um listener adicionado em um componente acima do target seria acionada duas vezes em cada ciclo - uma durante a fase de captura e outra durante a fase de borbulhamento. Mas não é isso que ocorre na maioria das vezes porque, por padrão, um listener não recebe eventos durante uma fase de captura, ao menos que seja atribuído "true" para o terceiro argumento ("useCapture") do método que adiciona um listener ("addEventListener") em um componente, conforme o exemplo abaixo:
this.addEventListener(MouseEvent.CLICK, btnEditClickHandler, true);
É importante salientar que o argumento ("useCapture") é exclusivo - isso significa que se ele for utilizado, a função handler não será chamada durante a fase de alvo ou de borbulhamento. Para chamar a função em todas as fases, embora seja raro utilizar esse recurso, teria que ser adicionado dois listeners, conforme o exemplo abaixo:
this.addEventListener(MouseEvent.CLICK, btnEditClickHandler);
this.addEventListener(MouseEvent.CLICK, btnEditClickHandler, true);

Dica.: Só use a fase de captura quando for realmente necessário, porque ela é mais pesada computacionalmente que a fase de borbulhamento.


Bom, esse post já está ficando meio longo, então vou encerrá-lo por aqui e quebrar o assunto em dois. O próximo post será uma continuação desse, mas com uma abordagem bem mais prática do fluxo de eventos, dirigida a código-fonte. Para informações de como as versões mais antigas (que utilizavam até a versão 2 do Action Script) lidavam com eventos, leia esse artigo.


Referências

About Events
Event propagation
Fluxo de um Evento no Flex
How ActionScript 3.0 event handling differs from earlier versions
Introduction to event handling in ActionScript 3.0
Understanding the display architecture
Using events

Um comentário:

Débora C. B. disse...

Oi!!! Olha o feedback no post de tempos atrás...sou iniciante em flex e estava pesquisando sobre eventos quando vi teu post q me deu uma explicação muuito fácil de ser entendida!
Obrigado, vc me ajudou muiiito!!Tá favoritado =)


Parabéns!