Чтобы передать параметр слушателю событий в 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, например.

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

    Вам придется каждый раз создавать новый класс настройки.

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

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

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

    И куда в вызове обработчика событий уходит параметр?

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

    Нарушение тотального семантического беспорядка.

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

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

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

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

Я спрашиваю слишком много?

5 ответов

Из коробки: для решения этой древней головоломки требуется всего лишь 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);
}

ИМХО, это самый простой, но самый надежный способ сделать это.

вот пример

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

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

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

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

Наилучшее приближение - использование метода фабрики функций, IMO

// -EDITED: я создал отдельный класс для поддержки параметризованных обработчиков, см. 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 или потомок.

Я бы немного изменил пример Крейндерса:

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

с уважением

Другие вопросы по тегам