Flex:以新名称公开组件的现有事件

Posted

技术标签:

【中文标题】Flex:以新名称公开组件的现有事件【英文标题】:Flex: Expose component's existing events with new name 【发布时间】:2011-01-07 22:26:08 【问题描述】:

我创建了一个 flex 组件,其中包含多个内置 (mx) 组件,例如列表框和组合框。我的组件依赖外部数据,我需要暴露 ComboBox.enter 和 List.click 等事件来获取某些数据。

我想知道是否有任何简单的方法可以做到这一点,而无需创建我自己的自定义事件处理程序。我只是试图以不同的名称公开这些事件,以便在使用我的组件时,我可以执行以下操作:

<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml"   xmlns:com="com.*">
    <mx:Script>
        <![CDATA[
            import mx.collections.ArrayCollection;
            [Bindable]
            public var dp:ArrayCollection = new ArrayCollection(["Apple","B","C","D","E","F","G","H","I","J","K"]);
        ]]>
    </mx:Script>
    <mx:ComboBox dataProvider="dp"/>
    <mx:List/>
</mx:Canvas>

我希望能够按如下方式使用它:

<com:MyComponent listBoxChanged="getExternalData(event)" comboBoxClick="comboBoxClicked(event)"/>

我想我想要做的是将组件中的事件传播到父组件,事件被重命名。

【问题讨论】:

【参考方案1】:

您可以通过创建一个类来重定向事件,从而用最少的代码做到这一点。您想要的是一个接受源事件分派器、源事件名称、目标事件名称和目标事件分派器的类。假设您将此 EventRedispatcher 称为。这是一个完整的例子。

EventRedispatcherTest.mxml:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application 
    xmlns:mx="http://www.adobe.com/2006/mxml"
    xmlns:local="*" 
    layout="horizontal" 
     
    >

    <mx:Script>
        <![CDATA[
            import mx.events.ListEvent;

            private function doLog(event:Event):void 

                var extraInfo:String = "";

                var listEvent:ListEvent = event as ListEvent;
                if (listEvent != null && listEvent.itemRenderer != null) 
                    extraInfo = String(listEvent.itemRenderer.data);
                

                var mouseEvent:MouseEvent = event as MouseEvent;
                if (mouseEvent != null) 
                    extraInfo = mouseEvent.stageX + "," + mouseEvent.stageY;
                
                log.text += event.target.id + "." + event.type + ":" + extraInfo + "\n";
            
        ]]>
    </mx:Script>
    <mx:TextArea   id="log" />

    <local:EventRedispatcherComponent 
        id="component1" 
        listboxChange="doLog(event)" 
        comboboxChange="doLog(event)" 
        buttonClick="doLog(event)" 
        />

    <local:EventRedispatcherComponent 
        id="component2" 
        listboxChange="doLog(event)" 
        comboboxChange="doLog(event)" 
        buttonClick="doLog(event)" 
        />
</mx:Application>

EventRedispatcherComponent.mxml:

<?xml version="1.0" encoding="utf-8"?>
<mx:HBox 
    xmlns:mx="http://www.adobe.com/2006/mxml" 
    creationComplete="init()"
    borderStyle="solid"
    borderColor="#FF0000" 
     
    >

    <mx:Metadata> 
        [Event(name="comboboxChange", type="mx.events.ListEvent")] 
        [Event(name="listboxChange", type="mx.events.ListEvent")] 
        [Event(name="buttonClick", type="flash.events.MouseEvent")] 
    </mx:Metadata> 

    <mx:Script>
    <![CDATA[

        private function init():void
               
            // TODO: Create EventRedispatcher class :-)
            new EventRedispatcher(combobox, "change", this, "comboboxChange");
            new EventRedispatcher(listbox, "change", this, "listboxChange");
            new EventRedispatcher(button, "click", this, "buttonClick");
        
    ]]>
    </mx:Script>
    <mx:ComboBox id="combobox" dataProvider="[1, 2, 3]" />
    <mx:List id="listbox" dataProvider="[1, 2, 3]" />
    <mx:Button id="button" label="Text" />
</mx:HBox>

EventRedispatcher.as

package

    import flash.events.Event;
    import flash.events.EventDispatcher;
    import flash.utils.describeType;
    import flash.utils.getDefinitionByName;
    import flash.utils.getQualifiedClassName;

    public class EventRedispatcher
    
        private var targetDispatcher:EventDispatcher;
        private var targetName:String;

        private static var propertiesByEventType:Object = new Object();

        public function EventRedispatcher(sourceDispatcher:EventDispatcher, sourceName:String, targetDispatcher:EventDispatcher, targetName:String)
        
            this.targetDispatcher = targetDispatcher;
            this.targetName = targetName;

            sourceDispatcher.addEventListener(sourceName, redispatch); 
        

        private function redispatch(event:Event):void 
            var newEvent:Event = copyEvent(event);
            targetDispatcher.dispatchEvent(newEvent);
        

        private function copyEvent(event:Event):Event 
            var className:String = getQualifiedClassName(event);
            var newEvent:Event = new (getDefinitionByName(className))(targetName);          

            var properties:Array = getPropertiesForClass(event, className);

            for each(var propertyName:String in properties) 
                newEvent[propertyName] = event[propertyName];
            

            return newEvent;                                    
        

        private function getPropertiesForClass(event:Event, className:String):Array 

            var properties:Array = propertiesByEventType[className];
            if (properties != null) 
                return properties;
            

            var description:XML = describeType(event);
            properties = new Array();

            for each(var accessor:XML in description.accessor.(@access == 'readwrite')) 
                properties.push(accessor.@name);
             

            for each(var variable:XML in description.variable) 
                properties.push(variable.@name);
             

            propertiesByEventType[className] = properties;
            return properties;
        
    

【讨论】:

听起来不太聪明,但如果我知道该怎么做,我就不会问这个问题了。 @FlashFlex:我更新了示例,现在它已经完成,包含完整的组件、完整的测试应用程序和 EventRedispatcher 的实现。 谢谢,这就是我想要做的。我有自己的事件 EventRedispatcher 类,但它将旧事件包装在自定义事件中。这好多了。【参考方案2】:

这应该不会太难。

首先,您需要确定您的事件(listBoxChanged、comboBoxClick)是否需要与它们一起存储数据。 如果是这种情况,则使用存储要传递的数据的变量创建 2 个自定义事件(每个事件一个)。

在MyComponent 中,需要为ComboBox 点击事件和List change 事件添加监听器。在您的方法处理程序中,调度您刚刚创建的类型的事件。

不要忘记在组件顶部添加 2 个 Event 元标记,以便在 listBoxChanged 和 comboBoxClick 上自动完成

【讨论】:

是的,但我想用不同的名称调度事件,因为例如多个组件都有一个“click”或“creationComplete”事件。我想要更清晰的内容,例如“listBoxClicked”或“comboBoxCreationComplete”。我已经知道如何调度事件。也许你已经回答了我的问题,但我不太明白如何回答。 @FlashFlex - 您在此处的评论澄清了您的问题,您应该编辑您的问题并将这些详细信息添加到其中。【参考方案3】:

您可以通过组件元数据做到这一点:

<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml"   xmlns:com="com.*">

    <mx:Metadata> 
        [Event(name="myListChange", type="mx.event.ListChange")] 
    </mx:Metadata> 

    <mx:Script>
        <![CDATA[
            import mx.collections.ArrayCollection;
            [Bindable]
            public var dp:ArrayCollection = new ArrayCollection(["Apple","B","C","D","E","F","G","H","I","J","K"]);

            private function onListEventChange(event:ListEvent):void
                     
                  //create your own event if you need to pass data with the event.    
                  dispatchEvent(new Event("myListChange"));
            
        ]]>
    </mx:Script>
    <mx:ComboBox dataProvider="dp"/>
    <mx:List change="onListEventChange(event)/>
</mx:Canvas>

或者你可以只冒泡事件并在父组件上监听它们,尽管我不推荐这样做。

【讨论】:

【参考方案4】:

Event.type 是只读的,因此您不能使用新名称调度相同的确切事件。因此,您需要在新发送的事件中包装或复制数据。 (通常你会使用clone,但这会使事件类型保持不变。)

熊正在复制单个事件类型,但如果你有一个有限的集合,你可以做到。

public function EventRedispatcher(src:EventDispatcher, srcType:String, dest:EventDispatcher, destType:String) 
    src.addEventListener(srcType, function(e:Event):void 
       var clone:Event = cloneEvent(e, destType);
       dest.dispatchEvent(clone);
    );


// pick your favorite factoryish idiom here
private function cloneEvent(e:Event, newType:String):Event 
    if (e is MouseEvent) 
       var me:MouseEvent = MouseEvent(e);
       return new MouseEvent(newType, me.bubbles, me.cancelable, me.localX, ...);
    
    if (e is ...) 

    // default
    return new Event(newType, e.bubbles, e.cancelable);

【讨论】:

以上是关于Flex:以新名称公开组件的现有事件的主要内容,如果未能解决你的问题,请参考以下文章

FLEX:flash 组件没有收到它自己的自定义事件

Flex 页面启动事件

Flex 应用程序到组件?

如何从 Flex 3 组件发送信息?

flex 自定义组件

如何在 TypeScript v2.x 中从自定义 React 组件公开“通用”事件