Drag & Drop em Adobe Flex: Como limitar a quantidade de elementos

Neste post gostaria de compartilhar uma solução que aplicamos em uma de nossas aplicações para limitar a quantidade de elementos em uma List enquanto o usuário utiliza as funcionalidades de Drag & Drop.

Basicamente possuímos duas lista, onde a lista de origem pode possuir diversos elementos e a segunda lista, que receberá os elementos selecionados pelo usuário, deve aceitar uma quantidade máxima de elementos.

Após configurar as propriedades de drag e drop de cada elemento, habilitamos o evento dragDrop na lista destino. Neste evento realizaremos a validação da quantidade máxima aceitável de elementos, através da soma dos elementos já existentes na lista de destino mais a quantidade de elementos que o usuário está tentando adicionar através do drop.

Para a quantidade de elementos já existentes na lista basta obter a propriedade length do dataprovider da list.

var qtdeDestino:int = event.currentTarget.dataProvider.length;

Para a quantidade de elementos que o usuário está tentando adicionar à lista, obtemos a propriedade ‘items’ do dragSource do evento, criando um Array. Em seguida obtemos a propriedade length do Array.

var itemsArray:Array = event.dragSource.dataForFormat('items') as Array;
var qtdeOrigem:int = itemsArray.length;

Finalmente basta somar os valores e comparar com a quantidade máxima de elementos desejada. Após a validação apresentamos uma mensagem de alerta para o usuário.

Para que não ocorra de mesmo após exibir a mensagem de alerta os elementos serem inseridos na lista de destino, devemos cancelar o evento de drop para que os elementos selecionados retornem para a lista de origem.

event.preventDefault();
event.target.hideDropFeedback(event);
DragManager.showFeedback(DragManager.NONE);

Abaixo apresento o código completo do exemplo que criei para esta solução:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" initialize="initApp()">

	<mx:Script>
		<![CDATA[
			import mx.managers.DragManager;
			import mx.controls.Alert;
			import mx.events.DragEvent;
			import mx.collections.ArrayCollection;
		
			private function initApp():void {
                lstDe.dataProvider = new XML(getDados()).dado;
                lstPara.dataProvider = new XML(<dados></dados>).dado;
            }
            
            private function getDados():XML {
            	var xmlDados:XML = 	<dados>
										<dado>Linha 1</dado>
										<dado>Linha 2</dado>
										<dado>Linha 3</dado>
										<dado>Linha 4</dado>
										<dado>Linha 5</dado>
										<dado>Linha 6</dado>
										<dado>Linha 7</dado>
										<dado>Linha 8</dado>
										<dado>Linha 9</dado>
										<dado>Linha 10</dado>
									</dados>;
            	return xmlDados;
            }
            
            private function dragDropHandler(event:DragEvent):void {
				
				//Array com elementos que estão sendo movidos
				var itemsArray:Array = event.dragSource.dataForFormat('items') as Array;
				
				//Quantidade de elementos da lista de Origem e Destino
				var qtdeOrigem:int = itemsArray.length;
				var qtdeDestino:int = event.currentTarget.dataProvider.length;
				
				//Verificar quantidade máxima de elementos
				if ((qtdeOrigem + qtdeDestino) > 6) {
					Alert.show("Você excedeu a quantidade máxima de registros!");
					//Cencelar evento de Drag e Drop
					event.preventDefault();
				 	event.target.hideDropFeedback(event);
				 	DragManager.showFeedback(DragManager.NONE);
				}
				
	        } 
			
		]]>
	</mx:Script>
	
	<mx:List x="10" y="10" width="173" height="393" id="lstDe" 
		allowMultipleSelection="true"
        dragEnabled="true"
        dragMoveEnabled="true"/>
	<mx:List x="241" y="10" width="173" height="393" id="lstPara"
		dropEnabled="true"
		dragDrop="dragDropHandler(event);"/>
		
	<mx:Button id="btnReset" 
        label="Reset"
        click="initApp()"
     	x="10" y="411"/>
	
</mx:Application>

Qualquer dúvida não exitem em perguntar!

Abs!!!

Implementar ToolTip com HTML em qualquer componente em Adobe Flex

Como sabemos nem todos componentes em Adobe Flex permitem criar ToolTip com caracteres de formatação HTML. Algumas vezes isso pode ser um pequeno impecilho.

 

 

 

 

 

 

Depois de pesquisar um pouco encontrei a seguinte solução.

Basicamente trata-se de substituir a classe que gerencia as propriedades de ToolTip por uma classe que recebe HTML Texto.
Primeiro criamos uma classe extendida da classe ToolTip:

package asClass
{
 import mx.containers.*;
 import mx.controls.Text;
 import mx.controls.ToolTip;
 import mx.core.*;

 public class HTMLToolTip extends ToolTip
 {
 public function HTMLToolTip()
 { super(); }

 override protected function commitProperties():void{
 super.commitProperties();
 textField.htmlText = text;
 }
 }
}

Em seguida devemos atribuir nossa classe ao Gerenciador de ToolTip no início de nossa aplicação:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="init()">

<mx:Script>
 <![CDATA[

import mx.managers.ToolTipManager;
import asClass.HTMLToolTip;
<span style="font-family: 'Courier 10 Pitch', Courier, monospace; font-size: small;"><span style="line-height: 19px;">
</span></span> private function init():void {

 //Substituindo a classe do Gerenciador de ToolTip pela nossa classe
 ToolTipManager.toolTipClass = HTMLToolTip;
 //Atribuindo o texto ao compoennte
 btn.toolTip = "Button em Flex com <b>HTML</b>\nE quebra de linha.";

 }

 ]]>
 </mx:Script>
 <mx:Button id="btn" y="40" label="Implementando ToolTip" horizontalCenter="0"/>

</mx:Application>

Desta forma qualquer componente passa a aceitar texto de ToolTip com tags HTML.

 

 

 

 

 

Espero que a dica seja útil! Abraços!!!

Centralizar uma janela PopUp na aplicação Adobe Flex

Centralizar uma Janela com Popup em Flex

 

 

 

 

 

 

 

 

 

 

 

Um recurso muito utilizado nas aplicações Flex são as janelas do estilo PopUp, que são aquelas que abrem sobre sua aplicação, podendo inclusive serem movidas pelo usuário. Normalmente quando abrimos uma nova janela desse tipo queremos que a mesma apareça exatamente no centro da tela, ou seja, da aplicação.

Para isso utilizamos o seguinte código:

PopUpManager.addPopUp(myPopUp, this, true);
PopUpManager.centerPopUp(myPopUp);

Normalmente utilizamos estes três parâmetros:

window: Nossa janela que desejamos exibir
parent: Componente pai que será utilizado como referência para posicionar a nova janela
modal: Variável booleana que define se nossa janela será do tipo Modal (se irá inibir o restante da aplicação).

O método centerPopUp, como o próprio nome diz, é utilizado para centralizar a janela sobre o componente pai. Aqui começa nosso problema. Quando o “parent” é nosso Application, tudo fica perfeito, pois nossa janela ficará exatamente no centro da aplicação do usuário. Entretanto se estamos executando nosso código, a partir de um sub-componente, por exemplo um que esteja na parte de baixo da aplicação, nossa janela popup será centralizada sobre sobre este componente.

Para corrigir isso, utilizamos o seguinte código:

PopUpManager.addPopUp(concessionaria,
                      DisplayObject(this.parentApplication),true);
PopUpManager.centerPopUp(concessionaria);

Desta forma estaremos dizendo ao PopUpManager que o componente pai da nossa aplicação será nosso Application, fazendo com que nossas janelas PopUp sempre sejam centralizadas sobre nossa aplicação.

Que tal, ótima dica não? Abs!

 

Auto ajuste de Posição e Zoom no Google Maps em Adobe Flex

Essa dica vai para quem trabalha com a API do Google Maps para Flex (que aliás não será continuada pelo Google depois que a Adobe jogou a toalha na guerra contra a Apple). A dica também vale para as API´s de outras linguagens.

Após criar uma série de elementos no seu mapa, como marcas ou polígonos, é comum você querer visualizar todos os elementos de uma só vez. Para tanto é necessário identificar a coordenada de latitude e longitude que representa o centro entre todas suas coordendas e identificar o melhor nível de zoom para que todos os elementos fiquem visíveis.

Por sorte a API já possibilita fazer isso de forma automática utilizando a classe LatLngBounds.

Basta criar um loop para ler todas suas coordenadas e executar o método extend da classe LatLngBounds, passando como parâmetro um LatLng.

for each (var coor:XML in coordenadas.coordenada) {
 var latlng:LatLng = new LatLng(<a href="mailto:coor.@lat">coor.@lat</a>, <a href="mailto:coor.@long">coor.@long</a>);  
 latlngbounds.extend( latlng ); 
}

Após isso, a classe LatLngBounds retorna o ponto central através do método getCenter e o melhor nível de zoom através do método getBoundsZoomLevel. Em seguida é só chamar o método setCenter do seu map, passando estes valores como seus parâmetros.

map.setCenter( latlngbounds.getCenter( ), map.getBoundsZoomLevel( latlngbounds ) );

Realmente muito fácil!

Em seguida meu código completo para o exemplo. Abraços!


<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="<a href="http://www.adobe.com/2006/mxml">http://www.adobe.com/2006/mxml</a>" layout="absolute" xmlns:ns1="com.google.maps.*">
 <mx:Script>    
  <![CDATA[
   import com.google.maps.controls.ZoomControl;
   import com.google.maps.controls.PositionControl;
   import com.google.maps.overlays.Marker;
   import com.google.maps.LatLngBounds;        
  
   import com.google.maps.LatLng;    
   import com.google.maps.Map;    
   import com.google.maps.MapEvent;    
   import com.google.maps.MapType; 
   
   private var coordenadas:XML =  <Coordenadas>
            <coordenada lat="40.725536353580054" long="-74.0279584510972"/>
            <coordenada lat="40.73699441500612" long="-73.95928881794303"/>
            <coordenada lat="40.74114835218548" long="-74.00160709841569"/>
            <coordenada lat="40.732606151031106" long="-73.96607223885681"/>
            <coordenada lat="40.72625822524804" long="-74.03205298109546"/>
            <coordenada lat="40.72575598523321" long="-74.02672837265969"/>
            <coordenada lat="40.74094766828434" long="-74.02550009149331"/>
            <coordenada lat="40.74386527348633" long="-73.99121805396857"/>
            <coordenada lat="40.72263440857748" long="-73.9793768120008"/>
            <coordenada lat="40.73356963372927" long="-74.02600361168126"/>
           </Coordenadas>;   
   
   private function onMapReady(event:Event):void {      
    
    this.map.addControl(new PositionControl());
    map.addControl(new ZoomControl());
    this.map.setCenter(new LatLng(40.736072,-73.992062), 14, MapType.NORMAL_MAP_TYPE);  
    
    for each (var coor:XML in coordenadas.coordenada) {
     var latlng:LatLng = new LatLng(<a href="mailto:coor.@lat">coor.@lat</a>, <a href="mailto:coor.@long">coor.@long</a>);  
     map.addOverlay(new Marker(latlng));         
    }
     
   } 
   
   private function zoomToFit():void {
    
    var latlngbounds:LatLngBounds = new LatLngBounds();
    
    for each (var coor:XML in coordenadas.coordenada) {
     var latlng:LatLng = new LatLng(<a href="mailto:coor.@lat">coor.@lat</a>, <a href="mailto:coor.@long">coor.@long</a>);  
     latlngbounds.extend( latlng ); 
    }
    map.setCenter( latlngbounds.getCenter( ), map.getBoundsZoomLevel( latlngbounds ) );

   }  
   
  ]]>
 </mx:Script>

 <mx:Button label="Ajustar..." left="10" top="10" click="zoomToFit()"/>

 <ns1:Map top="40" bottom="10" left="10" right="10"  id="map" mapevent_mapready="onMapReady(event)" sensor="false"
  key=""/>
 
</mx:Application>

Exibir e ocultar marcadores do Google Maps em Adobe Flex

Recentemente no novo projeto da equipe tivemos a necessidade de criar um mapa para plotar uma série de elementos geo referenciados. Para tanto utilizamos a API do Google Maps para Flash. Uma das funcionalidades desenhadas foi a de exibir e ocultar os elementos de acordo com alguma característica.
O conceito utilizado na ferramenta foi de trazer todos os elementos e carregá-los em memória através de uma variável XML e em seguida criar os elementos sobre o mapa de acordo com as características, mudando por exemplo o ícone. Porém ao pressionar o botão para ocultar um tipo de elemento gostaríamos de resgatar os marcadores do mapa daquele tipo de elemento específico e aplicar um visible = false.
Depois de muito buscar, aparentemente não existe um método que retorne diretamente da classe Map os marcadores criados sobre ele. Dessa forma a solução seria “limpar” todos os marcadores do mapa e voltar a populá-los, aplicando filtros sobre o xml para que  apenas determinados elementos fossem plotados. A solução não pareceu ser a melhor, pois requer um certo nível de processamento cada vez que se solicite uma atualização.

Depois de um pouco de pesquisas na net, encontrei a solução que descreverei abaixo.

Vamos considerar como fonte de dados o seguinte arquivo xml:

<ListaClientes>
<Cliente nome='Sabor Mineiro' tipo='Restaurante' lat='-23.506779' lon='-46.647123'/>
<Cliente nome='Casa da Massa' tipo='Restaurante' lat='-23.527674' lon='-46.668667'/>
<Cliente nome='Tanque Cheio' tipo='Posto de Gasolina' lat='-23.505392' lon='-46.623842'/>
<Cliente nome='Posto da Freguesia' tipo='Posto de Gasolina' lat='-23.486505' lon='-46.695907'/>
<Cliente nome='Última opção' tipo='Posto de Gasolina' lat='-23.528078' lon='-46.639978'/>
<Cliente nome='Banco do Porquinho' tipo='Banco' lat='-23.504796' lon='-46.606681'/>
<Cliente nome='Colchão Cheio' tipo='Banco' lat='-23.477944' lon='-46.605554'/>
<Cliente nome='Árvore do Dinheiro' tipo='Banco' lat='-23.534688' lon='-46.673334'/>
</ListaClientes>

No nosso exemplo estaremos plotando sobre o gráfico clientes de uma determinada empresa onde esses clientes são agrupados com uma categoria tipo, que classifica os clientes de acordo com sua atividade.

Ao invés de apenas consumir o xml e agregar os marcadores sobre o mapa, vamos guardar em memória as instâncias dos marcadores, para que os mesmos possam ser acessados futuramente. Para isso criamos um Array do tipo Object onde serão armazenados grupos de tipos de clientes. Neste exemplo possuímos três grupos de clientes: Restaurantes, Postos de Gasolina e Bancos. Cada posição do array armazenará duas informações: a cor do marcador para a categoria e um array de marcadores. Dessa forma teremos um array dentro de outro, ou seja, um conjunto de marcadores dentro de um conjunto de tipos de clientes. Assim ficou nosso array:

private var categorias:Object =
{"Restaurante": {
  "color": 0xFF0000,
  "markers": []},
 "Posto de Gasolina": {
  "color": 0x0000FF,
  "markers": []},
 "Banco": {
  "color": 0x00FF00,
  "markers": []}
};

Após consumir o xml, criamos um laço para percorrer todas as tags e para cada uma invocar o seguinte método, onde serão criados os marcadores e estes adicionados ao array de categorias:

public function createMarker(latlng:LatLng, nome:String, tipo:String):void {
   var markerOptions:MarkerOptions = new MarkerOptions({});
   var fillStyle:FillStyle = new FillStyle({color: categorias[tipo].color});
   markerOptions.fillStyle = fillStyle;
    
   var marker:Marker = new Marker(latlng, markerOptions);
   var html:String = "<b>" + nome + "</b>";
   marker.addEventListener(MapMouseEvent.CLICK, function(e:MapMouseEvent):void {
      marker.openInfoWindow(new InfoWindowOptions({contentHTML:html}));
   });
   categorias[tipo].markers.push(marker);
   map.addOverlay(marker);
}

Para definir a cor dor marcadores, por exemplo, acessamos a variável “color” do grupo de categoria específico dessa forma: “categorias[tipo].color” e após criar o marcador, sua instência é armazenada no array dessa forma: “categorias[tipo].markers.push(marker)”

Ao final definimos uma série de botões do tipo Toogle para que ao clicar seus respectivos tipos de clientes sejam ocultos ou exibidor. Para isso resgatamos o array de marcadores do grupo de categoria específica, percorremos os marcadores e setamos cada um como visible true ou false.

private function toggleCategory(type:String):void {
   for (var i:Number = 0; i < categorias[type].markers.length; i++) {
      var marker:Marker = categorias[type].markers[i];
      if (!marker.visible) {
         marker.visible = true;
      } else {
         marker.visible = false;
      }
   }
}

A seguir o código completo do Application:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="<a href="http://www.adobe.com/2006/mxml">http://www.adobe.com/2006/mxml</a>" layout="absolute" xmlns:ns1="com.google.maps.*">

 <mx:Script>
  <![CDATA[
   import com.google.maps.MapType;
   import com.google.maps.InfoWindowOptions;
   import com.google.maps.overlays.Marker;
   import com.google.maps.styles.FillStyle;
   import com.google.maps.overlays.MarkerOptions;
   import com.google.maps.MapMouseEvent;
   import com.google.maps.controls.ZoomControl;
   import com.google.maps.LatLng;
   import com.google.maps.MapEvent;
   import mx.controls.Alert;
   import mx.rpc.events.FaultEvent;
   import mx.rpc.events.ResultEvent;
   import com.google.maps.Map;
  
   private var map:Map;
   private var xmlPredios:XML;
   
   private var categorias:Object =
   {  "Restaurante": {
     "color": 0xFF0000,
     "markers": []},
    "Posto de Gasolina": {
     "color": 0x0000FF,
     "markers": []},
    "Banco": {
     "color": 0x00FF00,
     "markers": []}
   };

   
   public function onCanvasMapInitialize(event:Event):void {
          map = new Map();
          map.percentHeight = 100;
          map.percentWidth = 100;
          map.key = "xxxxxxxxxxxxxx";
          map.addEventListener(MapEvent.MAP_READY, onMapReady);
          cvsMapa.addChild(map);
   }
   
   private function onMapReady(event:Event):void {
          map.enableScrollWheelZoom();
          map.enableContinuousZoom();
          map.setCenter(new LatLng(-23.516066, -46.643229), 12,MapType.NORMAL_MAP_TYPE);
          map.addControl(new ZoomControl());
         
          carregarXml();
   }
   
   private function carregarXml():void {
    var xmlString:URLRequest = new URLRequest("xml_clientes.xml");
       var xmlLoader:URLLoader = new URLLoader(xmlString);
          xmlLoader.addEventListener("complete", consumirXml);

   }

   public function consumirXml(event:Event):void {
    var xmlAgencias:XML = new XML(event.target.data);
          var markers:XMLList = xmlAgencias.Cliente;
          var markersCount:int = markers.length();
         
          for (var i:Number = 0; i < markersCount; i++) {
              var marker:XML = markers[i];
              var name:String = <a href="mailto:marker.@nome">marker.@nome</a>;
              var type:String = <a href="mailto:marker.@tipo">marker.@tipo</a>;
              var latlng:LatLng = new LatLng(<a href="mailto:marker.@lat">marker.@lat</a>, <a href="mailto:marker.@lon">marker.@lon</a>);
              createMarker(latlng, name, type);
          }
      }
  
   public function createMarker(latlng:LatLng, name:String, type:String):void {
    var markerOptions:MarkerOptions = new MarkerOptions({});
    var fillStyle:FillStyle = new FillStyle({color: categorias[type].color});
    markerOptions.fillStyle = fillStyle;
       
    var marker:Marker = new Marker(latlng, markerOptions);
    var html:String = "<b>" + name + "</b>";
    marker.addEventListener(MapMouseEvent.CLICK, function(e:MapMouseEvent):void {
     marker.openInfoWindow(new InfoWindowOptions({contentHTML:html}));
    });
    categorias[type].markers.push(marker);
    map.addOverlay(marker);
   }

   private function toggleCategory(type:String):void {
    for (var i:Number = 0; i < categorias[type].markers.length; i++) {
     var marker:Marker = categorias[type].markers[i];
     if (!marker.visible) {
      marker.visible = true;
     } else {
      marker.visible = false;
     }
    }
   }
   
   
  ]]>
 </mx:Script>
 
 <mx:Button x="10" y="10" label="Restaurantes" toggle="true" selected="true" click="toggleCategory('Restaurante')"/>
 <mx:Button x="121" y="10" label="Postos de Gasolina" toggle="true" selected="true" click="toggleCategory('Posto de Gasolina')"/>
 <mx:Button x="262" y="10" label="Bancos" toggle="true" selected="true" click="toggleCategory('Banco')"/>
 
 <mx:Canvas id="cvsMapa" initialize="onCanvasMapInitialize(event)" bottom="10" top="40" left="10" right="10">
 
 </mx:Canvas>
 
</mx:Application>

Alerta PopUp deslizante em Adobe Flex

Para quem assim como eu se preocupa muito com a experiência do usuário no desenvolvimento de sistemas sabe como as janelas PopUp saltitantes que insistem em congelar a aplicação são desesperadoras. E se você tem essas mensagens programadas também para as falhas do sistema, tem que tratar muito bem as Exceptions para não encher a aplicação de mensagens do tipo modal e fazer o usuário ficar minutos e mais minutos clicando em ok.

Quando comecei a utilizar o twitter percebi algo diferente. As mensagens de confirmação de ações da área de configuração aparecem sempre no topo do browser, deslizando suavente da parte de cima e exibindo a mensagem, o que me pareceu muito interessante. Este tipo de mensagem não faz com que você tenha que clicar outra vez em um botão (normalmente OK), sendo que você já sabe que sua ação foi bem sucedida. Isso economiza tempo do usuário e causa um efeito mais clean e profissional no sistema.

A partir disso adotei essa técnica nos novos desenvolvimentos e vou compartilhar de forma geral como utilizo.

A mensagem é um componente baseado em um Canvas com uma imagem do lado esquerdo e um Label, onde será exibida a mensagem, sendo que o conteúdo será armazenado em uma variável de classe do tipo String.

<mx:Image id="imgAlert" source="imagens/ok.png" verticalCenter="0" left="10"/>
<mx:Label text="{mensagem}" left="98" verticalCenter="0" right="10" fontSize="16"/>

Também criei uma pasta com as imagens que utilizo para cada tipo de mensagem: Erro, Ajuda, Informativo, Ok, Alerta. De acordo com o tipo de mensagem será exibido a respectiva imagem.

No componente também foi criado um Timer, responsável por fazer com que a mensagem “feche”. Basicamente o efeito funciona da seguinte forma: O compomente inicialmente é criado com height igual a 0. Ao executar a mensagem, a altura do componente é setada por exemplo para 100 pixels e é iniciado o Timer. Ao final da contagem, novamente o tamanho do componente é setado para 0.

private var timer:Timer;

private function init():void
{
   this.height = 0;
   timer = new Timer(3000);
   timer.addEventListener(TimerEvent.TIMER, timerComplete);
}

private function timerComplete(event:TimerEvent):void
{
   this.height = 0;
   timer.stop();
}

Para executar o alerta criei o seguinte método:

private var HEIGHT:int = 100;

public function show(tipo:String, mensagem:String):void
{
   this.height = HEIGHT;
   timer.start();
   this.mensagem = mensagem;
   if (tipo==ERROR) {
      imgAlert.source = "imagens/error.png";
   } else if (tipo==HELP) {
      imgAlert.source = "imagens/help.png";
   } else if (tipo==INFO) {
      imgAlert.source = "imagens/info.png";
   } else if (tipo==OK) {
      imgAlert.source = "imagens/ok.png";
   } else if (tipo==WARNING) {
      imgAlert.source = "imagens/warning.png";
   }
}

Dessa forma ao executar o método show() o tamanho do componente será setado para 100 pixels, executando o efeito de transição de cima para baixo e será iniciado a contagem do timer e de acordo com o tipo de mensagem informado no parâmetro “tipo”, será exibido um ícone diferente.

Para não ter que escrever sempre a string do tipo de mensagem, criei variáveis de classe do tipo static.

public static const ERROR:String = "error";
public static const HELP:String = "help";
public static const INFO:String = "info";
public static const OK:String = "ok";
public static const WARNING:String = "warning";

Para completar o componente, o tipo de efeito foi definido nos parâmetros do Canvas. É utilizado um efeito simples do tipo Resize.

<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas
   xmlns:mx="http://www.adobe.com/2006/mxml"
   width="100%" height="{HEIGHT}"
   backgroundColor="#E0EBF1"
   initialize="init()"
   resizeEffect="Resize">

Para utilizar o componente, crio uma variável de classe no Application, que chamarei de toda parte do sistema e no init defino onde deverá ficar o componente de alerta, ancorado na parte superior e nas laterais do Application.

public static var alert:MeuAlerta;

private function init():void {
   alert = new MeuAlerta();
   alert.setStyle("top",0);
   alert.setStyle("left",0);
   alert.setStyle("rigth",0);
   addChild(alert);
}

Defini a variável do tipo static para não ter que sempre fazer referência ao Application.application, dessa forma pose-se executar realizando referência a classe principal (Application) que no meu projeto se chama AlertaDeslizante, e a variável alert.

AlertaDeslizante.alert.show(MeuAlerta.OK, "Texto da mensagem");

O primeiro parâmetro é o tipo de mensagem, passado através das variáveis estáticas do componente MeuAlerta, e o segundo parâmetro a string da mensagem. Dessa forma essa chamada pode ser executada de qualquer parte da aplicação.

Segue código completo do componente MeuAlerta:

<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas
xmlns:mx="http://www.adobe.com/2006/mxml"
width="100%" height="{HEIGHT}"
backgroundColor="#E0EBF1"
initialize="init()"
resizeEffect="Resize">

<mx:Script>
<![CDATA[

private var HEIGHT:int = 100;
[Bindable]
private var mensagem:String;

public static const ERROR:String = "error";
public static const HELP:String = "help";
public static const INFO:String = "info";
public static const OK:String = "ok";
public static const WARNING:String = "warning";

private var timer:Timer;

private function init():void
{
this.height = 0;
timer = new Timer(3000);
timer.addEventListener(TimerEvent.TIMER, timerComplete);
}

public function show(tipo:String, mensagem:String):void
{
this.height = HEIGHT;
timer.start();
this.mensagem = mensagem;
if (tipo==ERROR) {
imgAlert.source = "imagens/error.png";
} else if (tipo==HELP) {
imgAlert.source = "imagens/help.png";
} else if (tipo==INFO) {
imgAlert.source = "imagens/info.png";
} else if (tipo==OK) {
imgAlert.source = "imagens/ok.png";
} else if (tipo==WARNING) {
imgAlert.source = "imagens/warning.png";
}
}

private function timerComplete(event:TimerEvent):void
{
this.height = 0;
timer.stop();
}
]]>
</mx:Script>

<mx:Image id="imgAlert" source="imagens/ok.png" verticalCenter="0" left="10"/>
<mx:Label text="{mensagem}" left="98" verticalCenter="0" right="10" fontSize="16"/>

</mx:Canvas>

E aqui podem ver o resultado final:

Espero que esta dica seja útil e fiquem abertos a contribuir com melhorias!

Combinando diferentes tipos de gráficos do Adobe Flex através de grupos (Sets)

Uma coisa que impressiona a qualquer gerente ou diretor de empresas são os gráficos do Flex. Com apenas dois ou três gráficos bem feitos e alguns efeitos de transição é muito fácil aprovar qualquer novo projeto. Porém as coisas complicam um pouco quando os usuários começam a pedir “… ah, mas aqui não poderia ter um detalhe assim… ou então uma coluna que mostra-se um valor assado…”.
Realmente construir gráficos com o Adobe Flex é uma tarefa relativamente fácil, porém alguns tipos mais específicos de gráficos requer um pouco mais de atenção.

Neste post vou tratar sobre os grupos de séries de gráficos, utilizados para criar gráficos empilhados e principalmente combinar diferentes grupos de tipos de gráficos diferentes.
O que as vezes causa um pouco de confusão no aprendizado dos iniciantes em Flex são justamente as funcionalidades que existem para facilitar nossa vida. Vejamos como exemplo o código necessário para criar o seguinte gráfico empilhado:

<mx:ColumnChart type="stacked">
    <mx:series>
        <mx:ColumnSeries />
        ...
    </mx:series>
</mx:ColumnChart>

Para realizar o empilhamento do gráfico, basta configurar o parâmetro “type” como “stacked”. Acontece que esse não é um parâmetro da classe ColumnChart. A classe responsável pelo empilhamento é a classe ColumnSet, porém a classe ColumnChart faz esse trabalho sozinha, quebrando a serie em grupos e definindo o empilhamento.
Portanto a forma correta de criar o código acima seria:

<mx:ColumnChart>
    <mx:series>
        <mx:ColumnSet type="stacked">
            <mx:ColumnSeries />
            ...
        </mx:ColumnSet>
    </mx:series>
</mx:ColumnChart>

O nível de complexidade aumenta quando surge a necessidade de combinar tipos de gráficos diferentes ou mesmo diferentes formas de combinação de um mesmo tipo de gráfico. Nessa hora não tem para onde fugir: temos que utilizar os grupos de Set.

Os agrupamentos existem apenas para três tipos de gráficos: AreaSet, ColumnSet e BarSet.

Para exemplificar a utilização, imaginemos o seguinte gráfico, onde a necessidade é mostrar a quantidade estimada de vendas por mês e a quantidade atingida de vendas segmentado por tipo de venda.

Se não fosse utilizado o ColumnSet para separar os tipos de vendas da estimativa de vendas, o que veríamos seria uma única coluna com três divisões.
Os agrupamentos de séries também podem ser utilizados para combinar tipos diferentes de gráficos como por exemplo LineChart.

Veja a seguir o código utilizado no exemplo acima:

<?xml version="1.0"?>
<mx:Application xmlns:mx="<a href="http://www.adobe.com/2006/mxml">http://www.adobe.com/2006/mxml</a>"
>
  <mx:Script><![CDATA[
     import mx.charts.Legend;
     import mx.charts.ColumnChart;
     import mx.charts.series.ColumnSet;
     import mx.charts.series.ColumnSeries;
     import mx.collections.ArrayCollection;

     [Bindable]
     private var vendasData:ArrayCollection = new ArrayCollection([
        {mes:"Janeiro", vendas:120, diretas:45, indiretas:102},
        {mes:"Fevereiro", vendas:108, diretas:42, indiretas:87},
        {mes:"Março", vendas:150, diretas:82, indiretas:32},
        {mes:"Abril", vendas:170, diretas:44, indiretas:68},
        {mes:"Maio", vendas:250, diretas:57, indiretas:77},
        {mes:"Junho", vendas:200, diretas:33, indiretas:51},
        {mes:"Julho", vendas:145, diretas:80, indiretas:62},
        {mes:"Agosto", vendas:166, diretas:87, indiretas:48},
        {mes:"Setembro", vendas:103, diretas:56, indiretas:42},
        {mes:"Outubro", vendas:140, diretas:91, indiretas:45},
        {mes:"Novembro", vendas:100, diretas:42, indiretas:33},
        {mes:"Dezembro", vendas:182, diretas:56, indiretas:25}
     ]);
  ]]>
  </mx:Script>

  <mx:Panel title="Análise de Vendas" id="panel1" width="616" horizontalAlign="center">
     <mx:ColumnChart id="myChart" dataProvider="{vendasData}" showDataTips="true" width="100%" height="322">
        <mx:horizontalAxis>
            <mx:CategoryAxis categoryField="mes"/>
        </mx:horizontalAxis>
       
        <mx:series>
                <mx:ColumnSeries yField="vendas"
                    displayName="Estimativa de Vendas"/>
                   
                <mx:ColumnSet type="stacked">
                    <mx:ColumnSeries
                        yField="diretas"
                        displayName="Vendas Diretas"/>
                    <mx:ColumnSeries
                        yField="indiretas"
                        displayName="Vendas Indiretas"/>
                </mx:ColumnSet>
               
        </mx:series>
     </mx:ColumnChart>
     <mx:Legend dataProvider="{myChart}" direction="horizontal"/>
  </mx:Panel>
</mx:Application>

Espero que tenham gostado!