Comunidad de diseño web y desarrollo en internet online

Integrar Flash CS3 y Flex para crear componentes y comportamientos

Muchos de los desarrolladores que venían de trabajar largo tiempo en Flash posiblemente les pase lo mismo que a mi: extrañan la libertad que teníamos en Flash a la hora de posicionar los elementos gráficos, disparar acciones, modificar elementos, etc. La relativa libertad que disponíamos se ve constreñida por las necesidades del framework de Flex. Para superar cuestiones gráficas, por ejemplo, rápidamente nos hicimos con los estilos (muy rápido si trabajaban con CSS) y ya muchas cosas se hicieron posibles, pero claro, no todas.

En líneas generales, muchas veces necesitamos hacer cosas que el framework de Flex no facilita (o no contempla), y para eso una posible solución puede ser usar Flash en combinación con Flex, para cosas pequeñitas como el ejemplo que viene a continuación, como para cosas mas importantes, como el ejemplo del final. Pero vamos a ello

Problema

Cliente: ¿No se podría poner un ícono para agrandar esa ventana?
Desarrollador: Si, claro, la ponemos arriba a la izquierda, al lado del título

Subtexto desarrollador: ese es un componente Panel, le puedo poner un titleIcon, pero no hay como alinearlo a la derecha

Cliente: No, pero mejor a la izquierda, igual que las ventanas de Guindows
Desarrollador: Ya, claro ... (arrggghhhhh)

Por supuesto, la máxima del desarrollo es que siempre hay una forma. Si en la documentación del componente Panel no indica que se pueda alinear el titleIcon, pues a rebuscar. F1 ... buscar ... F1 .... buscar ... Google .... buscar .... Google ... buscar .... arghh ... no encuentro (ni siquiera preguntando en el foro de Cristalab)

Bueno, no se encuentra, y esta es la situación:

Ok, no encuentro una propiedad que me permita alinear el ícono hacia el otro lado, aunque posiblemente exista y solo mi torpeza impida encontrarla. De todas formas se me ocurre una forma de arreglarlo: usando un swf como ícono que contenga código que arregle el asunto ... y de paso hacer un pequeño hack a los estilos de Flex

Para seguir los ejemplos aconsejo descargar los archivos de ejemplo

Aquellos más experimentados pueden saltearse el proceso paso a paso e ir directamente al resúmen

Preparando la película de Flash

Abro Flash CS3 y procedo a pintar mi botoncito en dos frames, para agrandar y achicar respectivamente,el layout queda así, con un MovieClip en cada frame

Claro ... pero tengo que hacer funcionar esto como un botón y hacer que se posicione a la derecha. Empiezo poniendo un stop en el primer frame (layer actions) y publico para embeberlo como ícono en Flex. Para simplificar la cosa, lo haré en un mxml nuevo, con solo un panel en el objeto Application que cargará este swf como ícono. El code queda así:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
    <mx:Panel x="200" y="90" 
        width="250" height="200"
        titleIcon="{bigWindow}">
    </mx:Panel>
    <mx:Script>
        <![CDATA[
            [Embed(source=/bigWindow2.swf")]        
            public var bigWindow:Class
        ]]>
    </mx:Script>
</mx:Application> 

Da un warning del binding, pero ¡compila y funciona! El tag Embed permite poner imágenes externas embebidas en una página, que debe ser identificada con Class para poder ser referenciada en el código.

Comportamiento de botón

Bien tengo mi ícono pero dos cosas me faltan:

  1. Hacer que cambie el ícono entre ambos frames
  2. Hacer que se coloque a la derecha
  3. Hacer que agrande realmente la ventana

Lo primero se me antoja bastante fácil. Vamos a darle el evento para que se mueva de fotograma :

//frame1
full_btn.addEventListener(MouseEvent.CLICK, goFull);
function goFull(evt:MouseEvent):void {
    gotoAndStop(2);
}
stop();
//frame 2
small_btn.addEventListener(MouseEvent.CLICK, goSmall)
function goSmall(evt:MouseEvent):void{ 
    gotoAndStop(1)
}

Ok, con esto es suficiente para que se mueva entre los frames. Publico y ... ¡funciona! Esto no fue tan difícil

Maximización del componente

Para poder interactuar con Flex, tengo que saber como hablarle a mi contenedor, con lo cual la primer tarea será averiguar la ruta (¿en cuantos contenedores estará metido?) Lo lógico en AS2 hubiera sido poner trace(this) al comienzo del swf, pero la salida en AS3 es:

[object MainTimeline]

Eso no ayuda con la ruta, así que pondremos trace(parent) para ver que sale (publicando como debug desde Flex, vemos los trace en el panel de salida)

[object Loader]

Vamos avanzando. Repetimos la prueba hasta que vemos que tenemos que trepar cuatro nodos para llegar al contenedor principal Application

TIP: Depende de como se tenga configurado Flex, es muy probable que al publicar en modo debug, los cambios que hacen en el swf no se reflejen. Esto es porque hay un cache interno a evitar (no recompila el embed) lo cual se puede hacer cambiando el identificador entre publicaciones, en este caso sería:

titleIcon="{bigWindow2}

[Embed(source="images/bigWindow2.swf")]
public var bigWindow2:Class

Luego lo incrementan a 3 y así sucesivamente

Para comprobar que podemos llamar a una función desde nuestro botón, agregamos esta functión en nuestro mxml

public function goFullSisze():void{
    trace("goFullSize")
}

Luego en flash modificamos el code del botón para que la llame:

function goFull(evt:MouseEvent):void { 
    this.parent.parent.parent.parent.goFullSisze()
    gotoAndStop(2);
}

Publicamos ... pero ooooooh, sale:

1061: Call to a possibly undefined method goFullSisze through a reference with static type flash.display:DisplayObjectContainer.

Vaya, el maldito error 1061. Tiene su lógica, ya que estamos llamando a una función que dentro del contexto de nuestro archivo de Flash no existe. Pero para evitarlo y lograr compilar, podemos "relajar" un poco el compilador y sacar el strict mode desde Archivo, Opciones de Publicación, Flash Tab, Settings

De esta forma nos permite compilar, aún llamando a un método que no existe al momento de publicar. Compilamos, copiamos el swf a la carpeta bin-debug, actualizamos el nombre del embed (voy por bigWindow4 a esta altura) para evitar el cache y bingo, aquí el trace que buscábamos

¡Hemos llegado a invocar la función en nuestra aplicación! Ahora solo queda darle funcionalidad. Para ello agregamos un estado (State) a nuestra aplicación, para lo cual abrimos el panel de States (menú Window->States), le damos al botón de New State y le ponemos de nombre fullPanel. Seleccionamos el estado y en modo visual agrandamos el panel para que cubra todo el escenario. Mi mxml queda así:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
    <mx:states>
        <mx:State name="fullPanel">
            <mx:SetProperty target="{panel1}" name="width" value="653"/>
            <mx:SetProperty target="{panel1}" name="height" value="412"/>
            <mx:SetProperty target="{panel1}" name="x" value="10"/>
            <mx:SetProperty target="{panel1}" name="y" value="10"/>
        </mx:State>
    </mx:states>
    <mx:Script>
        <![CDATA[
            [Embed(source="bigWindow.swf")]
		    //voy incrementando bigWindowX en cada publicación para evitar el cache        
            public var bigWindow8:Class
            
            public function goFullSize():void{
                this.currentState = "fullPanel"
            }
            public function goNormalSize():void{
                this.currentState = ""
            }
        ]]>
    </mx:Script>
    <mx:Panel x="200" y="90" 
        width="250" height="200"
        titleIcon="{bigWindow8}" id="panel1">
    </mx:Panel>   
</mx:Application>

Luego reescribimnos goFullSize y de paso goNormalSize (que sería cuando vuelve al tamaño normal) de esta forma

public function goFullSize():void{
        this.currentState = "fullPanel"
}
public function goNormalSize():void{
        this.currentState = ""
}

En el segundo frame del Flash agregamos

small_btn.addEventListener(MouseEvent.CLICK, goSmall)
function goSmall(evt:MouseEvent):void{ 
	this.parent.parent.parent.parent.parent.goNormalSize()
	gotoAndStop(1)
}

Publicamos todo y el panel se agranda y achica perfectamente. Ya solo nos falta alinearlo

Alineación del ícono

Ya solo nos falta alinear el ícono a la derecha. Para ello deberíamos encontrar el tamaño del contenedor y modificar nuestra posición x según el ancho de este (nuestro Panel) Sabemos que tenemos 4 saltos hasta Application, con lo cual tenemos 3 hasta el Panel. Vamos a agregar code a nuestro al principio del primer frame:

Al principio del primer frame

try {
    full_btn.x = parent.parent.parent.width - (full_btn.width*2);
} catch(e:Error){}

Al principio del segundo

try {
 small_btn.x = parent.parent.parent.width - (small_btn.width*2);
} catch(e:Error){}

Usamos un bloque try/catch para evitar recibir un notice al compilar, ya que inmediatamente está intentando acceder a una propiedad que no existe en tiempo de compilación. Volvemos a compilar nuestro archivo en Flex y voilá, nuestro ícono se posiciona perfectamente.

Resúmen

Problema: queremos alinear un ícono de un componente Panel a la derecha y además hacer que funciona como botón de fullscreen

1. Creamos el botón en Flash (ver archivo bigWindow.fla) en dos frames para los dos estados
2. Agregamos código que accede a la aplicación Flex "trepando" por el DisplayList:

//Frame 1
full_btn.addEventListener(MouseEvent.CLICK, goFull);
try { //accedo al componente Panel para posicionarme a la derecha
    full_btn.x = parent.parent.parent.width - (full_btn.width*2);
} catch (e:Error) {}
function goFull(evt:MouseEvent):void {
    //Llamo a una función en Application
    this.parent.parent.parent.parent.parent.goFullSize();
    gotoAndStop(2);
}
stop();

//frame 2

small_btn.addEventListener(MouseEvent.CLICK, goSmall);
try { //accedo al componente Panel para posicionarme a la derecha
    small_btn.x = parent.parent.parent.width - (small_btn.width*2);
} catch (e:Error) {}
function goSmall(evt:MouseEvent):void {
    //Llamo a una función en Application
    this.parent.parent.parent.parent.parent.goNormalSize();
    gotoAndStop(1);
}

3. Nuestra aplicación Flex usa este botón como titleIcon de nuestro Panel, permitiendo hacer que un ícono funcione realmente como un botón posicionable, llamando a una función dentro del ámbito de Application que cambia el state. Aquí nuestro mxml

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
    <mx:states>
        <mx:State name="fullPanel">
            <mx:SetProperty target="{panel1}" name="width" value="653"/>
            <mx:SetProperty target="{panel1}" name="height" value="412"/>
            <mx:SetProperty target="{panel1}" name="x" value="10"/>
            <mx:SetProperty target="{panel1}" name="y" value="10"/>
        </mx:State>
    </mx:states>
    <mx:Script>
        <![CDATA[
            [Embed(source="bigWindow.swf")] 
            public var bigWindow:Class
            
            public function goFullSize():void{
                this.currentState = "fullPanel"
            }
            public function goNormalSize():void{
                this.currentState = ""
            }
        ]]>
    </mx:Script>
    <mx:Panel x="200" y="90" 
        width="250" height="200"
        titleIcon="{bigWindow}" id="panel1">
    </mx:Panel>   
</mx:Application>

Conclusión

Si bien el ejemplo es bastante simple, la posibilidad de interactuar desde Flash con Flex de una forma no convencional (al framework) introduce muchas posibilidades para darle mayores posibilidades a una aplicación, sea simplemente cargando el swf desde un SWFLoader o como en este caso, embebiéndolo. Cualquiera sea la forma en que incorporemos el swf, nada impide que este ejecute código ... aunque también hay que ser cuidadoso con esto, ya que podríamos romper nuestra aplicación.

Como ejemplo de una utilización mas avanzada de este principio, pueden echar un ojo al configurador de este jukebox de video desarrollado en Flex. El configurador es un archivo de Flash, en donde cargo la aplicación de Flex que a su vez tiene un API para cambiar su aspecto runtime. Sería este mismo principio (Flex-Flash), pero al revés, ya que el contenedor es un archivo de Flash.

Aquí puedes verlo funcionando.

¿Sabes SQL? ¿No-SQL? Aprende MySQL, PostgreSQL, MongoDB, Redis y más con el Curso Profesional de Bases de Datos que empieza el martes, en vivo.

Descargar Archivo

Publica tu comentario

El autor de este artículo ha cerrado los comentarios. Si tienes preguntas o comentarios, puedes hacerlos en el foro

Entra al foro y participa en la discusión

o puedes...

¿Estás registrado en Cristalab y quieres
publicar tu URL y avatar?

¿No estás registrado aún pero quieres hacerlo antes de publicar tu comentario?

Registrate