Accediendo al stage antes de tiempo

En AS3 es una práctica habitual asociar eventos al stage para cogerlos globalmente. El caso más utilizado probablemente se dé con los del tipo MouseEvent. Por ejemplo, si tenemos varios objetos en pantalla, en vez de definir un evento click para cada uno de ellos, cogemos sólo el click en el escenario (global) y vemos sobre quién se está interactuando:

  1. this.stage.addEventListener(MouseEvent.CLICK, click);
  2. private function click(e:MouseEvent):void
  3. {
  4.     trace("Click en", e.target);
  5. }

Pero a veces, cuando intentamos añadir un evento al stage obtenemos el siguiente error:

TypeError: Error #1009: No se puede acceder a una propiedad o a un método de una referencia a un objeto nulo.

Vamos a reproducir el error.

Tenemos dos clases: Main, asociada al documentClass, y Circulo, que se encarga de dibujar un pequeño circulo y capta un evento click.

Clase Main

  1. package {
  2.     import flash.display.Sprite;
  3.     import Circulo;
  4.    
  5.     public class Main extends Sprite {
  6.        
  7.         function Main() {
  8.             var c:Circulo = new Circulo();
  9.             addChild(c);
  10.         }
  11.     }
  12. }

 

Clase Circulo

  1. package {
  2.     import flash.display.Sprite;
  3.     import flash.events.*;
  4.  
  5.     public class Circulo extends Sprite {
  6.        
  7.         function Circulo() {
  8.            
  9.             // Dibujar Circulo
  10.             this.graphics.beginFill(0xFF0000);
  11.             this.graphics.drawCircle(50, 50, 10);
  12.             this.graphics.endFill();
  13.            
  14.             // Definir el evento click
  15.             this.stage.addEventListener(MouseEvent.CLICK, clic);
  16.         }
  17.        
  18.         private function clic(e:MouseEvent):void
  19.         {
  20.             if (e.target == this) trace("Click sobre circulo")
  21.             else trace("No es circulo");
  22.         }
  23.     }
  24. }

Si compilamos obtenemos:

TypeError: Error #1009: No se puede acceder a una propiedad o a un método de una referencia a un objeto nulo.
    at Circulo()
    at Main()

 

¿Por qué obtenemos este error?

Stage es el contenedor base de todos los objetos visuales y se encuentra en lo más alto de la displayList.  Este contenedor no se puede acceder directamente, sino que se tiene que hacer mediante la propiedad stage, que se encuentra en cualquier objeto que extiende de DisplayObject.

Nuestro objeto Circulo extiende de Sprite y por tanto es un DisplayObject que soporta la propiedad stage, pero cuando intentamos acceder a dicha propiedad el objeto no se encuentra todavía en la displayList y por tanto la referencia es nula.

 

Solución

Para solucionar esto existen dos eventos muy útiles definidos en DisplayObjet: added y addedToStage. El primero se dispacha cuando un objeto se añade a la displayList y el segundo cuando se añade al escenario.

Así pues, podemos suscribirnos a estos eventos antes de acceder al stage y así garantizar que está disponible y que no genera error.

Finalmente nuestra clase Circulo quedaría:

  1. package {
  2.     import flash.display.Sprite;
  3.     import flash.events.*;
  4.  
  5.     public class Circulo extends Sprite {
  6.        
  7.         function Circulo() {
  8.            
  9.             // Dibujar Circulo
  10.             this.graphics.beginFill(0xFF0000);
  11.             this.graphics.drawCircle(50, 50, 10);
  12.             this.graphics.endFill();
  13.            
  14.             // Escuchar el evento ADDED
  15.             this.addEventListener(Event.ADDED, init);
  16.             // o en este caso también ADDED_TO_STAGE
  17.             //this.addEventListener(Event.ADDED_TO_STAGE, init);
  18.         }
  19.        
  20.         private function init(e:Event):void
  21.         {
  22.             // Definir el evento click
  23.             this.stage.addEventListener(MouseEvent.CLICK, clic);
  24.         }
  25.        
  26.         private function clic(e:MouseEvent):void
  27.         {
  28.             if (e.target == this) trace("Click sobre circulo")
  29.             else trace("No es circulo");
  30.         }
  31.     }
  32. }

>> Descargar ejemplo

Post publicado el 18 de February de 2008 a las 16:07 por llops

Categorias: Tips

Etiquetas: , , , ,

¿Quieres seguir los comentarios de esta entrada? RSS 2.0

Permalink, Trackback

  • Twitter
  • Meneame
  • Facebook
  • Google Gmail
  • Delicious
  • Share/Bookmark

19 comentarios

  1. Hummm, i tried create another instance of the circle object and i add it to displaylist. After this the event workn't properly. The output show me both messages. Because?

  2. #2   LLops

    Ummm it's strange, for me this code works properly:

    1. function Main() {
    2.     var c:Circulo = new Circulo();
    3.     addChild(c);
    4.            
    5.     var d:Circulo = new Circulo();
    6.     addChild(d);
    7.     d.x += 30;
    8. }

    Are you trying with the correct code?

  3. #3   EgoSum

    Gracias por el tip, me ha solucionado ese runtime error que me salia cuando intentaba lanzar un swf hecho en flash que cargaba a partir de un SWFLoader en flex.

  4. #4   Juan

    Hola. Como les va?

    Tengo la siguiente clase en un archivo .AS

    package {
    import mx.core.WindowedApplication;
    import flash.events.*;

    public class HW extends WindowedApplication
    {
    private var appWA:WindowedApplication;

    public function HW(){
    super();
    }

    }
    }

    Y cuando la quiero usar de la siguiente manera con el siguiente codigo de un archivo .MXML

    public function ingresar():void {

    var newWindow:HW = new HW();
    newWindow.initialize();
    newWindow.setVisible(true);

    }

    Obtengo el siguiente error:

    TypeError: Error #1009: No se puede acceder a una propiedad o a un método de una referencia a un objeto nulo.
    at mx.core::Application/initialize()
    at HW/initialize()
    at ingresar()

    Para resolver el problema intente agregar el siguiente codigo:

    override public function initialize():void{
    super.initialize();
    }

    public function HW(){
    this.addEventListener(Event.ADDED_TO_STAGE, initialize);
    super();
    }

    Pero no he tenido éxito. Agradecería me puedan indicar como lo soluciono.

    Gracias.

  5. #5   gonzalo geraldo

    Muchas gracias Llops, este tema no lo podia resolver y no lo he visto documentado. Tengo el Essential Actionscript 3.0 y tampoco aparece (espero no pecar de no entender lo que leo). En el Help de Actionscript tampoco lo habia visto explicado asi.

    Me ha servido mucho

    saludos

  6. #6   David E Sanchez

    Hola!
    Quisiera Agradecer la ayuda!!!! pues me proporciono informacion ademas de que encontre solucion al mismisimo problema que se presenta aqui; lo unico que puedo decir es gracias LLop!!! estare publicando mis trabajos para que me ayuden a mejorar pues soy Principiente en FLASH y empece con CS3
    Salu2 a la comunidad!!!!

  7. #7   matias

    Ok, pero esto solo funciona cuando se utilizan classes, pero si el codigo esta en un keyframe dentro de un movieclip no funciona...

    a mi me continua tirando ese error y no tengo forma de solucionarlo...

  8. #8   LLops

    Matias, si el código está en un keyframe, no necesitas utilizar este evento, ya que un objeto en linea de tiempo lleva implícito encontrarse en la displayList.

    Si quieres, detállame el caso concreto (puedes hacerlo a través de contacto) y le echo un vistazo a ver si te puedo ayudar.

  9. #9   Pilar

    Genial me has ayudado mucho a encontrar la solución a parte de mi problema, estoy pasando del AS2 al AS3 y la verdad el cambio es brutal, sobre todo para alguien que es mas de diseño que de desarrollo.
    Como podría cambiar las variables por el ejemplo el color del circulo creado'???? desde el fla por supuesto

  10. #10   LLops

    Hola Pilar, me temo que no te he entendido... como le comentaba a Matias (#8), si quieres contáctame y me detallas lo que quieres hacer.

    Saludos!

  11. #11   Pablo

    Hola Llop, he de decirte que llevo tiempo buscando a alguien que me de una respuesta a esto pero aún no lo he encontrado, o bien no supe entender lo que me explicaron, mi problema es parecido, yo no utilizo Adobe CS3, uso KoolMoves un programa de bajo costo pero que en su ultima version incorpora AS3, este programa no tiene al igual que CS3 en el panel de propiedades, el campo Clase de Documento (el del lapicito), por lo que tengo que llamar a las clases desde el timeline con action script en el primer frame, yo utilizo para llamarlas al estage en el fotograma 1:

    import Circulo;
    var c:Circulo = new Circulo();
    addChild(c);

    Esto a veces funciona y otras no, por ejemplo con tu clase MainSelector.as:

    import com.llops.MainSelector;
    var selector:MainSelector = new MainSelector();
    addChild(selector);

    No funciona. ¿Por que? ¿ Es que todas las clases no se conportan igual? Gracias.

  12. #12   llops

    Buenas Pablo, creo que lo que pasa es lo siguiente:
    Con el DocumentClass, el archivo se inicializa y se añade a la displayList, pero cuando tú lo haces "manualmente" en el stage (tanto en el KoolMoves como en Flash), pasa algo como esto:

    1. import Main;
    2. var m:Main = new Main();
    3. // Main en su constructor crea Circulo
    4. // Circulo en su constructor detecta que ha sido añadido (ADDED) y llama a init()
    5. // init contiene esta linea:
    6. this.stage.addEventListener(MouseEvent.CLICK, clic);
    7. // se intenta añadir el listener al stage erroneamente, porque this (Circulo) no se ha añadido todavía a la displayList, ya que su padre (Main) tampoco lo está
    8. // se genera el error
    9. addChild(m) // llegamos un poco tarde... :)

     
    En el ejemplo final que he puesto para descargar, en la clase Circulo, comenta la linea:

    1. this.addEventListener(Event.ADDED, init);

    y descomenta esta:

    1. this.addEventListener(Event.ADDED_TO_STAGE, init);

    Ahora sí se está esperando a que se añada Circulo a la display List (no al "programa"), y no debería generarte errores.
     
    Y si quieres evitar andar pensando cual va a ser el orden de ejecución del programa, siempre puedes hacer algo así:

    1. import Main;
    2. var m:Main = new Main();
    3. addChild(m)
    4. m.init();

    En el constructor del Main no pones nada e inicializas en un método público (init), habiéndote asegurado que ya está en displayList y que puedes añadir cualquier hijo.

    Saludos!

  13. #13   Pablo

    Gracias, por tu explicación Llops. Practicaré lo que tan gentilmente me has explicado. Muchas Gracias y un Saludo.

  14. #14   Chefo

    Hola LLops, muy bueno el tutorial. Me ha funcionado al pelo!

    Sin embargo cuando he querido ampliarlo un poco ve he visto frente a un problema serio. Quiero tener un Main.as que sea el document y que llame a un Controlador.as que sea quie efectivamente llame al círculo. Tengo que pasar el stage hacia los futuros hijos

    1. import flash.display.Sprite;
    2.     import flash.display.Stage;
    3.     import Circulo;
    4.     public class Main extends Sprite {
    5.         function Main() {
    6.             var controlador:Controlador = new Controlador(stage);
    7.             addChild(controlador);
    8.         }
    9.     }

    1. public class Controlador extends MovieClip {
    2.         public function Controlador(stageRef:Stage) {
    3.             var c:Circulo = new Circulo(stageRef);
    4.             addChild(c);
    5.         }
    6.     }

    Para Circulo ahora escucho stageRef en lugar de stage

    1. function Circulo(stageRef:Stage) {
    2.             ...
    3.             this.stageRef=stageRef;
    4.                        ...
    5. }
    6.  
    7. private function init(e:Event):void
    8.         {
    9.  
    10.             this.stageRef.addEventListener(MouseEvent.CLICK, clic);
    11.         }

    Hasta aqui todo va bien, pero si quiero editar a mano el objeto Circulo, es decir desde flash y no desde as. No se ve nada en la pantalla! Eso si que me ha dejado planchado. Mi objetivo es desde la clase Controlador.as cargar todos los simbolos de un juego y detectar un fin de partida para avisar a Main y que éste carge a traves de otro controlador otro juego.

    De momento estoy haciendo los juegos por separado, pero no puedo ensamblar sus clases principales a traves de un Main comun porque no se ve nada, ademas de que para probar cada juego desde el Main tengo que pasar una instancia de stage hasta los hijos, con lo cual, cuando los edito con flash me fallan por que les sobra un parametro en el constructor (el parametro que les paso el stage).

    Le he dado mil vueltas y leido muchos foros, pero no veo cual es el problema, a ver si me puedes orientar un poco Llop

  15. #15   ELSanto

    hola. soy nuevo en este tema, no se mucho pero ahi voy tratando de aprender.
    Tengo una serie de botones que me llevan cada uno a una etiqueta, cada boton es una sola etiqueta pero en una de ellas debo ingresar dos botones para darle continuacion a la informacion. Quiero decir que cada boton tiene una sola pagina de información, pero uno de ellos tiene dos paginas y debo poner un boton de "siguiente" eneste pantallazo. cuando hago esto me sale el error #1009: este es el codigo que tengo...me pueden ayudar urgente?

    1. //PROPOSITOS
    2. Propositos_btn.addEventListener(MouseEvent.CLICK, TextProp);
    3. function TextProp(event:MouseEvent):void
    4. {
    5.     gotoAndPlay("Propositos")
    6. }
    7. //REQUISITOS
    8. Requisitos_btn.addEventListener(MouseEvent.CLICK, TextReq);
    9. function TextReq(event:MouseEvent):void
    10. {
    11.     gotoAndPlay("Requisitos")
    12. }
    13. botonSiguiente_btn.addEventListener(MouseEvent.CLICK, TextReq_2);
    14. function TextReq_2(event:MouseEvent):void
    15. {
    16.     gotoAndPlay("Requisitos2")
    17. }

  16. #16   Ccenta

    Hola y gracias por el post, pero la misma dura me persiste a mi, por favor podria darme un pequeña ayuda.

    Soy novato en todo esto. Resulta q estoy haciendo una pequeña aplicacion en actionscript 3.0 que desde una clase as3 todo ese pequeño trozo de codigo, intento llamarlo desde el mismo reproductor fla, la clase as3, dentro de esa clase tengo un parametro de MovieClip y desde el reproductor aplicando su parametro movieclip me vota cierto error

    1120: Acceso a una propiedad stage no definida
    1120: Acceso a una propiedad stage no definida

    package
    {
    import flash.display.MovieClip;
    public class Centrar
    {
    public var miclip:MovieClip = new MovieClip();
    public function Centrar (clip):void
    {
    miclip = clip;
    miclip.x = (stage.stageWidth - miclip.width) / 2;
    miclip.y = (stage.stageHeight - miclip.height) / 2;

    }
    }
    }

    y lo llamo asi

    import Centrar;
    var movi:Centrar = new Centrar(cuadro_mc);

    press control + enter y me vota este error

    1120: Acceso a una propiedad stage no definida
    1120: Acceso a una propiedad stage no definida

    lo q intento es querer llamar por via codigo import, no por el mismo documento principal donde nada mas se pone el nombre de la clase, por favor ayuden

  17. #17   Nilson

    Hola, creo que tengo el mismo problema que Matìas, pues los Movieclips los tengo ya en el escenariom pero al pulsar un botòn dentro de un MC que me dirija al Escenario principal, me arroja el error

    aqui el còdigo dentro de un MC

    grafico_BTN.addEventListener(MouseEvent.MOUSE_DOWN,grafico);

    function grafico(Event:MouseEvent):void {
    root.imagen_mc.gotoAndStop(113);
    }

  18. #18   aquiles perez

    Hola todos, tengo una dudilla, por que este codigo no me trabaja, cuando creo un objeto en un frame de flash, no me muestra nada, sin embargo cuando agrego addChild(new GrettingApp) si trabaja, alguien me puede decir como puedo mostrar los graficos de esta clase sin poner esa instruccion, ya se que el stage es el tope, pero necesito referirme a el desde mi clase.

    saludos

    1. package {
    2. import flash.display.*;
    3. import flash.text.TextField;
    4. public class GreetingApp extends Sprite {
    5. public function GreetingApp( ) {
    6. // Create the Shape object
    7. var rectAndCircle:Shape = new Shape( );
    8. // Set line thickness to one pixel
    9. rectAndCircle.graphics.lineStyle(1);
    10. // Draw a blue rectangle
    11. rectAndCircle.graphics.beginFill(0x0000FF, 1);
    12. rectAndCircle.graphics.drawRect(125, 0, 150, 75);
    13. // Draw a red circle
    14. rectAndCircle.graphics.beginFill(0xFF0000, 1);
    15. rectAndCircle.graphics.drawCircle(50, 100, 50);
    16. // Move the shape to the right 125 pixels and down 100 pixels
    17. rectAndCircle.x = 125;
    18. rectAndCircle.y = 100;
    19. // Show rectAndCircle on screen by adding it to the display list
    20. addChild(rectAndCircle);
    21. // Create a TextField object to contain some text
    22. var greeting_txt:TextField = new TextField( );
    23. // Specify the text to display
    24. greeting_txt.text = "Hello world";
    25. // Position the text
    26. greeting_txt.x = 200;
    27. greeting_txt.y = 300;
    28. // Show the text on screen by adding greeting_txt to the display list
    29. addChild(greeting_txt);
    30. }
    31. }
    32. }

  19. #19   Kaus

    gracias men si no encuentro esto para evitar ese error no se que haria ya me estaba desesperando me fue de gran ayuda, creeme muchas gracias

¡Deja un comentario!

Recuerda:

  • Los programadores son gente noble y sensible (sí, todos), así que pórtate bien y se respetuoso con tus comentarios.
  • Puedes usar las siguientes etiquetas html: <b>, <em> y <a>.
  • Si deseas añadir código utiliza este formato: [as3]code[/as3].
Top