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:
- Hacer que cambie el ícono entre ambos frames
- Hacer que se coloque a la derecha
- 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.
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?
Inicia sesión
¿No estás registrado aún pero quieres hacerlo antes de publicar tu comentario?
Registrate