Flash CS4 y AS3. Problemas al parsear datos desde XML a un texto dinámico anidado mediante clase
Buenas a todos.
Espero que alguien pueda echarme un cable con este pequeño problema que estoy teniendo con Flash.
En estos momentos estoy rediseñando mi portfolio y he decido que en la sección de trabajos quiero presenar mis proyectos por categorías mediante un texto dinamico que cargara los titulos y los links de las imagenes desde un XML para hacer más sencilla su actualización.
Mi conflicto surge al intentar parsear el contenido de los títulos de los proyectos de ese XML hacia Flash, ya que si bien me instancia todos los subnodos del XML correctamente en la lista de display (donde uso un botón que a su vez usa una clase .as que creé para el efecto rollover) al intentar pasar esa información del título me tira este error:
TypeError: Error #1010: Un término no está definido y no tiene propiedades.
at menuMXL_fla::MainTimeline/xmlComplete()
at flash.events::EventDispatcher/dispatchEventFunction()
at flash.events::EventDispatcher/dispatchEvent()
at flash.net::URLLoader/onComplete()
Creo que el error estaría en la clase que usa el botón ya que si comento la linea que se encarga de parsear el título en mi .fla los subnodos son correctamente instanciados. He probado crear una variable publica dentro de la clase que defina el contenido del _txt dinámico del boton como TexField, pero de esa manera la clase deja de funcionar y me tira errores de sintaxis.
No sé muy bien por donde pillarlo así que aquí os dejo el código de mi Flash y mi clase .as de botón por si alguien supiera donde esta nuestro error sin solución, si fui yo el culpable, o fueron los 80.
Gracias de antemano.
Codigo en el fla:
var loader:URLLoader = new URLLoader();
loader.addEventListener(Event.COMPLETE, xmlComplete);
loader.load(new URLRequest("xml/datos.xml"));
function xmlComplete(e:Event):void
{
var myXML:XML = new XML(e.target.data);
var menu:Sprite = new Sprite();
menu.x = 50;
menu.y = 10;
addChild(menu);
for (var i:int = 0; i < myXML.boton.length(); i++)
{
var tmp:portButtonInst = new portButtonInst();
//La siguiente liena, si la "descomento", deja de instanciarme los botones que defino en el XML y me tira el error
//tmp.titulo.texto_txt.text = myXML.boton[i].@texto;
tmp.y = (tmp.height + 5) * i;
tmp.ruta = myXML.boton[i].@seccion;
tmp.addEventListener(MouseEvent.CLICK, onClick);
menu.addChild(tmp);
}
}
function onClick(e:MouseEvent):void
{
trace(e.target.ruta);
}
Código que uso para mi clase boton:
package tesko.utils
{
import flash.display.MovieClip;
import flash.events.MouseEvent;
import flash.events.Event;
//import flash.text.TextField;
public class portButton extends MovieClip
{
//Probé a declarar esta variable publica pero si lo "descomento" me tira errores de sintaxis.
//public var titulo.texto_txt:TextField();
public function portButton()
{
this.stop();
this.buttonMode = true;
this.addEventListener(MouseEvent.ROLL_OVER, onRoll);
this.addEventListener(MouseEvent.ROLL_OUT, onOut);
}
private function onRoll(event:MouseEvent):void
{
this.removeEventListener("enterFrame", onRetroceder);
this.addEventListener(Event.ENTER_FRAME, onAvanzar);
}
private function onOut(event:MouseEvent):void
{
this.removeEventListener("enterFrame", onAvanzar);
this.addEventListener(Event.ENTER_FRAME, onRetroceder);
}
private function onAvanzar(event:Event):void
{
if(this.currentFrame < this.totalFrames){
this.nextFrame();
}else{
this.removeEventListener("enterFrame", onAvanzar);
}
}
private function onRetroceder(event:Event):void
{
if(this.currentFrame > 1){
this.prevFrame();
}else{
this.removeEventListener("enterFrame", onRetroceder);
}
}
}
}
Saludos.
Roberto.
robertoabril
Bua, a mi lo de los patrones de diseño aún me suena al macramé de la abuela tío jaja. Mucha incultura, lo sé...
En fin, pues te acuerdas aquello que dije de "...a ver que nuevas aventuras me encuentro?", pues ya encontré otra!!. No sé si sabrás ayudarme con esto, pero ya que estoy con el hilo abierto me lanzo y pregunto (prometo que esta vez el problema no son nombres de instancia). Además el tema sigue teniendo que ver con XML y el parseo de datos...más o menos...ejem.
El tema está que, currando en mi nueva web me he encontrado con el problema de que, una vez "traceada" correctamente la ruta donde se encuentran mis imagenes (en mi output panel) desde mi XML, quiero ahora, una vez dentro de esa carpeta, el poder navegar entre imagenes. He de aclarar que el XML desde donde parseo dicha ruta, no es bien bien un directorio de imagenes sino un "apuntador a directorio". Me explico: en realidad el link que puedes ver en el XML de abajo, es una ruta que conduce a la primera imagen de la carpeta "/web" imagen "0.jpg" donde se encuntran a su vez imagen "1.jpg", imagen "2.jpg", etc...
<?xml version="1.0" encoding="utf-8" ?>
<menu>
<boton texto="Idea Lab Flash Microsite" seccion="xml/xml_test/web/idealab/0.jpg"/>
<boton texto="Aloces Blog for Iconist Design" seccion="xml/xml_test/web/aloces/0.jpg"/>
<boton texto="OSX Tracio Web Design" seccion=""/>
<boton texto="Sans Serifer Blog Design" seccion=""/>
<boton texto="Lorem Ipusm" seccion=""/>
<boton texto="Lorem Ipsum Dolor Sit Amet" seccion=""/>
<boton texto="Laborem Et Dolorem" seccion=""/>
<boton texto="Testing Another Link" seccion=""/>
</menu>
Antes usaba la dirección completa de la foto "0.jpg" de cada carpeta para trazar la ruta. Ahora he pensado en sencillamente apuntar a la carpeta y anadir mediante una variable int el numero de foto empezando por 0 e ir subiendo para tener un control de cuantas fotos hay y demás mandangas con un loop o algo así pero no me aclaro como... De todas formas el XML quedaría así para el primer nodo:
<boton texto="Idea Lab Flash Microsite" seccion="xml/xml_test/web/idealab/"/>
He conseguido cargar esas primeras imagenes de cada carpeta en un nuevo Loader y añadirlas a la lista display además de dos botones mediante clases externas (arrowButton y arrowButton2), pero en este punto me he estancado. Bueno una imagen o (en este caso) un bloque de código vale más que mil palabras.
import br.com.stimuli.loading.BulkLoader;
import br.com.stimuli.loading.BulkProgressEvent;
import gs.TweenMax;
import gs.easing.*;
import caurina.transitions.*;
//Definimos los sprites aqui para que sean accesibles desde todas las funciones. En la web van en el frame 1!! en el 2 para resize
var menuW:Sprite = new Sprite();
var menuI:Sprite = new Sprite();
var menuB:Sprite = new Sprite();
var menuP:Sprite = new Sprite();
var menuD:Sprite = new Sprite();
//Defino las variables que conendran lso botones para moverse entre imagenes aqui defino su x e y. Susceptible!! Son clases externas
//BOTON NEXT
var nextArrow:arrowButton = new arrowButton();
nextArrow.x = stage.stageWidth+nextArrow.width;
//este boton tiene el register point en la mitad derecha!! niquelado
nextArrow.y = stage.stageHeight/2;
//BOTON BACK
var backArrow:arrowButton2 = new arrowButton2();
backArrow.x = 0-nextArrow.width;
//este tiene el register point er la mitad izquierda
backArrow.y = stage.stageHeight/2;
//Agrego BulkLoader y lo inicio
var xmlloader:BulkLoader = new BulkLoader("portxmlLoader");
xmlloader.add("xml/xml_test/web_test.xml", {id: "web"});
xmlloader.add("xml/xml_test/icon_test.xml", {id: "icon"});
xmlloader.add("xml/xml_test/branding_test.xml", {id: "branding"});
xmlloader.add("xml/xml_test/poster_test.xml", {id: "poster"});
xmlloader.add("xml/xml_test/downloads_test.xml", {id: "downloads"});
xmlloader.addEventListener(BulkLoader.COMPLETE, xmlComplete);
//La carga de los XML no dura más de 1 segundo a 1MB, no agrego onProgress de momento
//xmlloader.addEventListener(BulkLoader.PROGRESS, onProgress);
xmlloader.start();
//Lo que usaba antes de BulkLoader para cargar 1 solo XML
/*var loader:URLLoader = new URLLoader();
loader.addEventListener(Event.COMPLETE, xmlComplete);
loader.load(new URLRequest("xml/web.xml"))*/;
//defino la variable ldrPort para que pueda ser accesible desde la funcion alCompletar
var ldrPort:Loader = new Loader();
//Aqui intento crear una variable que contenga el número de fotos dentro de la carpeta para usar en una función posterior
//var imgCount:int = 0;
//INICIO DE XML COMPLETE
//Funcion Complete desde BulkLoader
function xmlComplete(e:Event):void {
var mywXML:XML = xmlloader.getXML("web");
var myiXML:XML = xmlloader.getXML("icon");
var mybXML:XML = xmlloader.getXML("branding");
var mypXML:XML = xmlloader.getXML("poster");
var mydXML:XML = xmlloader.getXML("downloads");
//Lo que usaba antes de BulkLoader
//var myXML:XML = new XML(e.target.data);
//Creo los sprites donde meter los datos y los añado a la lista display
//Para menu web
menuW.x = 50;
menuW.y = 50;
addChild(menuW);
//para menu icon
menuI.x = Math.round(menuW.x+(menuW.width +200));
menuI.y = menuW.y;
addChild(menuI);
//para menu braning
menuB.x = Math.round(menuI.x+(menuI.width +200));
menuB.y = menuW.y;
addChild(menuB);
//para menu poster
menuP.x = Math.round(menuB.x+(menuB.width +200));
menuP.y = menuW.y;
addChild(menuP);
//para menu downloads
menuD.x = menuW.x;
menuD.y = Math.round(menuW.y+(menuW.width +300));
addChild(menuD);
//CADA for HA DE TENER SUS PROPIAS VARIABLES EN REFERENCIA A CADA XML QUE CARGO !!!!
//for que se encarga de cargar los datos en el menu web.
for (var iW:int = 0; iW < mywXML.boton.length(); iW++)
{
var tmpW:portButtonInst = new portButtonInst();
tmpW.titulo.texto_txt.text = mywXML.boton[iW].@texto;
tmpW.newTag.alpha = 0;
tmpW.newTag.x = Math.round(tmpW.newTag.x+tmpW.titulo.texto_txt.length+15);
tmpW.y = Math.round((tmpW.height + 1) * iW);
tmpW.ruta = mywXML.boton[iW].@seccion;
tmpW.addEventListener(MouseEvent.CLICK, onClick);
menuW.addChild(tmpW);
}
//for que se encarga de cargar los datos en el menu icon
for (var iI:int = 0; iI < myiXML.boton.length(); iI++)
{
var tmpI:portButtonInst = new portButtonInst();
tmpI.titulo.texto_txt.text = myiXML.boton[iI].@texto;
tmpI.newTag.alpha = 0;
tmpI.newTag.x = Math.round(tmpI.newTag.x+tmpI.titulo.texto_txt.length+15);
tmpI.y = Math.round((tmpI.height + 1) * iI);
tmpI.ruta = myiXML.boton[iI].@seccion;
tmpI.addEventListener(MouseEvent.CLICK, onClick);
menuI.addChild(tmpI);
}
//for que se encarga de cargar los datos en el menu branding
for (var iB:int = 0; iB < mybXML.boton.length(); iB++)
{
var tmpB:portButtonInst = new portButtonInst();
tmpB.titulo.texto_txt.text = mybXML.boton[iB].@texto;
tmpB.newTag.alpha = 0;
tmpB.newTag.x = Math.round(tmpB.newTag.x+tmpB.titulo.texto_txt.length+15);
tmpB.y = Math.round((tmpB.height + 1) * iB);
tmpB.ruta = mybXML.boton[iB].@seccion;
tmpB.addEventListener(MouseEvent.CLICK, onClick);
menuB.addChild(tmpB);
}
//for que se encarga de cargar los datos en el menu poster
for (var iP:int = 0; iP < mypXML.boton.length(); iP++)
{
var tmpP:portButtonInst = new portButtonInst();
tmpP.titulo.texto_txt.text = mypXML.boton[iP].@texto;
tmpP.newTag.alpha = 0;
tmpP.newTag.x = Math.round(tmpP.newTag.x+tmpP.titulo.texto_txt.length+15);
tmpP.y = Math.round((tmpP.height + 1) * iP);
tmpP.ruta = mypXML.boton[iP].@seccion;
tmpP.addEventListener(MouseEvent.CLICK, onClick);
menuP.addChild(tmpP);
}
//for que se encarga de cargar los datos en el menu downaloads
for (var iD:int = 0; iD < mydXML.boton.length(); iD++)
{
var tmpD:portButtonInst = new portButtonInst();
tmpD.titulo.texto_txt.text = mydXML.boton[iD].@texto;
tmpD.newTag.alpha = 0;
tmpD.newTag.x = Math.round(tmpD.newTag.x+tmpD.titulo.texto_txt.length+15);
tmpD.y = Math.round((tmpD.height + 1) * iD);
tmpD.ruta = mypXML.boton[iD].@seccion;
tmpD.addEventListener(MouseEvent.CLICK, onClick);
menuD.addChild(tmpD);
}
}
//FIN DE LA FUNCION XMLCOMPLETE
/*De momento la carga es sin progreso
function onProgress(evt:BulkProgressEvent)
{
trace(evt.bytesLoaded + " - " + evt.bytesTotal);
//trace(Math.round(evt.bytesLoaded*100/evt.bytesTotal));
//var perxmlPort:Number=Math.round(evt.bytesLoaded*100/evt.bytesTotal);
//trace(perxmlPort);
}*/
//MUY IMPORTANTE, LA RUTA A LAS IMAGENES EN EL XML HA DE SER DESDE DONDE SE GENERA EL SWF Y NO EN RELACION AL PROPIO XML!!!!!
function onClick(e:MouseEvent):void
{
var destino:String = e.currentTarget.ruta;
//Añadimos el loader que cargara las imagenes
ldrPort.load(new URLRequest(destino+"0.jpg"));
//ldrPort.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, Progreso);
ldrPort.contentLoaderInfo.addEventListener(Event.COMPLETE, alCompletar);
}
function alCompletar(e:Event):void {
addChild(ldrPort);
addChild(nextArrow);
addChild(backArrow);
ldrPort.y = stage.stageHeight;
TweenMax.to(menuW, 1, {y:stage.stageHeight+200, alpha:0, ease:Expo.easeOut});
TweenMax.to(menuI, 1, {y:stage.stageHeight+200, alpha:0, ease:Expo.easeOut});
TweenMax.to(menuB, 1, {y:stage.stageHeight+200, alpha:0, ease:Expo.easeOut});
TweenMax.to(menuP, 1, {y:stage.stageHeight+200, alpha:0, ease:Expo.easeOut});
TweenMax.to(menuD, 1, {y:stage.stageHeight+200, alpha:0, ease:Expo.easeOut});
Tweener.addTween(ldrPort, {y:0, alpha:1, time:1, delay:1, transition:"easeOutExpo"});
Tweener.addTween(nextArrow, {x:nextArrow.x-nextArrow.width, alpha:1, time:.5, delay:2, transition:"easeOutExpo"});
Tweener.addTween(backArrow, {x:backArrow.x+backArrow.width, alpha:1, time:.5, delay:2, transition:"easeOutExpo"});
}
/*Esta es la función que pretendía usar para manejar la navegación dentre imagenes
function control():void {
}*/
Utilizo BulkLoader para cargar los XML pero una vez devueltos los datos de los XML, todo es bastante entendible (creo).
A todo esto, EL PROBLEMA, está en que no sé muy bien como encarar esta movida. No sé si he cometido un error de planificación con mis XML y por lo tanto, debería incluir todas las imagenes que esten dentro de todas las carpetas dentro de los nodos de los XML o si mi actual "hoja de ruta" es acertada: Es decir, tirar por donde voy y, a raíz de haber cargado la primera de las imagenes de cada una de las carpetas desde la ruta de los XML, crear nuevas funciones y variables que me permitan manejar el contenido de las mismas (las imagenes). En un principio creí que debido a mi lamentable nivel con AS3, sería el mejor de los movimeintos, pero llegados a este punto me encuentro con bastantes dudas al respecto de como afrontar el problema.
No sé si tu (o alguién) sabría orientarme al respecto de por donde tirar apartir de ahora y si mi intención inicial es acertada o una paja mental propia de un novato.
Sea como sea, gracias por la ayuda que me pudieras/pudiérais prestar.
Salutens!
xcx
Lo conozco, lo conozco, ahora estoy con el de Patrones de diseño de Peachpit aunque me he aclarado bastantes cosas al final acabo usando patrones MVC casi siempre jajaja
robertoabril
Hey again!
Muchas gracias por tu ayuda y tus comentarios XCX. Jaja que va tio, aqui el único cateto soy yo que pregunto por cosas que luego se arreglan poniendo un nombre de instancia...
La verdad que aún me falta mucho por aprender de AS3 en cuanto a clases y estructura interna por eso me choca lo de esas propiedades que se generan por arte de magia, aunque claro, teniendo en cuenta que son clases dinamicas enlazadas por paquetes tiene más que sentido.
Hablando del temita aprender, no sé si has oido hablar del libro "Actionscript 3.0 a beginner´s guide" de O´Reilly. Yo lo empecé hace un par de días y la verdad que esta wapo tío. Es sencillo de entender (de momento), ameno de leer y me estoy enterando de mogollón de movidas que antes se me escapaban. Si tienes tiempo y ganas échale un ojo. Fijo que sacas algo positivo.
Bueno, pues eso, que muchas gracias por tu tiempo. Me vuelvo al portfolio a ver que nuevos misterios me encuentro jaja.
Salutens!
xcx
Nada, hombre, estamos para ayudar, sirva o no jejeje
Respecto a tus dudas y siendo yo bastante cateto en cuanto a Flash:
Si no me equivoco (y que me corrija alguien si digo una barbaridad) currentTarget es el objeto al que asignas el evento mientras que target en el objeto que recibe el evento.
A veces son lo mismo, pero por ejemplo, imagina tienes un clip "boton", al que añades un EventListener para el evento MOUSE_OVER, y dentro del mismo un clip "texto" . Imagínate que haces rollover por "boton", pero a la vez pasas por encima de "texto".
En ese caso el target sería "texto" y el currentTarget "boton"
Es un poco lío pero creo que me explico.
Lo otro, pues no lo he probado pero así a ojo supongo que al extender portButton de MovieClip será una clase dinámica y puedes añadirle propiedades sin declarar. La verdad es que desde que comencé siempre declaro todo, así que nunca me he fijado, pero creo que será por eso.
Quizás si la extiendes en la librería como Sprite te salta un error... Como te digo esto último ya son suposiciones mías, luego lo pruebo a ver jajaj
robertoabril
Hey!
Muchas gracias por la ayuda y por tomarte la molestia de probar mi ejemplo XCX.
Esto....espero que no te mosquees si te digo que ya soluciones el problema...lo único que pasaba (a veces las cosas más básicas son aquellas que das por sentadas...) es que me olvidé de dar un nombre de instancia al mc de "titulo" con lo cual AS no podia trabajar con él. Lo siento, pero es que me hice la picha un lío y no daba con la solución hasta que después de postear el mensaje me paré a reflexionar realmente sobre el tipo de error.
Por otra parte tu código me ha ayudado a ver otro error que me tiraba el panel output cuando quería hacer un trace de "ruta". Yo estaba usando:
trace(e.target.ruta);
Este método me tiraba este error:
ReferenceError: Error #1069: No se encontró la propiedad ruta en flash.text.TextField y no hay ningún valor predeterminado.
at menuMXL_fla::MainTimeline/onClick()
Creo que no podía encontrar la ruta dentro del textField del target porque (imagino, corrigeme si me equivoco) no se encuentra allí sino en el XML del boton que se presiona (currentTarget sería el boton y target sería hacia donde apunta el boton quizás)?¿.
Sea como fuere tu currentTarget funciona a la perfección y ahora la ventana de salida me tira el trace correcto. Otra cosa que no me ha echo falta es el definir la variable "ruta" como publica de tipo String en la clase portButton. En realidad, esto es algo que no entiendo aunque funcoine, ya que para el resto de objetos a los que accedo si necesito que sean objetos definidos o bien mediante instancias (con su nombre claro esta jeje) o propiedades inherentes a objetos como x o y. Si no te rayo mucho, podrías explicarme el porque funciona sin necesidad de declarar esa variable ruta en la clase?. La verdad que estas movidas son el tipo de cosas que a un principiante como yo en AS3 le chocan un poco.
Bueno pues eso. que de nuevo muchas gracias por la ayuda, se agradece el altruismo y la buena onda.
Salutens, tachas y metal.
Roberto.
xcx
Bueno, a mi me parece que pueden ser varias cosas: un error en el xml o aquí:
var tmp:portButtonInst = new portButtonInst();
He copiado tu script y tu creado tu clase en mi pc y a mi me funciona si:
1- añado a la librería un MovieClip exportado con el nombre de clase portButton (no portButtonInst) que contiene una instancia de otro movieclip llamada titulo con el campo de texto texto_txt
2- el código de la linea de tiempo queda así
var loader:URLLoader = new URLLoader();
loader.addEventListener(Event.COMPLETE, xmlComplete);
loader.load(new URLRequest("datos.xml"));
function xmlComplete(e:Event):void
{
var myXML:XML = new XML(e.target.data);
var menu:Sprite = new Sprite();
menu.x = 50;
menu.y = 10;
addChild(menu);
for (var i:int = 0; i < myXML.boton.length(); i++)
{
var tmp:portButton = new portButton();
tmp.titulo.texto_txt.text = myXML.boton[i].@texto;
tmp.y = (tmp.height + 5) * i;
tmp.ruta = myXML.boton[i].@seccion;
tmp.addEventListener(MouseEvent.CLICK, onClick);
menu.addChild(tmp);
}
}
function onClick(e:MouseEvent):void
{
trace(e.currentTarget.ruta);
}
3- Declaro en la clase portButton la variable ruta como públca:
public var ruta:String;
4- El XML es algo como esto:
<?xml version="1.0" encoding="utf-8"?><botones>
<boton texto="seccion1" seccion="seccion1.swf"/>
<boton texto="seccion2" seccion="seccion2.swf"/>
</botones>
No se si te he liado más o es distinto a lo que pretencías hacer, pero a mi con estos cambios no me da ningun error y me salen los boton con el texto y trazan la ruta al clicar