web-dev-qa-db-fra.com

Pour passer un paramètre à l'écouteur d'événements dans AS3 de manière simple ... existe-t-il?

Exemple attendu/pseudo:

stage.addEventListener(MouseEvent.CLICK, onClick.someWayToPassParameters(true, 123, 4.56, "string"));
function onClick(e:MouseEvent):void {
    trace("Received " + someWayToRetrieveParameters().b/i/n/s + ".");
}

Pendant de nombreuses années (3 à 4), sur chaque site Web, forum, blog, où que je recherche, les gens me disent qu'il n'y a pas de moyen simple de le faire. Ils suggèrent généralement de:

  • Ajoutez l'écouteur à un objet dynamique, où vous pouvez définir la valeur d'une propriété supplémentaire et la référencer (e.target.property/e.currentTarget.property) dans la fonction.

    Toutes les classes ne sont pas dynamiques. Cela ne fonctionnera pas sur un Sprite, par exemple.

  • Étendez la classe de l'objet avec une classe personnalisée pour ajouter une propriété ou simplement la rendre dynamique.

    Vous devrez à chaque fois créer une toute nouvelle classe Tweak.

  • Utilisez une fonction anonyme comme gestionnaire d'événements.

    Il n'y a aucune référence (et c'est moche). Pour supprimer l'écouteur afin de libérer des ressources, vous êtes obligé de le faire depuis l'intérieur de la fonction elle-même avec arguments.callee.

  • Appeler une autre fonction, à l'aide du paramètre, à l'intérieur du gestionnaire d'événements.

    Et où va l'appel du gestionnaire d'événements?

  • Gardez le gestionnaire d'événements dans la même portée que le paramètre.

    Brèche dans un désordre sémantique total.

  • Encapsule à la fois la définition du gestionnaire d'événements et l'appel addEventListener dans une fonction recevant la cible et les paramètres.

    Il peut mélanger des étendues, mais c'est proche. Sois quand même prudent.

... Parmi de nombreuses autres solutions de contournement suggérées.

Tout ce que je veux, c'est juste passer un argument au gestionnaire d'événements pour que je puisse l'utiliser dans sa fonction, comme n'importe quelle fonction normale!

Suis-je trop demander?

23
Trovs

Hors de la boîte: il suffit de 2 lignes supplémentaires de code élégant pour résoudre cet ancien puzzle.

stage.addEventListener(MouseEvent.CLICK, onClick(true, 123, 4.56, "string"));
function onClick(b:Boolean, i:int, n:Number, s:String):Function {
  return function(e:MouseEvent):void {
    trace("Received " + b + ", " + i + ", " + n + " and " + s + ".");
  };
}

Mais plus important encore, vous devrez très probablement supprimer l'écouteur plus tard pour libérer des ressources, donc + 1 ligne pour le stocker dans une variable:

var functionOnClick:Function = onClick(true, 123, 4.56, "string");
stage.addEventListener(MouseEvent.CLICK, functionOnClick);
function onClick(b:Boolean, i:int, n:Number, s:String):Function {
  return function(e:MouseEvent):void {
    trace("Received " + b + ", " + i + ", " + n + " and " + s + ".");
  };
}

Et vous pourrez le supprimer normalement:

trace("Before: " + stage.hasEventListener(MouseEvent.CLICK));
stage.removeEventListener(MouseEvent.CLICK, functionOnClick);
trace("After: " + stage.hasEventListener(MouseEvent.CLICK));

Voici un exemple dynamique plus élaboré pour prouver son utilisation:

function onClick(s:String):Function {
  return function(e:MouseEvent):void {
    trace("The square " + s + " at x = " + e.currentTarget.x + "px was clicked");
  };
}
var myFunctions:Array = new Array();
for (var i:int = 0; i < 10; i++) {
  myFunctions.Push(onClick("#" + (i+1)));
}
for (i = 0; i < myFunctions.length; i++) {
  var square:Sprite = new Sprite();
  square.name = "sqr" + i;
  square.addChild(new Bitmap(new BitmapData(20, 20, false, 0)));
  square.x = 5 + 25 * i;
  square.addEventListener(MouseEvent.CLICK, myFunctions[i]);
  stage.addChild(square);
}

Aucune propriété via des objets dynamiques, aucune classe personnalisée, aucune fonction lâche, aucun chevauchement de portée. Juste ce que la logique attend d'elle: vous lui passez simplement des arguments.

Et pour supprimer correctement chaque auditeur, vous pouvez le faire plus tard:

for (i = 0; i < myFunctions.length; i++) {
  square = stage.getChildByName("sqr" + i) as Sprite;
  trace("Before (#" + (i+1) + "): " + square.hasEventListener(MouseEvent.CLICK));
  square.removeEventListener(MouseEvent.CLICK, myFunctions[i]);
  trace("After (#" + (i+1) + "): " + square.hasEventListener(MouseEvent.CLICK));
  stage.removeChild(square);
}

À mon humble avis, c'est la manière la plus simple mais la plus solide de le faire.

88
user1837285

voici un exemple

var s1:Boolean = true;

station1.addEventListener(MouseEvent.ROLL_OVER, function(e:MouseEvent) : void { spread(e, s1) });
station1.addEventListener(MouseEvent.ROLL_OUT, function(e:MouseEvent) : void { collapse(e, s1) });


function spread(event:MouseEvent ,bb:Boolean):void {
 if(bb != true) event.currentTarget.gotoAndPlay(2);
}

function collapse(event:MouseEvent,bb:Boolean ):void {
    if(bb != true) event.currentTarget.gotoAndPlay(18);
}
6
xbraim

Il n'est pas possible de passer un paramètre directement au gestionnaire d'événements.

La meilleure approximation est d'utiliser une méthode d'usine de fonction, IMO

// - EDITÉ: J'ai créé une classe distincte pour gérer les gestionnaires paramétrés, voir https://Gist.github.com/4124722

l'utilisation est simple:

    public class Usage extends Sprite{

        private var _handlers : ParameterizedHandlerContainer;

        public function ParameterizedEvents()
        {

            _handlers = new ParameterizedHandlerContainer();
            _handlers.registerHandler( this, Event.ADDED_TO_STAGE, someMethod, 3000 );
        }

        private function someMethod( event : Event, amount : uint ):void
        {
            trace( this, 'someMethod', amount );
            _handlers.destroyHandler( arguments.callee, event );
        }


    }

Vous pouvez maintenant enregistrer des gestionnaires paramétrés avec ParameterizedHandlerContainer#registerHandler et détruisez-les avec ParameterizedHandlerContainer#destroyHandler.

Vous pouvez transmettre n'importe quelle quantité de paramètres que vous souhaitez, mais le premier paramètre de la méthode de rappel doit être de type Événement ou un descendant.

1
Creynders

Pourquoi ne pas utiliser les signaux AS3? Passer un paramètre est alors aussi simple que:

import org.osflash.signals.Signal;

public class AlarmClock 
{
    public var alarm:Signal;

    public function AlarmClock() 
    {
        alarm = new Signal(String);
    }

    public function ring():void
    {
        alarm.dispatch("9 AM");
    }
}
0
Varnius

Je modifierais légèrement l'exemple de Creynders:

    protected function createHandler(handler:Function, ...args):Function
    {
        var h:Function = function(e:Event = null):void
        {
            trace(args);

            if (handler != null)
                handler(e);
        }
        return h;
    }

    sm.addEventListener(StyleEvent.STYLE_CHANGE, 
            createHandler(updateStyle, 1,true,"Lorem",["a","b","c"]), 
            false, 0, true);

de cette façon, vous pouvez utiliser des gestionnaires d'événements originaux et y injecter les "arguments",

vous pouvez également demander à vos gestionnaires d'avoir la signature suivante:

protected function myHandler(e:Event, ...args):void;

alors vous pouvez passer des arguments directement au gestionnaire cible:

    protected function createHandler(handler:Function, ...args):Function
    {
        var h:Function = function(e:Event = null):void
        {
            //trace(args);

            if (handler != null)
            {
                args.unshift(e);
                handler.apply(this, args);
            }
        }
        return h;
    }

meilleures salutations