Feb 18, 2008

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:

[as3]this.stage.addEventListener(MouseEvent.CLICK, click);
private function click(e:MouseEvent):void
{
trace(“Click en”, e.target);
}[/as3]

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

[as3]package {
import flash.display.Sprite;
import Circulo;

public class Main extends Sprite {

function Main() {
var c:Circulo = new Circulo();
addChild(c);
}
}
}[/as3]

Clase Circulo

[as3]package {
import flash.display.Sprite;
import flash.events.*;

public class Circulo extends Sprite {

function Circulo() {

// Dibujar Circulo
this.graphics.beginFill(0xFF0000);
this.graphics.drawCircle(50, 50, 10);
this.graphics.endFill();

// Definir el evento click
this.stage.addEventListener(MouseEvent.CLICK, clic);
}

private function clic(e:MouseEvent):void
{
if (e.target == this) trace(“Click sobre circulo”)
else trace(“No es circulo”);
}
}
}[/as3]

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:

[as3]package {
import flash.display.Sprite;
import flash.events.*;

public class Circulo extends Sprite {

function Circulo() {

// Dibujar Circulo
this.graphics.beginFill(0xFF0000);
this.graphics.drawCircle(50, 50, 10);
this.graphics.endFill();

// Escuchar el evento ADDED
this.addEventListener(Event.ADDED, init);
// o en este caso también ADDED_TO_STAGE
//this.addEventListener(Event.ADDED_TO_STAGE, init);
}

private function init(e:Event):void
{
// Definir el evento click
this.stage.addEventListener(MouseEvent.CLICK, clic);
}

private function clic(e:MouseEvent):void
{
if (e.target == this) trace(“Click sobre circulo”)
else trace(“No es circulo”);
}
}
}[/as3]

>> Descargar ejemplo

Información del artículo

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

Categorias: Tips

Etiquetas: , , , ,

Comparte

26 comentarios

  • 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?

  • Ummm it’s strange, for me this code works properly:
    [as3]function Main() {
    var c:Circulo = new Circulo();
    addChild(c);

    var d:Circulo = new Circulo();
    addChild(d);
    d.x += 30;
    }[/as3]

    Are you trying with the correct code?

  • 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.

  • Juan

    Hola. Como les va?

    Tengo la siguiente clase en un archivo .AS

    [as3]
    package {
    import mx.core.WindowedApplication;
    import flash.events.*;

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

    public function HW(){
    super();
    }

    }
    }[/as3]

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

    [as3]public function ingresar():void {
    var newWindow:HW = new HW();
    newWindow.initialize();
    newWindow.setVisible(true);
    }[/as3]

    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:

    [as3]
    override public function initialize():void{
    super.initialize();
    }
    public function HW(){
    this.addEventListener(Event.ADDED_TO_STAGE, initialize);
    super();
    }[/as3]

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

    Gracias.

  • 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

  • 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!!!!

  • 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…

  • 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.

  • 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

  • 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!

  • 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:

    [as3]
    import Circulo;
    var c:Circulo = new Circulo();
    addChild(c);[/as3]

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

    [as3]
    import com.llops.MainSelector;
    var selector:MainSelector = new MainSelector();
    addChild(selector);[/as3]

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

  • 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:

    [as3]import Main;
    var m:Main = new Main();
    // Main en su constructor crea Circulo
    // Circulo en su constructor detecta que ha sido añadido (ADDED) y llama a init()
    // init contiene esta linea:
    this.stage.addEventListener(MouseEvent.CLICK, clic);
    // 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á
    // se genera el error
    addChild(m) // llegamos un poco tarde… :)[/as3]
     
    En el ejemplo final que he puesto para descargar, en la clase Circulo, comenta la linea:
    [as3]this.addEventListener(Event.ADDED, init);[/as3]
    y descomenta esta:
    [as3]this.addEventListener(Event.ADDED_TO_STAGE, init);[/as3]

    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í:
    [as3]import Main;
    var m:Main = new Main();
    addChild(m)
    m.init();[/as3]

    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!

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

  • 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

    [as3]
    import flash.display.Sprite;
    import flash.display.Stage;
    import Circulo;
    public class Main extends Sprite {
    function Main() {
    var controlador:Controlador = new Controlador(stage);
    addChild(controlador);
    }
    }
    [/as3]
    [as3]
    public class Controlador extends MovieClip {
    public function Controlador(stageRef:Stage) {
    var c:Circulo = new Circulo(stageRef);
    addChild(c);
    }
    }
    [/as3]
    Para Circulo ahora escucho stageRef en lugar de stage
    [as3]
    function Circulo(stageRef:Stage) {

    this.stageRef=stageRef;

    }

    private function init(e:Event):void {
    this.stageRef.addEventListener(MouseEvent.CLICK, clic);
    }
    [/as3]

    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

  • 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?

    [as3]//PROPOSITOS
    Propositos_btn.addEventListener(MouseEvent.CLICK, TextProp);
    function TextProp(event:MouseEvent):void
    {
    gotoAndPlay(“Propositos”)
    }
    //REQUISITOS
    Requisitos_btn.addEventListener(MouseEvent.CLICK, TextReq);
    function TextReq(event:MouseEvent):void
    {
    gotoAndPlay(“Requisitos”)
    }
    botonSiguiente_btn.addEventListener(MouseEvent.CLICK, TextReq_2);
    function TextReq_2(event:MouseEvent):void
    {
    gotoAndPlay(“Requisitos2”)
    }[/as3]

  • 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

    [as3]
    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;
    }
    }
    }
    [/as3]

    y lo llamo asi

    [as3]import Centrar;
    var movi:Centrar = new Centrar(cuadro_mc);[/as3]

    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

  • 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

    [as3]grafico_BTN.addEventListener(MouseEvent.MOUSE_DOWN,grafico);

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

  • 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

    [as3]
    package {
    import flash.display.*;
    import flash.text.TextField;

    public class GreetingApp extends Sprite {
    public function GreetingApp( ) {
    // Create the Shape object
    var rectAndCircle:Shape = new Shape( );
    // Set line thickness to one pixel
    rectAndCircle.graphics.lineStyle(1);
    // Draw a blue rectangle
    rectAndCircle.graphics.beginFill(0x0000FF, 1);
    rectAndCircle.graphics.drawRect(125, 0, 150, 75);
    // Draw a red circle
    rectAndCircle.graphics.beginFill(0xFF0000, 1);
    rectAndCircle.graphics.drawCircle(50, 100, 50);
    // Move the shape to the right 125 pixels and down 100 pixels
    rectAndCircle.x = 125;
    rectAndCircle.y = 100;
    // Show rectAndCircle on screen by adding it to the display list
    addChild(rectAndCircle);
    // Create a TextField object to contain some text
    var greeting_txt:TextField = new TextField( );
    // Specify the text to display
    greeting_txt.text = “Hello world”;
    // Position the text
    greeting_txt.x = 200;
    greeting_txt.y = 300;
    // Show the text on screen by adding greeting_txt to the display list
    addChild(greeting_txt);
    }
    }
    }
    [/as3]

  • 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

  • Adrian

    Hola, chido el post y llegue aqui en busqueda de la solucion al error #1009 y asi esta la cosa
    estoy incorporando un portafolio a una pagina el cual es una galeria de imagenes que carga las fotos desde un archivo xml, todo esta esta bien, pero cuando quiero cargar este swf en una pagina externa (la pagina principal)me aparece este error.
    La galeria esta creada desde una clase as3.

  • Adrian

    De nuevo yo, con respecto al problema que me da al intentar cargar un swf externo, dejo el codigo as3 que tengo desde una clase y el cual lo intento cargar desde la linea de tiempo.
    Estaria chido que me pudieran ayudar ya que no he podido solucionar este problema.
    Como dices en tu post addedToStage creo que es lo que me falta poner para solucionar esto, pero no encuentro en donde colocarlo.

    Ver código

  • elmanusito

    Excelente tip!!!! excelente!!!! me salvaste cabezón!!! gracias!!!!

  • Sergio

    Muy buen tip. Gracias amigo. Me sacaste de un problema de hace casi 2 horas.

  • Tchavez

    Hola soy nuevo en flash y estoy aprendiendo. En as3 acabo de generar esto para poder hacer un formulario para mi pag web sin embargo tengo error Error #1009 y no se como solucionarlo. Ojala me pudieran ayudar. Un saludo!!!

    Ver código

  • pulgososms

    Bueno, sencillamente genial… Gracias por este aporte tan bien explicado :)

  • mira llops yo lo tengo con un enter frame, estoy haciendo un simple preloader ya habia funcionado sin errores y luego vuelvo y lo hago tal cual y nada manda el error #1009, el preloader funciona pero manda el error cientos de veces.
    TypeError: Error #1009: No se puede acceder a una propiedad o a un método de una referencia a un objeto nulo.
    at preloader_002_fla::MainTimeline/carga()

    Aqui esta el codigo …. Agredezco la ayuda
    stop();

    addEventListener (Event.ENTER_FRAME, carga);
    function carga (event:Event):void

    {
    var bytestotal = stage.loaderInfo.bytesTotal;
    var bytescargados = stage.loaderInfo.bytesLoaded;
    var porcentaje = Math.round(bytescargados *100/bytestotal);

    texto.text = porcentaje + ” % Cargados ”
    if (bytescargados == bytestotal)
    {
    removeEventListener (Event.ENTER_FRAME, carga);
    gotoAndPlay(2);
    texto.text = ” ”
    removeChild (texto);
    }
    }