Чтобы передать параметр прослушивателю событий в AS3 простым способом... он существует?

Ожидаемый/псевдо-пример:

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

На протяжении многих лет (3 ~ 4), на каждом сайте, форуме, блоге, где бы я ни искал, люди говорят мне, что нет простого способа сделать это. Они обычно предлагают:

  • Добавьте слушателя в динамический объект, где вы можете установить значение для дополнительного свойства и ссылаться на него (e.target.property/e.currentTarget.property) в функции.

    Не все классы являются динамическими. Например, это не будет работать на Sprite.

  • Расширить класс объекта с помощью специального класса для добавления свойства или просто сделать его динамическим.

    Каждый раз вам нужно будет создать новый класс.

  • Использовать анонимную функцию как обработчик событий.

    Нет ссылки (и это уродливо). Чтобы удалить слушателя на свободные ресурсы, вы вынуждены делать это внутри самой функции с аргументами .callee.

  • Вызовите другую функцию, используя этот параметр, внутри обработчика событий.

    А где в вызове обработчика события идет параметр?

  • Храните обработчик событий в той же области, что и параметр.

    Нарушение общего смыслового беспорядка.

  • Инкапсулируйте как определение обработчика события, так и вызов addEventListener в функцию, получающую цель и параметры.

    Он может смешивать области, но он близок. Однако вы должны быть осторожны.

... Среди многих других предлагаемых обходных решений.

Все, что я хочу, это просто передать аргумент обработчику событий, чтобы я мог использовать его внутри своей функции, как и любая нормальная функция!

Я прошу слишком много?

Ответы

Ответ 1

Из коробки: для решения этой древней загадки требуется 2 дополнительных строки элегантного кода.

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 + ".");
  };
}

Но самое главное, вам, скорее всего, придется удалить слушателя позже на свободные ресурсы, поэтому +1 для его хранения в переменной:

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 + ".");
  };
}

И вы сможете удалить его в обычном режиме:

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

Вот более подробный, динамичный пример, чтобы доказать его использование:

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);
}

Никаких свойств через динамические объекты, без пользовательского класса, без функций, без перекрытия области. То, что логика ожидает, это: вы просто передаете ему аргументы.

И чтобы удалить каждого слушателя правильно, вы можете сделать это следующим образом:

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);
}

IMHO это простейший, но наиболее надежный способ сделать это.

Ответ 2

вот пример

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);
}

Ответ 3

Передача параметра непосредственно обработчику события невозможна.

Наилучшее приближение - это метод функции factory, IMO

//- Редакция: Я создал отдельный класс для поддержки параметризованных обработчиков, см. https://gist.github.com/4124722

просто:

    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 );
        }


    }

Теперь вы можете зарегистрировать параметризованные обработчики с помощью ParameterizedHandlerContainer#registerHandler и уничтожить их с помощью ParameterizedHandlerContainer#destroyHandler.

Вы можете передать любое количество требуемых параметров, но первый параметр метода обратного вызова должен иметь тип Event или потомок.

Ответ 4

Я бы немного изменил пример 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);

таким образом вы можете использовать оригинальные обработчики событий и вставлять в него "аргументы",

Кроме того, у вас могут быть следующие обработчики:

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

то вы можете передать аргументы непосредственно целевому обработчику:

    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;
    }

С наилучшими пожеланиями

Ответ 5

Почему бы не использовать AS3-Signals? Передача параметра так же просто, как:

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");
    }
}