以简单的方式将参数传递给 AS3 中的事件侦听器......它存在吗?
Posted
技术标签:
【中文标题】以简单的方式将参数传递给 AS3 中的事件侦听器......它存在吗?【英文标题】:To pass a parameter to event listener in AS3 the simple way... does it exist? 【发布时间】:2012-11-09 06:56:21 【问题描述】:预期/伪示例:
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)。 p>
并非所有类都是动态的。例如,它不适用于 Sprite。
使用自定义类扩展对象的类以添加属性或使其动态化。
您必须每次都创建一个全新的调整类。
使用匿名函数作为事件处理程序。
没有参考(而且很丑)。要删除侦听器以释放资源,您必须使用 arguments.callee 从函数本身内部执行此操作。
在事件处理程序中使用参数调用另一个函数。
参数在事件处理程序调用中的位置是什么?
将事件处理程序与参数保持在同一范围内。
破坏了整个语义混乱。
将事件处理程序定义和 addEventListener 调用封装在一个接收目标和参数的函数中。
它可以混合范围,但它是一个接近的。不过,你必须小心。
...在许多其他建议的解决方法中。
我想要的只是传递一个参数给事件处理程序,这样我就可以在它的函数中使用它,就像任何普通函数一样!
我是不是要求太多了?
【问题讨论】:
编辑了我的答案以使用一个单独的类来处理处理程序的创建、存储和注册。 【参考方案1】:无法将参数直接传递给事件处理程序。
最好的近似是使用函数工厂方法,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 类型或后代类型。
【讨论】:
编辑了我的答案,将其抽象化并添加了删除处理程序的可能性【参考方案2】:我会稍微修改一下 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;
最好的问候
【讨论】:
【参考方案3】:开箱即用:只需 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);
恕我直言,这是最简单但最可靠的方法。
【讨论】:
当然是。它实际上是“面向安全的”:函数的生命周期一直持续到你得到它的返回监听器,它的引用可以保存在一个变量中,以便以后确定删除。您可以控制一切。【参考方案4】:为什么不使用 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");
【讨论】:
【参考方案5】:这是一个例子
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 中的事件侦听器......它存在吗?的主要内容,如果未能解决你的问题,请参考以下文章
如何将令牌存储作为参数传递给 Symfony 3.4 中的事件监听器