将参数传递给事件监听器/处理程序

10
如何通过事件监听器传递参数/变量?我曾经使用匿名函数相当有效地解决了这个问题;这是一种非常简单的解决方案,但归根结底,它感觉像是一个巨大的功能漏洞,我认为这种功能应该本来就提供。
通常情况下,生活会继续下去,但命运似乎总是如此,现在我需要删除监听器,但在使用匿名函数时这样做有点奇怪。因此,我再次尝试找出如何通过事件侦听器传递参数,以便可以通过引用函数来删除事件侦听器。
尽管这看起来很奇怪,但我也克服了这个问题并找到了解决方法,但我不喜欢它,也厌倦了使用它。在我看来,这是代码异味。但它像魔法一样工作。我将变量、对象或任何其他东西存储在派发 MovieClip 上。因此,如果我正在循环遍历数据数组,动态生成缩略图,我只需将数据变量(通常是具有多个属性的对象)存储在实际的缩略图 MovieClip 中。然后,我可以通过引用
event.target.data
在事件侦听器方法中访问所有数据。在这个示例中,“数据”是保存我想要的信息的变量的名称。因为当我不使用这个方法时,会出现另一个问题,那就是当我循环遍历数组并生成缩略图以查看大图片时,索引不一致。在循环结束时,所有缩略图都将使用 “i” 的最后一个索引打开图像。所以如果我有一个长度为 12 的数组,它们都会加载第 12 张图片,无论你点击哪个缩略图。将数据存储到 MovieClip 本身中会创建一个永远不会更改的坚实参考。
这困扰着我有一段时间了。基本上我想知道的是,这是好的做法吗,是否有更好的解决方案?

以下是一些简要示例。如有必要,我可以发布更详细的示例。所有示例都描述了单击时加载大图像的缩略图。 不使用匿名函数(问题):
tempThumb.addEventListener(MouseEvent.CLICK, loadImage);

public function loadImage(_event:MouseEvent):void  
{
    // 我没有变量_url,并准备用冷刀热浴
}

使用匿名函数:

tempThumb.addEventListener(MouseEvent.CLICK, function(_event:MouseEvent) { loadImage("large.jpg"); } );

public function loadImage(_url:String):void  
{
    // 我获得了变量_url,并收起了剃刀
}

不使用匿名函数,而是使用我臭名昭著的小矮人技巧,将数据存储到MovieClip中并分派事件

tempThumb.data = "large.jpg";
tempThumb.addEventListener(MouseEvent.CLICK, loadImage); public function loadImage(_event:MouseEvent):void { trace(event.target.data); // 我可以访问该变量 }

我对编程术语不熟悉,所以我称上述为小矮人技巧。将变量存储/隐藏在对象中以供稍后使用/访问。它解决了我的所有问题,并且表现出色。但是,如果有更好的方法,我想知道那种方法。

8个回答

2

是的,我知道你的意思。我的做法是扩展事件并分派自定义事件。这样我可以在自定义事件中创建包含所需数据的属性。这种方法看起来很干净,但确实需要一些努力。虽然不太理想,但我不知道有更好的方法。


1

(我认为你会想要阅读这个答案。)

关于你的疑问,你自己感觉到了:你的小矮人很臭(双关语)。试想一下,如果它是一个 Sprite 而不是 MovieClip。它不是动态的,所以你不能添加属性,除非你像 Richard 说的那样做(链接)

以下是基于你的示例的更好的解决方案:

tempThumb.addEventListener(MouseEvent.CLICK, loadImage("large.jpg"));

public function loadImage(_url:String):Function {
  return function(_event:MouseEvent):void {
    // Now you have both variables _url and _event here!
  }
}

但是您希望能够删除侦听器,因此:

var functionLoadImage:Function = loadImage("large.jpg");
tempThumb.addEventListener(MouseEvent.CLICK, functionLoadImage);

public function loadImage(_url:String):Function {
  return function(_event:MouseEvent):void {
    // Now you have both variables _url and _event here!
  }
}

//trace(tempThumb.hasEventListener(MouseEvent.CLICK));
tempThumb.removeEventListener(MouseEvent.CLICK, functionLoadImage);
//trace(tempThumb.hasEventListener(MouseEvent.CLICK));

很好的移除监听器 ^^。 - Zouhair Elamrani Abou Elassad

1

匿名函数最好避免使用,因为它们可能会使代码笨拙,并有时会导致内存泄漏。如果我负责分派事件,我会使用自定义事件,但这里并非如此。

在这种情况下,我会将URL和电影剪辑存储在两个单独的类级数组中,并使用

var url:String = urlArray[mcArray.indexOf(event.target)];

在事件处理程序中获取URL。

并不是说将数据存储在movieclip中有什么问题,但我认为这不是一个好的选择。然而,即使最佳实践会让事情变得困难,也不应该盲目遵循 - 选择权在你手中。


1

如果您不喜欢将数据存储在目标成员中,可以使用所谓的“委托”。两者都没有更好的选择,这主要取决于个人偏好。

以下是一个委托类的示例(没有内置内容):http://www.actionscript.org/resources/articles/205/1/The-Delegate-Class/Page1.html

如果事件分派器不是您自己的类,则这可能是最好的选择。否则,在您自己的类中存储变量也不是一个坏主意,这只是面向对象编程的风格。

干杯。


我发现阅读“没有哪个比另一个更好。这主要是偏好。”让人感到非常安慰 :)那个Delegate类很有趣。我意识到,这个问题似乎是关于优雅的问题。我将更新标签。 - Daniel Carvalho

1
如果MovieClip需要跟踪额外的信息,当它被点击时你需要知道,那么我会使用一个更正式的版本来实现你的Leprechaun技术。
我会创建一个名为SpecialImage的新类,它扩展了MovieClip,并具有用于存储要加载的URL的属性。
根据您创建SpecialImages的方式,您可以在代码中创建它们,或将库中的MovieClip链接到此类。
这基本上是一个高档版的leprechaun,但由于信息直接与图像MovieClip相关,因此将它们一起存储似乎是最好的选择。

这是一个有趣的想法,我从未考虑过将MovieClip链接到自定义类。为其创建一个类听起来像个好主意。虽然我没有为此创建一个类,但它在某种程度上——虽然不如你的想法那么正式——已经被规范化了。我确实需要跟踪额外的信息,比我给出的示例中使用url更多,并且我会即时创建一个对象,其中包含许多不同的属性,存储各种值。我不喜欢将MovieClip链接到类的唯一原因是,我通常喜欢通过代码最小化对.fla文件的所有依赖性,通过控制一切。 - Daniel Carvalho
1
我认为能够将MovieClips链接到自定义类是Flash的一大优势。您可以兼顾Flash设计师和代码灵活性的优点。 - Richard Garside
然而,如果您不使用Flash设计师来制作原始的loadImage MovieClip,则可以将所有使其成为可能的逻辑封装到类中,并在代码中实例化它。 - Richard Garside
是的,你说得对,这将发挥Flash的强大优势。 - Daniel Carvalho

0

你可以使用以下方法,这基本上是传递自定义数据的更清晰的方式。

example code:
public function myCustomEvenListener(e:MouseEvent,myCustomData:Object)
{
//Do whatever you wnat with myCustomData Object here...
}
//this is the function where you add event listeners to components..
public function init():void
{
var myCustomObject:Object=new Object();
myCustomObject.url="http://xyz.com/image.jsp";
myCustomObject.anydata="You can Add anything here";

mycomponent.addEventListener(MouseEvent.CLICK,function (e:MouseEvent) : void {
                  myCustomEventListener(e,myCustomObject);
            });

}

这只是使用匿名函数的另一种方式而已...


是的,我看得出你在做什么。使用各种技巧的组合,但正如你所说,这只是另一种使用匿名函数的方式。对我来说,这感觉有些冗长。我认为每种方法,扩展或Leprechaun,都是很好的独立方法,但合并在一起感觉像是额外的不必要的努力,牺牲了一眼就能读懂的易读性。 - Daniel Carvalho

0

最好的做法可能是创建一个自定义类,该类扩展MovieClip以用于您的缩略图,并使该缩略图分派一个自定义事件类,该事件类具有存储数据的位置:

创建一个自定义的URLEvent,可以将url属性与事件一起存储。

package{

import flash.events.Event;

public class URLEvent extends Event{

    public var url:String;
    public static const CLICK:String = "URLEventClick"

    public function URLEvent(type:String, url:String = "", bubbles:Boolean = false, cancelable:Boolean=false){
        super(type, bubbles, cancelable);
        this.url = url;
    }

    override public function clone():Event{
        return new URLEvent(type,url,bubbles,cancelable);
    }
  }
}

创建一个自定义缩略图类,可以存储“url”变量,并在单击时发送URLEvent:
package{

import flash.display.MovieClip;
import flash.events.MouseEvent;

public class Thumbnail extends MovieClip{

    protected var url:String;

    public function Thumbnail(url:String){
        this.url = url;
        addEventListener(MouseEvent.CLICK,clickHandler);
    }

    //the thumbnail captures its own click and dispatches a custom event
    protected function clickHandler(event:MouseEvent):void{
        dispatchEvent(new URLEvent(URLEvent.CLICK,url));
    }

}

现在你可以使用你的缩略图类来加载每个图片:

tempThumb.addEventListener(URLEvent.CLICK,tempThumbClickHandler);

function tempThumbClickHandler(event:URLEvent):void{
//we can get the url property direct from the URLEvent
    loadImage(event.url);
}

这种方法是更好的面向对象编程实践,因为您不需要知道event.target的对象类型 - 您可以根据事件类型确定将使用哪种类型的数据发送每个事件。


0
如果我正在寻找这个功能,而且我经常这样做,我会将tempThumb作为一个带有url属性的类。然后:
MyThumbClass(event.target).url //in the MouseEvent.CLICK handler

强类型、易访问、简洁语法、低复杂度。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接