Archive for the ‘Adobe Flex 3’ Category

Flash Builder 4 por fin disponible

Lunes, Marzo 22nd, 2010

-
Por mucho tiempo los desarrolladores que creamos RIA’s con Flash, Flex y/o AIR esperamos la salida del Adobe Flash Builder 4 (antes Adobe Flex) y hoy por fin se dio al ver que en el Labs de Adobe ya te dirigía al sitio oficial. Aunque muchos de nosotros hemos estado jugando con esta versión, la lista de los bug reportados fueron solucionados, obviamente no todos, pero una gran parte si y eso gracias a la colaboración de los desarrolladores que han venido reportando las fallas o problemas.


Lo nuevo de Flash Builder 4

Sin duda una de las mejores cosas que hay en esta versión es la integración a datos de una forma más simple y rápida, utilizando componentes Drag&Drop podrás enlazar tu aplicación frontEnd a servicios proporcionados por Java, PHP, ColdFusion, REST, SOAP, entre otros.

El soporte nativo para Adobe AIR es algo que ya hemos venido comentando en cristalab.

Un pedido a gritos por parte de la comunidad fue la mejora de la refactorización que es tan necesaria y muchas veces nos dio dolor de cabezas, en esta versión fue extremadamente mejorada.

Facilidad en el uso con Adobe Flash Catalyst hizo que el diseño visual de nuestras aplicaciones sean aun mejor, y ahora aumentando su capacidad con la integración de Illustrator, Fireworks o Photoshop.

Gran integración entre Flash y Flash Builder permite a los desarrolladores de componentes personalizados en Flex un flujo de trabajo eficaz.

Entre otros, tenemos el Network Monitor que permite un detallado listado de transmisión de datos que tu aplicación está transfiriendo o utilizando logrando ayudar a la optimización o depuración.

Utilización de línea de comandos para automatizar procesos así como la unidad de pruebas de Flex.

Todos sabemos el uso de WebOrb o amfphp, pero realmente pocos saben el potencial existente en BlazeDS y LiveCycle que es mejorado en Flash Builder 4, eso quiere decir un mejor rendimiento, entre otras cosas.

Los estilos y el aspecto final de nuestras aplicaciones fue altamente reforzado en esta versión.

De hecho podemos encontrar más detalles al respecto o comparando con otras versiones en este link

Si quieres tener más información al respecto puedes visitar el sitio de Adobe Flash Builder 4.

Enviar comentario

Enviar y recibir datos UDP con Adobe AIR 2.0

Miércoles, Marzo 3rd, 2010

En este tip, aprenderemos a enviar información a través de UDP (Universal Datagram Protocol) que es una nueva característica de Adobe AIR 2.0 que nos permitirá enviar y recibir datos, que al ser UDP no garantiza la recepción o entrega, ya que sus cabeceras no llevan el control de datos haciendo ligero los envíos. Al ser así, se puede usar esta nueva característica para aplicaciones con baja latencia y donde la perdida de datos sea indiferente. Por ejemplo puedes utilizar esto con aplicaciones en tiempo real o juegos multiplayer, ejemplos o utilidades pueden haber miles, depende mucho del análisis que hagas.

Para este ejemplo, como ya saben, necesitan tener Flash Builder o eclipse con el SDK de AIR 2.0. De la misma forma puedes ver los anteriores tips que escribí para que te sientas familiarizado con Adobe AIR.

Usaremos la clase DatagramSocket y para eso crearemos una instancia de esta clase. Asimismo, tendremos una variable con la dirección IP que usaremos para la conexión. En este ejemplo usaré la dirección local de mi computador, puedes poner IP de máquinas en tu red y usar el ejemplo en más de un computador.

Código :

private var $__datagramSocket:DatagramSocket;
private var $__IP:String = "127.0.0.1";

Cuando la aplicación se inicie crearemos el constructor de la clase DatagramSocket, agregando un escuchador que permita detectar cuando un mensaje fue recibido:

Código :

$__datagramSocket = new DatagramSocket();
$__datagramSocket.addEventListener( DatagramSocketDataEvent.DATA, dataReceived, false, 0, true );

Adicional a esto el método bind() nos permite establecer la conexión entre el puerto que usaremos y la dirección IP que servirá para dicha conexión. El método receive() es quien nos permite iniciar la escucha de los datos enviados.

Código :

$__datagramSocket.bind( 55555, $__IP );
$__datagramSocket.receive();

El método que usaremos para escuchar cuando los datos lleguen es dataReceived:

Código :

private function dataReceived( event:DatagramSocketDataEvent ):void{
   campo.text = campo.text + "nOtro dice: " + event.data.toString();
}

Para este tip, crearemos un ejemplo básico de chat enviando texto y mostrando la información de otro usuario. Para eso crearemos la siguiente interfaz:

Código :

<s:Label text="Chat:" width="200"  fontWeight="bold"/>
<s:TextArea width="200" height="200" id="campo" text="...INICIA CHAT..." 
         verticalAlign="bottom" editable="false"/>
<s:Label text="Enviar:" width="200"  fontWeight="bold"/>
<s:TextInput id="txtInput" width="200"
          click="{txtInput.text = ''}"
          keyDown="send(event)"
          text="Escribe aqu’ y presiona enter" />

Como ven en el código, al presionar una tecla llamará al método send, nosotros validaremos que sea con ENTER:

Código :

private function send( e:KeyboardEvent = null ):void
{
   if( e.keyCode == Keyboard.ENTER ){
      var data:ByteArray = new ByteArray();
      data.writeUTFBytes(txtInput.text);

      $__datagramSocket.send( data, 0, 0, $__IP, 55554);

      campo.text = campo.text + "nTu dices: " + txtInput.text;
      txtInput.text = "";
   }
}

Es necesario aclarar que los puertos donde se conectarán deben estar libres y no siendo utilizados incluso por varias de esta misma app, además de tener permisos de seguridad. Aquí te dejo el código completo de la app que hice, es sólo un demo pero creo que sirve como base para que puedas crear cosas impresionantes.

Código :

<?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
                  xmlns:s="library://ns.adobe.com/flex/spark"
                  xmlns:mx="library://ns.adobe.com/flex/mx"
                  creationComplete="init()"
                   width="236" height="300">
   <s:layout>
      <s:VerticalLayout horizontalAlign="center" paddingTop="10"/>
   </s:layout>

   
   <fx:Script>
      <![CDATA[
         
         private var $__datagramSocket:DatagramSocket;
         private var $__IP:String = "127.0.0.1";
         
         private function init():void{
            
            $__datagramSocket = new DatagramSocket();
            $__datagramSocket.addEventListener( DatagramSocketDataEvent.DATA, dataReceived, false, 0, true );
            $__datagramSocket.bind( 55555, $__IP );
            $__datagramSocket.receive();
         }
         
         private function dataReceived( event:DatagramSocketDataEvent ):void{
            campo.text = campo.text + "nOtro dice: " + event.data.toString();
         }
         
         private function send( e:KeyboardEvent = null ):void
         {
            if( e.keyCode == Keyboard.ENTER ){
               var data:ByteArray = new ByteArray();
               data.writeUTFBytes(txtInput.text);
               
               $__datagramSocket.send( data, 0, 0, $__IP, 55554);
               
               campo.text = campo.text + "nTu dices: " + txtInput.text;
               txtInput.text = "";
            }
         }
         
      ]]>
   </fx:Script>
   
   <s:Label text="Chat:" width="200"  fontWeight="bold"/>
   <s:TextArea width="200" height="200" id="campo" text="...INICIA CHAT..." 
            verticalAlign="bottom" editable="false"/>
   <s:Label text="Enviar:" width="200"  fontWeight="bold"/>
   <s:TextInput id="txtInput" width="200"
             click="{txtInput.text = ''}"
             keyDown="send(event)"
             text="Escribe aqu’ y presiona enter" />
   
</s:WindowedApplication>

Puedes descargar el demo aquí

Enviar comentario

Acceder a dispositivos de almacenamiento en AIR 2.0

Viernes, Febrero 26th, 2010

Este es uno de los tips de Adobe AIR 2.0 que más me agradó preparar, porque en realidad tenía la necesidad, en un proyecto, de acceder a dispositivos de almacenamientos para leer, editar y grabar información. La clase StorageVolume nos permite leer todos los archivos existentes en nuestro dispositivo de almacenamiento, reconociendo los permisos de los archivos, si son de sistema o no, etc. Por otro lado podemos crear y eliminar directorios, así como también archivos: moviendo, copiando, etc. Recuerda que debes tener Adobe AIR 2.0 configurado en tu Flash Builder o Eclipse

Entonces para este tip, haremos una básica aplicación que permita reconocer cuando se insertó o removió un dispositivo de almacenamiento. Empezaremos creando la interfaz:

Código :

<s:Label id="msg" width="100%" y="20" text="inserta un USB drive" 
          verticalAlign="top" textAlign="center"
          color="#110E91" fontWeight="bold"/>
<mx:Box id="boxContent" width="90%" height="126" x="20"  y="40"/>

Ahora crearemos nuestros listener que estén pendientes de cuando el dispositivo sea agregado o retirado, para eso usaremos una función que será ejecutada al iniciar la aplicación:

Código :

private function init():void{
   StorageVolumeInfo.storageVolumeInfo.addEventListener(StorageVolumeChangeEvent.STORAGE_VOLUME_MOUNT, mount, false, 0, true);
   StorageVolumeInfo.storageVolumeInfo.addEventListener(StorageVolumeChangeEvent.STORAGE_VOLUME_UNMOUNT, unMount, false, 0, true);
}

Y escribimos ahora las funciones que nos avisará en el campo de texto:

Código :

private function mount(e:StorageVolumeChangeEvent):void{
   var myDrive:StorageVolume = e.storageVolume;
   msg.text = "Se conectó: "+ myDrive.name + "  Size: "+Math.round( (myDrive.rootDirectory.spaceAvailable /1073741824) *100)/100 +" Gb";
}
         
private function unMount(e:StorageVolumeChangeEvent):void{
   msg.text = "Se removió: "+ e.rootDirectory.nativePath;
}

Si conectamos un dispositivo:

Si retiramos el dispositivo:

Vemos que nuestro evento StorageVolumeChangeEvent nos avisa cuando un dispositivo fue agregado o removido, este a su vez, nos envía información importante como por ejemplo el nombre, el espacio disponible, que tipo es ("FAT", "NTFS", "HFS" o "UFS"), etc. Pero lo más importante es poder acceder a los archivos que contiene.

Reconocer los archivos

En realidad hasta aquí es el tip de acceso a dispositivos de almacenamiento, pero decidí ampliarlo un poco más reconociendo los archivos que contiene, para eso usaremos la propiedad rootDirectory y recorreremos el dispositivo usando el método getDirectoryListing().

Usaré una función que nos haga el trabajo de reconocer los directorios (carpetas) y archivos para poder guardarlos en un arreglo y usarlo después:

Código :

private var $__arrFile:Array = new Array();

private function readDrive( files:File ):void{
   var arr:Array = files.getDirectoryListing();
   for each (var f:File in arr){
      if (f.isHidden == false){
         if (f.isDirectory){
            trace("Directorio", f.name);
         }else{
            trace("archivo", f.name );
            $__arrFile.push(f);
         }
      }
   }
}

Listo!!!, tenemos guardado en el arreglo todos los archivos que podemos acceder.

¿Y ahora?

Ya tenemos los archivos del dispositivo y podemos basarnos en el anterior tip que hice de Abrir archivos con Adobe AIR 2.0, creando una lista y abriendo con la aplicación por defecto cada file.

Nos valdremos de un componente creado en Flex para poder colocar el nombre del archivo y su id del array. Lo llamaremos MyBox.mxml.

Código :

<?xml version="1.0" encoding="utf-8"?>
<s:Group xmlns:fx="http://ns.adobe.com/mxml/2009"
       xmlns:s="library://ns.adobe.com/flex/spark"
       xmlns:mx="library://ns.adobe.com/flex/mx"
       creationComplete="init()"
       width="200" height="20">
   <s:Label x="10" y="5" width="180" height="15"
          id="campo" buttonMode="true"/>
   
   <fx:Script>
      <![CDATA[
         public var idBox:int;
         
         private function init():void{
            this.addEventListener(MouseEvent.MOUSE_OVER, mouseEvent, false, 0, true);
            this.addEventListener(MouseEvent.MOUSE_OUT, mouseEvent, false, 0, true);
         }
         
         public function setLabel(myText:String):void{
            if(myText.length>=20)
               myText = myText.substr(0,15)+"..."+myText.substr(myText.length-4,4);
            campo.text = myText
         }
         
         private function mouseEvent(e:MouseEvent):void{
            if(e.type == MouseEvent.MOUSE_OVER)
               this.alpha = 0.2;
            else
               this.alpha = 1;
         }
      ]]>
   </fx:Script>
   
</s:Group>

Volvemos a nuestra aplicación y creamos una función que utilice el componente MyBox y haga la lista:

Código :

private function createList():void{
   for (var i:int = 0; i< $__arrFile.length; i++) {
      var box:MyBox = new MyBox();
      box.setLabel( $__arrFile[i].name );
      box.y = 30*i;
      box.idBox = i;
      box.addEventListener(MouseEvent.CLICK, go, false, 0, true);
      boxContent.addElement(box);
   }
}

He creado un listener de Mouse que llama al evento go, que abrirá el archivo con su aplicación por defecto:

Código :

private function go(e:MouseEvent):void{
   var tmp:File = ($__arrFile[e.currentTarget.idBox] as File);
   tmp.openWithDefaultApplication();
}

Y con eso tenemos ya nuestra aplicación funcionando, reconociendo los archivos de tu dispositivo de almacenamiento y al dar click abrir el archivo.

Ingresamos un dispositivo:

Hacemos click a un elemento de la lista, por ejemplo al SWF:

Eso es todo, quería hacer los thumb pero creo que ya salía un poco del tip, pero haré otro con eso :P Aquí les dejo el código completo:

Código :

<?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
                  xmlns:s="library://ns.adobe.com/flex/spark"
                  xmlns:mx="library://ns.adobe.com/flex/mx"
                  creationComplete="init()"
                  width="300" height="200">

   <fx:Script>
      <![CDATA[
         
         private var $__arrFile:Array = new Array();
         
         private function init():void{
            StorageVolumeInfo.storageVolumeInfo.addEventListener(StorageVolumeChangeEvent.STORAGE_VOLUME_MOUNT, mount, false, 0, true);
            StorageVolumeInfo.storageVolumeInfo.addEventListener(StorageVolumeChangeEvent.STORAGE_VOLUME_UNMOUNT, unMount, false, 0, true);
         }
      
         private function mount(e:StorageVolumeChangeEvent):void{
            var myDrive:StorageVolume = e.storageVolume;
            msg.text = "Se conectó: "+ myDrive.name + "  Size: "+Math.round( (myDrive.rootDirectory.spaceAvailable /1073741824) *100)/100 +" Gb";
            readDrive( myDrive.rootDirectory );
         }
         
         private function unMount(e:StorageVolumeChangeEvent):void{
            msg.text = "Se removió: "+ e.rootDirectory.nativePath;
         }
         
         private function readDrive( files:File ):void{
            var arr:Array = files.getDirectoryListing();
            for each (var f:File in arr){
               if (f.isHidden == false){
                  if (f.isDirectory){
                     trace("Directorio", f.name);
                  }else{
                     trace("archivo", f.name );
                     $__arrFile.push(f);
                  }
               }
            }
            createList();
         }
         
         private function createList():void{
            for (var i:int = 0; i< $__arrFile.length; i++) {
               var box:MyBox = new MyBox();
               box.setLabel( $__arrFile[i].name );
               box.y = 30*i;
               box.idBox = i;
               box.addEventListener(MouseEvent.CLICK, go, false, 0, true);
               boxContent.addElement(box);
            }
         }
         
         private function go(e:MouseEvent):void{
            var tmp:File = ($__arrFile[e.currentTarget.idBox] as File);
            tmp.openWithDefaultApplication();
         }
      ]]>
   </fx:Script>
   
   <s:Label id="msg" width="100%" y="20" text="inserta un USB drive" 
          verticalAlign="top" textAlign="center"
          color="#110E91" fontWeight="bold"/>
   <mx:Box id="boxContent" width="90%" height="126" x="20"  y="40"/>
   
</s:WindowedApplication>

Pueden descargar la aplicación aquí.

Enviar comentario

Control de errores globales en Adobe AIR 2.0

Miércoles, Febrero 24th, 2010

En muchas ocasiones cuando trabajamos con aplicaciones pueden aparecer errores no controlados o inesperados, que muchas veces podemos controlar, pero otras fueron realmente imprevistos. Para eso la nueva versión de Adobe AIR implementa un controlador de errores globales (usando AIR 2.0 y FlashPlayer 10.1), con lo cual NO QUIERE DECIR que, nosotros, los desarrolladores no prestemos importancia al control de errores, sino que será una previsión en caso de no entender porque existió un error desconocido. Por ejemplo, podemos usar la clase UncaughtErrorEvent para detectar un error imprevisto y este ser enviado vía email o al servidor para que nosotros lo solucionemos.

Para empezar, como en los demás tips que escribí, usaremos Flash Builder y el SDK de AIR 2.0.

Primero crearemos la interfaz, un botón que obligue al error y un textArea que nos muestre los mensajes:

Código :

<s:Button id="btn" label="Disparar Error"
           click="viewError(event)"
           horizontalCenter="0" top="10"/>
<s:TextArea  id="txtResp"
           horizontalCenter="0" top="40" width="80%" height="160"
           verticalAlign="top" textAlign="left" color="#FF0000"/>

Ahora utilizaremos la clase UncaughtErrorEvent que nos permite controlar los errores globales.

Código :

loaderInfo.uncaughtErrorEvents.addEventListener(UncaughtErrorEvent.UNCAUGHT_ERROR, captureError );

Cabe resaltar que podemos controlar errores del propio main de la aplicación, así como otros generados desde swf externos utilizando:

  • LoaderInfo.uncaughtErrorEvents
  • Loader.uncaughtErrorEvents

Estos deben ser escuchados desde el SWF principal ;)

Siguiendo con el ejemplo, nuestro botón llamará a una función que nos genere el error:

Código :

private function viewError(e:Event):void{
   var foo:String = null;
   trace(foo.length);
}

Este error será escuchado por el evento UncaughtErrorEvent y ejecutará nuestra función captureError:

Código :

private function captureError(e:UncaughtErrorEvent):void{
   txtResp.text = "CaptureError";
   if (e.error is Error){
      var error:Error = e.error as Error;
      txtResp.text +=   "nnError ID: " + error.errorID +
                        "nnError Name: " + error.name +
                        "nnError Message:n" + error.message
   }else{
      var errorEvent:ErrorEvent = e.error as ErrorEvent;
      txtResp.text += "n" + errorEvent.errorID
   }
}

Es una gran ventaja el uso de esta clase, ya que nos permite no sólo poder conocer, vía email por ejemplo, de algún error inesperado, sino que podemos decidir si la aplicación puede o no continuar, ya que muchas veces estos errores impiden el flujo normal de la aplicación.

El código completo está aquí:

Código :

<?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
                  xmlns:s="library://ns.adobe.com/flex/spark"
                  xmlns:mx="library://ns.adobe.com/flex/mx"
                  applicationComplete="init()"
                  width="200" height="230">
   
   <fx:Script>
      <![CDATA[
      
         private function init():void{
            loaderInfo.uncaughtErrorEvents.addEventListener(UncaughtErrorEvent.UNCAUGHT_ERROR, captureError );
         }
         
         private function captureError(e:UncaughtErrorEvent):void{
            txtResp.text = "CaptureError";
            if (e.error is Error){
               var error:Error = e.error as Error;
               txtResp.text +=   "nnError ID: " + error.errorID +
                           "nnError Name: " + error.name +
                           "nnError Message:n" + error.message
            }else{
               var errorEvent:ErrorEvent = e.error as ErrorEvent;
               txtResp.text += "n" + errorEvent.errorID
            }
         }
         
         private function viewError(e:Event):void{
            var foo:String = null;
            trace(foo.length);
         }
         
      ]]>
   </fx:Script>
   
   
   <s:Button id="btn" label="Disparar Error"
           click="viewError(event)"
           horizontalCenter="0" top="10"/>
   <s:TextArea  id="txtResp"
           horizontalCenter="0" top="40" width="80%" height="160"
           verticalAlign="top" textAlign="left" color="#FF0000"/>
   
</s:WindowedApplication>

Puedes descargar aquí el ejemplo.

Enviar comentario

Accede a las DNS desde Adobe AIR 2

Martes, Febrero 23rd, 2010

Entre las novedades de Adobe AIR 2.0 está la posibilidad de obtener el registros de recursos DNS (Domain Name System) gracias a su nueva API DNSResolver, que cuando obtiene los datos solicitados nos dispara un evento DNSResolverEvent. Incluso ahora puedes obtener información de un host IPv4 (32-bits) y IPv6 (64-bits). Aunque existen varios tipos de registros DNS, Adobe AIR implementa sólo 5:

  • ARecord: Clase que devuelve información de 32-bits de la dirección IPv4.
  • AAAARecord: Clase que devuelve información de 64-bits de la dirección IPv6.
  • MXRecord: Clase que proporciona información acerca de un nombre de dominio apuntando a una lista de intercambio de correo (MX).
  • PTRRecord: Se le conoce como "registro inverso", funciona a la inversa del registro A, traduce IP a dominio.
  • SRVRecord: Permite indicar los servicios que ofrece el dominio.

Como ya explicamos en el tip anterior, debemos usar Flash Builder(FB) o eclipse (con el SDK de flex), sumado a esto tenemos que tener el SDK de AIR 2.0.

Entonces, crearemos una instancia de la clase DNSResolver y un listener que nos permita detectar cuando la información llegue a nuestra aplicación, asimismo usaremos un evento ErrorEvent que nos avise en caso ocurra algún problema.

Código :

private var $__dns:DNSResolver;
         
private function init():void{
   $__dns = new DNSResolver();
   $__dns.addEventListener(DNSResolverEvent.LOOKUP, look, false, 0, true);
   $__dns.addEventListener(ErrorEvent.ERROR, error, false, 0, true);
}

Para poder hacer funcionar el DNSResolver usamos:

Código :

$__dns.lookup( "www.google.com" , ARecord );

Eso nos dará como respuesta la ip, por lo menos a la que yo accedo: 74.125.159.106. Dicha información es capturada desde nuestro método look

Código :

private function look (e:DNSResolverEvent):void{
   var records:Array = new Array();
   records = e.resourceRecords;
   //
   txtResp.text = records[0].address;
}

Con esto ya puedes acceder a la IP del dominio que desees, como el resto de respuestas DNS son casi iguales, las implementé en el código y creo que no es necesario explicar, ya que es muy parecido.

Código :

<?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
                  xmlns:s="library://ns.adobe.com/flex/spark"
                  xmlns:mx="library://ns.adobe.com/flex/mx"
                  creationComplete="init()"
                  width="300" height="290">
   <s:layout>
      <s:VerticalLayout/>
   </s:layout>
   
   
   <fx:Script>
      <![CDATA[
         import flash.net.dns.AAAARecord;
         import flash.net.dns.ARecord;
         import flash.net.dns.DNSResolver;
         import flash.net.dns.MXRecord;
         import flash.net.dns.PTRRecord;
         import flash.net.dns.SRVRecord;
         
         private var $__dns:DNSResolver;
         
         private function init():void{
            $__dns = new DNSResolver();
            $__dns.addEventListener(DNSResolverEvent.LOOKUP, look, false, 0, true);
            $__dns.addEventListener(ErrorEvent.ERROR, error, false, 0, true);
         }
         
         private function look (e:DNSResolverEvent):void{
            var records:Array = new Array();
            records = e.resourceRecords;
            //
            if (records[0] is ARecord)
               txtResp.text = "Addr: " + records[0].address;
            
            else if (records[0] is PTRRecord)
               txtResp.text = "PTR: " + records[0].ptrdName;
            
            else if (records[0] is MXRecord){
               txtResp.text = "Exchange: " + records[0].exchange;
               txtResp.text += "nPreference: " +records[0].preference;
            }
            else if (records[0] is SRVRecord)
            {
               var priority:String = "Priority: " + records[0].priority;
               var weight:String = "Weight: " + records[0].weight;
               var port:String = "Port: " + records[0].port;
               var target:String = "Target: " + records[0].target;
               
               txtResp.text += priority + "n" + weight + "n" + port + "n" + target;
            }
         }
         
         private function error(e:ErrorEvent):void{
            txtResp.text = "ERROR: "+e;
         }
         
         private function go():void{
            var type:String = cbType.selectedItem.label;
            switch(type)
            {
               case "ARecord":
                  $__dns.lookup(campo.text, ARecord);
                  break;
               case "AAAARecord":
                  $__dns.lookup(campo.text, AAAARecord);
                  break;      
               case "MXRecord":
                  $__dns.lookup(campo.text, MXRecord);
                  break;
               case "PTRRecord":
                  $__dns.lookup(campo.text, PTRRecord);
                  break;
               case "SRVRecord":
                  $__dns.lookup(campo.text, SRVRecord);
                  break;
            }
         }
         
      ]]>
   </fx:Script>
   
   <mx:HBox horizontalCenter="0"  paddingTop="10" width="300" horizontalAlign="center">   
      <s:Label text="Consultar:" paddingTop="5"/>
      <s:TextInput id="campo" width="200"/>
   </mx:HBox>
   
   <mx:HBox paddingLeft="10" width="300" horizontalAlign="center">
      <mx:ComboBox editable="false" id="cbType">
         <fx:Array>
            <fx:Object label="ARecord" />
            <fx:Object label="AAAARecord" />
            <fx:Object label="MXRecord" />
            <fx:Object label="PTRRecord" />
            <fx:Object label="SRVRecord" />
         </fx:Array>
      </mx:ComboBox>
      <s:Button id="btn" click="go()" label="Ver información"/>
      
   </mx:HBox>
   
   <mx:Box paddingLeft="10" width="300" horizontalAlign="center">
      <s:TextArea id="txtResp" width="90%" height="200" />
   </mx:Box>
   
</s:WindowedApplication>

Puedes descargar la aplicación aquí.

Enviar comentario

Abrir archivos desde Adobe AIR 2.0

Lunes, Febrero 22nd, 2010

-
Cuando Adobe decidió escuchar a los principales desarrolladores o agencias sobre que les gustaría implementar en la nueva versión de Adobe AIR, muchos de ellos querían abrir archivos externos logrando con esto un gran abanico de posibilidades. Con la nueva versión de Adobe AIR 2.0 se puede seleccionar un archivo y abrirlo con su aplicación por defecto, si este archivo no tiene asignado una aplicación con cual poder abrir, mostrará un error. Por otro lado, no puedes acceder a este archivo para manipularlo ya que te saldrá un mensaje de Seguridad (por lo menos no con File, lo veremos en otro tip).

Como ya explicamos en el tip anterior, debemos usar Flash Builder(FB) o eclipse (con el SDK de flex), sumado a esto tenemos que tener el SDK de AIR 2.0.

Vamos a crear un boton en FB para que nos dispare una función:

Código :

<s:Button id="btn" label="Open File" click="openNewFile()" />

La función nos abrirá el browser para poder seleccionar el archivo que deseamos:

Código :

private var $__file:File;
         
private function openNewFile():void{
   $__file = new File();
   $__file.addEventListener(Event.SELECT, fileSelected, false, 0, true);
   $__file.browseForOpen("Buscar archivo a abrir!!");
   //con esto también podemos poner un título a la ventana que abrirá
}

Nuestro evento Event.SELECT será ejecutado cuando ya tengamos aceptado el archivo que seleccionamos.

Código :

private function fileSelected(e:Event):void{
   try {
      //aquí la magia
      $__file.openWithDefaultApplication();
   } catch(error:Error) {
      trace("Problemas con el archivo a abrir");
   }
}

Y listo, con esto podemos probar y ver que logramos abrir un .PSD en Photoshop o un .DOC en Word, siempre y cuando estas apicaciones sean "por defecto".

Aquí el código completo

Código :

<?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
                  xmlns:s="library://ns.adobe.com/flex/spark"
                  xmlns:mx="library://ns.adobe.com/flex/mx" width="356" height="183">
   
   <fx:Script>
      <![CDATA[
         
         private var $__file:File;
         
         private function openNewFile():void{
            $__file = new File();
            $__file.addEventListener(Event.SELECT, fileSelected, false, 0, true);
            $__file.browseForOpen("Buscar archivo a abrir!!");
         }
         
         private function fileSelected(e:Event):void{
            try {
               $__file.openWithDefaultApplication();
            } catch(error:Error) {
               trace("Problemas con el archivo a abrir");
            }
         }
         
      ]]>
   </fx:Script>
   
   
   <s:Button id="btn" label="Open File" click="openNewFile()" horizontalCenter="0" verticalCenter="0"/>
   
   
</s:WindowedApplication>

Descarga la aplicación aquí

Enviar comentario

Multitouch con Adobe AIR 2, Flash Lite 4 y Flash Player 10.1

Lunes, Febrero 22nd, 2010

-
Con las novedades del Flash Player 10.1, Flash Lite 4 y AIR 2.0 existe una que a los usuarios de iphone o macbook les debe ser muy familiar, el multitouch. En este tip enseñaremos como utilizar la clase TransformGestureEvent que nos permite zoom y rotar elementos. Para eso necesitamos Flash Builder con el SDK de AIR 2.0 que ya pueden descargar del sitio Adobe Labs. Cuando Flash CS5 sea liberado, también podrán usarlo para sus aplicaciones.

Una vez configurado el SDK de AIR 2.0, creamos un proyecto FLEX del tipo Desktop (Adobe AIR) y lo primero que haremos es cargar una imagen que usaremos de ejemplo:

Código :

var l:Loader = new Loader();
l.load(new URLRequest("logo.png"));
l.x = l.y = -100;
$__img1 = new Image();
$__img1.addChild(l);
this.addElement($__img1);
$__img1.x = $__img1.y = 200;

Con eso agregamos un elemento a nuestro stage de la aplicación, y ahora pasamos a agregar un evento para que detecte la rotación, usaremos el evento TransformGestureEvent del tipo GESTURE_ROTATE

Código :

this.addEventListener(TransformGestureEvent.GESTURE_ROTATE, _rotation, false, 0, true);

Este evento se dispará cuando se detecte los dos dedos sobre el stage y hagas el movimiento de rotación

Código :

private function _rotation(e:TransformGestureEvent):void{
$__img1.rotation += e.rotation;
}

Ahora, si queremos usar el zoom o escalar los elementos abriendo o cerrando los dedos sobre el stage usamos GESTURE_ZOOM del evento TransformGestureEvent.

Código :

this.addEventListener(TransformGestureEvent.GESTURE_ZOOM, _zoom, false, 0, true);

Y creamos su función:

Código :

private function _zoom(e:TransformGestureEvent):void
{
   if (e.scaleX > 1)
   {
      $__img1.scaleX += e.scaleX / 100;
      $__img1.scaleY += e.scaleY / 100;
   }
   else
   {
      $__img1.scaleX -= e.scaleX / 100;
      $__img1.scaleY -= e.scaleY / 100;
   }
}

Aquí es necesario un IF porque siempre está detectando que el zoom actual es 1 cada vez que interactuas, entonces si juntas los dedos al momento de querer hacer ZoomIN, irá de 1.0, 0.9, 0.8 así sucesivamente.

La aplicación final quedaría así:

Código :

<?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
                  xmlns:s="library://ns.adobe.com/flex/spark"
                  xmlns:mx="library://ns.adobe.com/flex/mx"
                  creationComplete="init()"
                  width="320"
                  height="400">

   <fx:Script>
      <![CDATA[
         import mx.controls.Image;
         private var $__img1:Image;

         //
         private function init():void
         {
            var l:Loader=new Loader();
            l.load(new URLRequest("logo.png"));
            l.x=l.y=-100;
            $__img1=new Image();
            $__img1.addChild(l);
            this.addElement($__img1);
            $__img1.x=$__img1.y=200;
            //
            $__img1.addEventListener(MouseEvent.MOUSE_DOWN, activeMove, false, 0, true);
            $__img1.addEventListener(MouseEvent.MOUSE_UP, activeMove, false, 0, true);
            this.addEventListener(TransformGestureEvent.GESTURE_ROTATE, _rotation, false, 0, true);
            this.addEventListener(TransformGestureEvent.GESTURE_ZOOM, _zoom, false, 0, true);
         }

         private function _rotation(e:TransformGestureEvent):void
         {
            $__img1.rotation+=e.rotation;
         }

         private function _zoom(e:TransformGestureEvent):void
         {
            if (e.scaleX > 1)
            {
               $__img1.scaleX+=e.scaleX / 100;
               $__img1.scaleY+=e.scaleY / 100;
            }
            else
            {
               $__img1.scaleX-=e.scaleX / 100;
               $__img1.scaleY-=e.scaleY / 100;
            }
         }

         private function activeMove(e:MouseEvent):void
         {
            if (e.type == MouseEvent.MOUSE_DOWN)
               $__img1.startDrag();
            else if (e.type == MouseEvent.MOUSE_UP)
               $__img1.stopDrag();
            else
               throw new Error("upsss!!!");
         }
      ]]>
   </fx:Script>
</s:WindowedApplication>

Puedes descargar el ejemplo aquí, necesitas tener el runtime de AIR 2

Enviar comentario

El futuro de Flash

Jueves, Febrero 11th, 2010

Desde que el iPad fue anunciado, sin Flash, mucha gente ha estado especulando su caída. Se suma el experimento de Youtube con HTML 5 y el tag <video>. Tal fue el hype generado por la supuesta muerte, que dedicamos un capítulo de MejorandoLaWeb al tema.

El mundo de Flash está cambiando. La computación está cambiando. La web está cambiando. Pero Flash no morirá y si Adobe toma las decisiones correctas, incluso puede convertirse en el líder de campos que no esperamos. Este año será decidido todo.

Flash, HTML 5, Javascript y el video

HTML 5 tiene cosas impresionantes. Drag and Drop de archivos al navegador, animaciones vectoriales en SVG, un tag para embeber video, otro tag para audio, acceso a disco y geolocalización por múltiples métodos (GPS, IP, manual, etc). Esto ha dado pie a que muchos digan que "Flash es innecesario".

Molly Holzschlag es una de las personas que más sabe de estándares en el mundo y compiló una lista de capacidades de HTML 5 y los navegadores que las implementan. No es mala la implementación, pero usarla en producción es triste.

El video es un problema "emocional". Algunos quieren OGG como formato, otros H.264 y otros algo completamente diferente. Ahora mismo, el tag <video> tiene la misma versatilidad de las epocas del Real Player.

Cosas que hace Flash que no puede hacer HTML5 o JS ni hay planes para que pueda

  • Streaming: UStream, Tinychat, Livestream, incluso Youtube live, inviables con "estándares".
  • Animación vectorial compleja: SVG? CSS3? Jajajaja. Si tu crees que hacer animaciones con esas tecnologías es más fácil, igual que Flash y gasta menos CPU, no has comparado a nivel técnico. La realidad es que actualmente, sólo Flash lo permite como debe ser. El resto de animaciones en SVG, CSS3 o JS gastan demasiada CPU y no hay un software del nivel de Flash para crearlas.
  • Edición y manipulación de audio: AS3 es capaz de mezclar audio en tiempo real. Nadie más puede hacerlo al nivel de AS3.
  • Edición bit por bit de mapas de bits: Aviary, Picnik y Photoshop Online hechos en Flash y Flex lo demuestran. ¿En HTML5 o JS? Ninguno realmente usable.
  • 100% de compatibilidad a través de todas las plataformas: Si dices que es posible escribir un sólo código HTML5/CSS/JS que funcione en todos los navegadores ahora mismo, no has hecho nada profesional. En SWF es normal

Flash no es sólo el player. Flash CS5 es un entorno integrado con la capacidad de diseñar, dibujar, animar, incluir video, audio, editar todos estos componentes, agregar interactividad y programación de alta complejidad, compilar para desktops, móviles o iPhones. Ninguna herramienta del lado "estándar abierto" ofrece ese nivel de integración ahora mismo. No Dreamweaver, no Visual Studio 2010, no Aptana, ni siquiera una combinación de varias.

Flash, teléfonos móviles, Apple

El mundo móvil es diferente. El iPhone cambió el mundo y estableció una fuerte tendencia a las tiendas de aplicaciones, la integración de HTML 5 actualizado en el teléfono e ignorar a Flash. Android y otros siguieron el mismo camino.

Este mes en el Mobile World Congress, Adobe presentará el Flash Player 10.1 para todos los teléfonos móviles (excepto iPhone), un compilador especial para crear apps de iPhone con SWF desde Flash CS5 y el secreto a gritos, una forma unificada de desarrollar apps "pseudo-nativas" para Android, WebOS, Blackberry y Symbian, con un mismo código.

Flash ya era usado por el 90% de la humanidad conectada antes de Youtube. Las animaciones y los juegos en linea lograron posicionar a Flash en el principio. No hay razón para pensar que no pasará a nivel móvil. Ninguna empresa tiene un entorno tan avanzado para el desarrollo de juegos móviles como Adobe con Flash. Y pasará en todas las plataformas de teléfonos actuales (En tu Nokia 1100 puede que no)

¿Y Flex? ¿Y Adobe AIR?

Flex es líder en desarrollo de RIAs. No se nota mucho su presencia en la web, pero está en muchas intranets y empresas. Aun no tengo claro si Flex Mobile hará un impacto tan fuerte como Flash/AIR Mobile, pero su presencia como la mejor y más veloz herramienta para aplicaciones web se mantendrá, a pesar de que HTML 5 y jQuery UI son amenazas muy reales y fuertes.

Adobe AIR tiene que evolucionar. El uso de archivos .air y "badge installers" es simplemente estúpido. No está mal que los ofrezcan, pero también incluyan la opción de crear instaladores por SO. Si el rumor de AIR Mobile es cierto y cometen una cagada como la de los .air, Flash no será el sueño que esperábamos. Esperemos que Adobe tome la decisión correcta.

Flash "estándar", Flash Open Source

Flash es estándar en el mismo sentido que los .doc y .docx lo son. Es una realidad. No es algo malo de por sí, pero muchos no se sienten cómodos. Flash tiene abiertas las especificaciones del formato SWF, pero prohíben crear "players" alternativos con esas specs ¿Por qué? Según ellos, para proteger la segmentación del mercado y mantener un sólo player. Yo solía creer en esto.

Ya no lo creo más. La pésima forma en la que Adobe ha manejado las plataformas móviles (i-Mode -> Flash Lite con AS0.5 -> muerte de Flash Lite -> vacío -> ¿10.1?) , sumado a la lección de Android que se puede tener una distro oficial open source sin perder el control me lo deja claro. Adobe debería, sin duda, liberar el código del Player y permitir que la comunidad "ayude". Si Adobe no puede implementar bien Flash en Linux y Mac, la comunidad sí podrá. ¿El miedo es que Microsoft los mate como mató a Java con una maquina virtual especial? Adobe ya está grande y debe poder superar esto. Con su penetración, pueden mantener el control de un player abierto.

Si no, quizás y el 2022, cuando HTML 5 será un estándar cerrado y aprobado, será realmente el declive de Flash.

NOTA: Si "odias Flash", por favor cita razones técnicas para odiarlo.
Si crees que estoy equivocado y Flash morirá, di tus razones técnicas o políticas por la que lo crees.
Si no tienes razones y es sólo porque "no te gusta", reflexiona.
Lee los comentarios, han aportado mucho al tema.

Enviar comentario

Obtener informaci�n de la direcci�n URL desde Actionscript 3

Miércoles, Mayo 27th, 2009

En este Tip les dejo una clase que encontr� el otro d�a perdida por el disco r�gido (y que justo andaba necesitando algo igual). Se trata de una clase que se encarga de proveer a Flex datos provenientes de la URL de la aplicaci�n, o sea, la url, el path relativo, el puerto, etc.

Código :

package com.alfathenus.core.utils
{
   import flash.external.ExternalInterface;

   import mx.core.Application;
   import mx.collections.ArrayCollection;
   import mx.controls.Alert;
   /**
    *
    * Clase que maneja el window del browser de la aplicacion
    *
    */
   public class HTTPUtil
   {
      //--------------------------------------------------------------------------
      // Constructor
      //--------------------------------------------------------------------------
      /**
       *
       * Construcgor
       *
       */
      public function HTTPUtil()
      {
      }
      /**
       *
       * Me devuelve la url de la p�gina
       *
       */
      public static function getUrl():String
      {
         return ExternalInterface.call( "window.location.href.toString" );
      }
      /**
       *
       * Me devuelve el host de la pag
       *
       * @example: http://www.flexpasta.com/?x=1&y=2 => me devuelve => www.flexpasta.com
       *
       */
      public static function getHostName():String
      {
         return ExternalInterface.call( “window.location.hostname.toString” );
      }
      /**
       *
       * Me devuelve el protocolo (http:, https:, etc)
       *
       */
      public static function getProtocol():String
      {
         return ExternalInterface.call( “window.location.protocol.toString” );
      }
      /**
       *
       * Me devuelve el puerto de la aplicaicon
       *
       */
      public static function getPort():String
      {
         return ExternalInterface.call( “window.location.port.toString” );
      }
      /**
       *
       * Me devuelve el path relativo a la aplicacion
       *
       * @example http://www.ejemplo.com/test?x=1&y=2 => devuelve => /test
       */
      public static function getContext():String
      {
         return ExternalInterface.call( “window.location.pathname.toString” );
      }
      /**
       *
       * Me devuelve el valor de un par�metro del QueryString
       *
       */
      public static function getParameterValue(key:String):String
      {
         var value:String;
         var uparam:String = ExternalInterface.call( “window.location.search.toString” );

         if(uparam == null)
         {
            return null;
         }
         var paramArray:ArrayCollection = new ArrayCollection( uparam.split( ‘&’ ) );
         for(var x:int = 0; x < paramArray.length ; x++)
         {
            var p:String = paramArray.getItemAt( x ) as String;
            if(p.indexOf( key + ‘=’ ) > -1)
            {
               value = (p.replace( (key + ‘=’) , ” )).replace( ‘?’ , ” );
               x = paramArray.length;
            }
         }

         return value;
      }
   }
}

Se aceptan como siempre sugerencias para su mejora.

Saludos!

Enviar comentario

Abrir y guardar archivos de manera usable en AIR

Domingo, Octubre 12th, 2008

Una de las grandes virtudes de Adobe AIR es ser multiplataforma. Sin embargo, esto hace que cosas como el manejo de los diálogos del sistema de archivos tengan que adaptarse a Windows, Mac y Linux, y no ofrezcan tantas funciones como sería de esperar. Por ejemplo, cada vez que abrimos un diálogo para abrir o guardar un archivo, por defecto se abre en la localización en que dejamos la ventana anterior (si le damos un objeto File nuevo, y sin ruta definida), aunque eso no siempre sea lo más conveniente.

Por ejemplo, imaginemos que estamos haciendo algún tipo de editor, que cargue archivos fuente (digamos, por ejemplo, archivos txt), y genere algún tipo de archivo de salida (como un jpg en el que insertamos el texto que hemos cargado). No nos interesa que al abrir el diálogo de guardar archivo tengamos que encontrar la carpeta donde dejamos la salida (pongamos D:/trabajos/jpgtxt/ ), y cada vez que queremos cargar tengamos que buscar el directorio de las fuentes (que puede ser C:\Documents and Settings\usuario\Mis documentos\fuentes), desde el de las salidas.

Para solucionar esto, haremos una clase que se encargue de memorizar mediante SharedObjects las rutas en las que guardamos los diferentes archivos, y de paso, un par de clases que encapsulen el proceso de cargar y guardar datos desde los diálogos del sistema, ya que realmente lo único que nos interesa es cargar un ByteArray y guardar un ByteArray, ya que al fin y al cabo, son estos objetos los que contienen la información útil de los archivos. De esta manera, podríamos recordar tanto la ruta del archivo que guardamos, como la del que cargamos, y tenerla disponible cada vez que abrimos el programa. Además nos ahorraríamos preocupaciones sobre cómo se cargan y guardan archivos, y trabajaríamos únicamente con los byteArrays que contiene los datos.

Así pues, la clase PathSaver, sería algo así:

Código :

package com.zigzah.utils
{
   import flash.events.Event;
   import flash.filesystem.File;
   import flash.net.SharedObject;
   
   /**Class to manage the file paths in a more usable way.*/
   public class PathSaver
   {
      
      private static var so:SharedObject;

      /** Returns a file from the location it was saved the last time.
       * @param id The id of the given file.
       * @param returnName if true, the function returns name of the file, else, it returns the folder.
       * @param newName if returnName is set to false, the new name of the file.
       * @return A File object, with the path it had the last time it was saved. */
            
      public static function getFile (id:String , returnName:Boolean = false , newName:String = ""):File
      {
         /*Obtenemos el SharedObject,
         y sacamos la ruta del archivo y la del directorio.*/
         so = SharedObject.getLocal(id + "$file");
         var path: String = so.data.path;
         var parent: String = so.data.parent;
         var file:File = new File ();
         
         /*Devolvemos un nuevo archivo según si existía el so,
          y según los parámetros.*/
         if (path && parent)
         {
            if (returnName)
            {
               file = file.resolvePath(path);
            }else if (newName != "")
            {
               file = file.resolvePath(parent).resolvePath(newName);
            }else {
               file = file.resolvePath(parent)
            }
         } else
         {
            file = File.desktopDirectory.resolvePath(newName);
         }
         
         /*Indicamos que cada vez que el archivo se guarde o abra,
         su nueva ruta quede almacenada en el so.*/
         file.addEventListener(Event.SELECT, savePath);
         
         return file;
      }
      
      
      /** This function saves the file path in a sharedObject. */
      private static function savePath (event:Event) :void
      {
         var file:File = event.target as File;
         so.data.path = file.nativePath;
         so.data.parent = file.parent.nativePath;
         so.flush();
      }

   }
}

Con esta clase, cada vez que necesitamos llamar a una archivo cuya ruta almacenó el usuario, en vez de crear el objeto File con new File (); usamos PathSaver.getFile ("IDDelArchivo");. Además, jugando con los otros parámetros, podemos conseguir distintas funcionalidades, como conseguir la ruta absoluta del archivo, o la de la carpeta que lo contiene, y obtener un nuevo nombre por defecto, que puede ser útil para guardar (como veremos en el ejemplo de abajo).

Veamos ahora una clase para cargar archivos desde el diálogo de Abrir, que hace uso de esta clase, y devuelve simplemente un ByteArray con los datos cargados:

Código :

package com.zigzah.utils
{
   import flash.events.Event;
   import flash.events.EventDispatcher;
   import flash.filesystem.File;
   import flash.filesystem.FileMode;
   import flash.filesystem.FileStream;
   import flash.net.FileFilter;
   import flash.utils.ByteArray;
   
   /**Dispatched when the user selects a file from the fileSystem,
    * and the bytearray from the file is ready.*/
   [Event(name="bytesloaded", type="com.zigzah.utils.LoadFileEvent")]
   
   /** A utility class to get files easily from the filesystem  */
   public class FileLoader extends EventDispatcher
   {
      private var file :File
      private var filter:FileFilter
      
      /**The title of the open dialog window*/
      public var openDialog : String = "Open File";
      
      
      /**Creates a new FileLoader object, with the specified filefilter and
       * default path, in the open dialog
       * @param filter the filefilter object that will be used to browse for files in the filesystem.
       * @param fileID the id that PathSaver will to get a file.*/
      public function FileLoader(filter : FileFilter , fileID : String = "$defaultSaveFile")
      {
         file = PathSaver.getFile(fileID);
         this.filter = filter;
         file.addEventListener(Event.SELECT, makeBytes);
      }
      
      /** Opens the browse dialog. When the user chooses a file, a
       * LoadFileEvent will be dispatched, and it will contain the bytearray
       * retrieved by the file.
       * @see LoadFileEvent*/
      public function getBytes ():void
      {
         file.browseForOpen(openDialog ,[filter]);
      }
      
      /**Returns a ByteArray, for the LoadFileEvent.*/
      private function makeBytes (event:Event) :void
      {
         var fs:FileStream = new FileStream ();
         fs.open(event.target as File, FileMode.READ);
         var ba:ByteArray = new ByteArray ();
         fs.readBytes(ba, 0, fs.bytesAvailable);
         fs.close();
         var ev:LoadFileEvent = new LoadFileEvent();
         ev.bytes = ba;
         ev.fileName = file.name;
         this.dispatchEvent (ev);
      }

   }
}

Ésta clase también requiere LoadFileEvent, que no es más que:

Código :

package com.zigzah.utils
{
   import flash.events.Event;
   import flash.utils.ByteArray;

   public class LoadFileEvent extends Event
   {
      public var bytes : ByteArray;
      public var fileName : String;
      
      public static const BYTES_LOADED: String = "bytesloaded";
      
      
      public function LoadFileEvent(/*type:String, bubbles:Boolean=false, cancelable:Boolean=false*/)
      {
         super(LoadFileEvent.BYTES_LOADED, false, false);
      }
      
   }
}

Y ahora, otra clase para guardar ByteArrays, mediante el diálogo:

Código :

package com.zigzah.utils
{
   import flash.events.Event;
   import flash.filesystem.File;
   import flash.filesystem.FileMode;
   import flash.filesystem.FileStream;
   import flash.utils.ByteArray;
   
   /**A simple class that lets the user save a byteArray in a file,
    * and uses PathSaver to save the file location.*/
   public class FileSaver
   {
      private var ba:ByteArray
      
      /**The title of the save dialog window.*/
      public var dialog : String = "Save File";
      
      /**Constructor.
       * @param bytes The byteArray that will be saved.*/
      public function FileSaver(bytes:ByteArray)
      {
         this.ba = bytes;
         
         
      }
      
      /**Opens a save dialog.
       * @param name the default name of the file.
       * @param fileID the id that will be used by PathSaver.*/
      public function save (name:String , fileID:String = "$defaultFile"):void
      {
         var file:File = PathSaver.getFile(fileID , false , name);
         file.addEventListener(Event.SELECT,saveBytes);
         file.browseForSave(dialog);
         
      }
      
      /**Writes the given bytes in the fileSystem.*/
      private function saveBytes (event:Event):void
      {
         var file:File = event.target as File;
         var fs:FileStream = new FileStream();
         fs.open(file , FileMode.WRITE)
         fs.writeBytes(ba);
         fs.close();
      }

   }
}

Para probarlo, podríamos hacer un simple editor de texto en Flex/AIR. Fíjense en que recuerda la ruta tanto de los archivos que cargan como de los que guardan (para eso, pruébenlo varias veces):

Código :

<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
   <mx:Script>
   <![CDATA[
      import com.zigzah.utils.*;
      
      [Bindable]
      private var text:String = "";
      
      private function load (event:Event):void
      {
         var ff:FileFilter = new FileFilter ("txt" , "*.txt");
         var fldr:FileLoader = new FileLoader (ff , "loadedTXT");
         fldr.getBytes();
         fldr.addEventListener(LoadFileEvent.BYTES_LOADED,loadHandler);
      }
      
      private function loadHandler (event:LoadFileEvent):void
      {
         
         var ba:ByteArray = event.bytes;
         text = ba.readUTFBytes(ba.length);
      }
      
      private function save (event:Event):void
      {
         var ba:ByteArray = new ByteArray();
         ba.writeUTFBytes(ta.text);
         var fsaver:FileSaver = new FileSaver (ba);
         fsaver.save("newtxt.txt","savedTXT");
      }
   ]]></mx:Script>
   
      
   <mx:Button label="Load txt" click="load(event)" top="80" left="30"/>
   <mx:Button label="Save txt" click="save(event)" right="30" top="80"/>
   <mx:TextArea id="ta" text="{text}" right="30" left="30" bottom="10" top="120"/>
   
</mx:WindowedApplication>

Aquí pueden descargar los archivos del proyecto:


[Archivos]

Enviar comentario