自定义事件
为什么要自定义事件?
- 一个页面,肯定是有许许多多的components组成的;
- 为了方便管理和复用,我们要按照逻辑划分,把一个页面划分成多个小的页面(main包含,a包含,b包含,c是叶子节点)。
- 页面a和页面b之间,或者页面a和主页面(main)之间要相互通信,
- 通信,就意味着有页面之间有数据传递,
- 如果想在数据传递的同时,降低页面与页面之间的耦合度,就要使用事件去传递数据。
- 虽然每个component都自带默认事件,但无法满足如上情况的要求,因此需要自定义事件。
- 自定义事件一定是为了一个自定义页面所服务的(同样的,一个自定义页面也会包含许多components)
如何自定义事件?
- 首先这个class 要继承 flash.events.Event
- 构造函数,要向上传递参数:super();
- 要重写clone()方法。
- 要有,自定义的事件类型定义。
- 要有,事件要传递数据类型的定义。
- (好吧,目前总结这么多)
- 代码如下:
package events
{
import flash.events.Event;
import mx.events.FlexEvent;
import vo.Persion;
// 继承flash.events.Event
public class MyCustomPersionEvent extends Event
{
// 自定义的事件类型
public static const AGE_CHANGED:String = "ageChanged";
public static const NAME_CHANGED:String = "nameChanged";
// 事件要传递数据的定义
public var persion:Persion;
// 构造函数的向上通知
public function MyCustomPersionEvent(type:String,persion:Persion=null, bubbles:Boolean=false, cancelable:Boolean=false)
{
super(type, bubbles, cancelable);
this.persion = persion;
}
// 覆盖clone()
override public function clone():Event{
return new MyCustomPersionEvent(
super.type,
this.persion,
super.bubbles,
super.cancelable
);
}
}
}
这里面要有一些注意事项:
- “自定义事件类型”的命名规则:其实也没有什么规则啦,flex都会识别的。但是查看了,flex现有的事件类之后(参考:mx.events.FlexEvent),还是按照现有的事件类命名规则就好:
- 变量修饰:public static const
- 变量名字都是大写,单词之间,用下划线“_”连接
- 变量的值:第一个单词的首字母小写,后续的单词首字母大写,单词之间没有划线“_”连接。
- "构造函数向上通知":
- 呵呵,flex的类,构造函数里面,super没有强制在方法的第一行。个人觉得还是放在第一行吧
- super(type, bubbles, cancelable) 这句必不可少,构造函数负责向上通知,包括设定事件是否冒泡(度娘:flex事件的冒泡机制)
- 在构造event时候,type,不要随便穿入,一定要使用事先定义好的常量。在如上的类中,就要使用 MyCustomPersionEvent.AGE_CHANGED 或 MyCustomPersionEvent.NAME_CHANGED
- 一定要覆盖clone()方法:
- 当一个event被二次派发出去的时候,会调用此方法。如果没有覆盖clone()方法,那么就会调用Event.clone(),后果可想而知。
- clone()方法内部就是,调用构造函数,其参数级别,super和this,清晰可见。
自定义事件的使用和数据传递
大致使用步骤:
- 让自定义事件和一个自定义view绑定:<fx:Metadata/>
- 绑定之后,需要自身把自定义的事件,派发出去。
- 自定义事件的派发,一定是借助于一个现有的事件响应的时候。
- 详细代码如下:
自定义的view: Persionform.mxml 在form package下
<?xml version="1.0" encoding="utf-8"?>
<mx:Box xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
label="{persion.id}"
>
<fx:Declarations>
</fx:Declarations>
<fx:Metadata>
[Event(name="ageChanged", type="events.MyCustomPersionEvent")]
[Event(name="nameChanged", type="events.MyCustomPersionEvent")]
</fx:Metadata>
<fx:Script>
<![CDATA[
import spark.events.TextOperationEvent;
import events.MyCustomPersionEvent;
import vo.Persion;
[Bindable]
public var persion:Persion;
protected function aa_changeHandler(event:TextOperationEvent):void
{
persion.age = new Number(aa.text) ;
this.dispatchEvent(new MyCustomPersionEvent(MyCustomPersionEvent.AGE_CHANGED,persion));
}
protected function nn_changeHandler(event:TextOperationEvent):void
{
persion.name = nn.text;
this.dispatchEvent(new MyCustomPersionEvent(MyCustomPersionEvent.NAME_CHANGED,persion))
}
]]>
</fx:Script>
<s:BorderContainer>
<s:layout>
<s:HorizontalLayout horizontalAlign="center" verticalAlign="middle" />
</s:layout>
<mx:Form>
<mx:FormItem label="Age">
<s:TextInput id="aa" text="{persion.age}" change="aa_changeHandler(event)"/>
</mx:FormItem>
<mx:FormItem label="Name">
<s:TextInput id="nn" text="{persion.name}" change="nn_changeHandler(event)"/>
</mx:FormItem>
</mx:Form>
</s:BorderContainer>
</mx:Box>
请看:
- <fx:Metadata/> 标签,让自定义事件MyCustomPersionEvent与 Persionform.mxml 绑定到了一起:
- [Event(name="ageChanged", type="events.MyCustomPersionEvent")] 描述了,Persionform.mxml会派发events.MyCustomPersionEvent事件,事件的名称是“ageChanged”
- 注意name 所对应的值 就是MyCustomPersionEvent.AGE_CHANGED的值,type所对应的值,一定是包.类名
- 同样的 [Event(name="nameChanged", type="events.MyCustomPersionEvent")] 描述了, Persionform.mxml会派发events.MyCustomPersionEvent事件,事件的名称是“nameChanged”
- 自定义事件的派发,一定是借助于现有的事件,根据自己想要的业务逻辑给派发出去的。
- 在这里,自定义事件,有两个类型,分别针对于age修改和name修改
- age和name的textInput本身就有change事件,
- 监听change事件,借助于现有的change事件,将自定义事件(MyCustomPersionEvent)的相关类型(nameChanged || ageChanged)派发出去
- 事件的数据传递
- this.dispatchEvent(new MyCustomPersionEvent(MyCustomPersionEvent.AGE_CHANGED,persion));
- persion就是自定义的数据。
- 在这里,我只是,将age || name 的修改结果,放在person对象里面,让后让MyCustomPersionEvent事件将person对象传递出去(dispatchEvent())。
- this.dispatchEvent(new MyCustomPersionEvent(MyCustomPersionEvent.AGE_CHANGED,persion));
- 自定义事件派发成功了,并不意味着,必须要监听(就像textInput的change事件监听不是必须的,是根据自己的业务逻辑来的)。
- 如下的业务逻辑是:
- 主程序页面,button会创建多个Persionform.mxml页面实例,
- 每个Persionform.mxml页面会维护一个person对象,
- 主程序要监听每个Persionform.mxml页面的对person对象的修改情况
- 代码如下:
MainTest.mxml
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
minWidth="955" minHeight="600" xmlns:form="form.*"
>
<fx:Script>
<![CDATA[
import form.Persionform;
import mx.events.FlexEvent;
import events.MyCustomPersionEvent;
import vo.Persion;
protected function b_clickHandler(event:MouseEvent):void
{
var persion:Persion = new Persion();
persion.age = 18;
persion.name = "郭美美";
var persionForm:Persionform = new Persionform();
persionForm.persion = persion;
persionForm.addEventListener(MyCustomPersionEvent.AGE_CHANGED,ageChangedHandler);
persionForm.addEventListener(MyCustomPersionEvent.NAME_CHANGED,nameChangedHandler);
tn.addChild(persionForm);
tn.selectedChild = persionForm;
persion.id = "persion"+tn.selectedIndex;
}
private function ageChangedHandler(event:MyCustomPersionEvent):void{
if(event.target is Persionform){
var pf:Persionform = event.target as Persionform;
trace("Persionform ("+pf.label+") ageChanged:" + event.persion.age);
//this.dispatchEvent(event); // 这里二次转发event的时候,会调用内部的clone()方法
}
}
private function nameChangedHandler(event:MyCustomPersionEvent):void{
if(event.target is Persionform){
var pf:Persionform = event.target as Persionform;
trace("Persionform ("+pf.label+") nameChanged:" + event.persion.name);
}
}
]]>
</fx:Script>
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<s:VGroup gap="20" width="100%">
<s:Button id="b" click="b_clickHandler(event)" label="add">
</s:Button>
<mx:TabNavigator id="tn" width="70%" height="100%"/>
</s:VGroup>
</s:Application>
请看这里:
- persionForm.addEventListener(MyCustomPersionEvent.AGE_CHANGED,ageChangedHandler);
persionForm.addEventListener(MyCustomPersionEvent.NAME_CHANGED,nameChangedHandler);
- 为每个persionForm添加事件监听。
- 当persionForm内部派发MyCustomPersionEvent.AGE_CHANGED事件的时候,ageChangedHandler方法就会被调用
- 当persionForm内部派发MyCustomPersionEvent.NAME_CHANGED事件的时候,nameChangedHandler方法就会被调用
- 整个过程,称之:事件的监听和响应过程。
注意:
- 因为persionForm自身会派发出MyCustomPersionEvent.AGE_CHANGED 和 MyCustomPersionEvent.NAME_CHANGED事件
- 所以,想要监听MyCustomPersionEvent事件,也要在persionForm身上添加!!!!:persionForm.addEventListener(MyCustomPersionEvent.AGE_CHANGED,ageChangedHandler);
- 幸好,MainTest.mxml主程序页面里面可以得到persionForm实例,然后为每个persionForm实例添加事件监听。
- 打个比方,如果在MainTest.mxml主程序页面里面没有(或者多级页面嵌套太深,无法获得)获得persionForm实例,但是想要在主程序页面里面监听MyCustomPersionEvent事件,我们应该怎么办???
- 只能用flex事件的冒泡特性了。
- 此修改后的demo利用冒泡特性,监听自定义事件的实现,代码修改后如下(请看,代码注释部分的讲解)
自定义的view: Persionform.mxml 在form package下(事件冒泡功能版本)
<?xml version="1.0" encoding="utf-8"?>
<mx:Box xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
label="{persion.id}"
>
<fx:Declarations>
</fx:Declarations>
<fx:Metadata>
[Event(name="ageChanged", type="events.MyCustomPersionEvent")]
[Event(name="nameChanged", type="events.MyCustomPersionEvent")]
</fx:Metadata>
<fx:Script>
<![CDATA[
import spark.events.TextOperationEvent;
import events.MyCustomPersionEvent;
import vo.Persion;
[Bindable]
public var persion:Persion;
protected function aa_changeHandler(event:TextOperationEvent):void
{
persion.age = new Number(aa.text) ;
this.dispatchEvent(new MyCustomPersionEvent(MyCustomPersionEvent.AGE_CHANGED,persion,true)); // true,让该事件可以冒泡
}
protected function nn_changeHandler(event:TextOperationEvent):void
{
persion.name = nn.text;
this.dispatchEvent(new MyCustomPersionEvent(MyCustomPersionEvent.NAME_CHANGED,persion,true)) // true,让该事件可以冒泡
}
]]>
</fx:Script>
<s:BorderContainer>
<s:layout>
<s:HorizontalLayout horizontalAlign="center" verticalAlign="middle" />
</s:layout>
<mx:Form>
<mx:FormItem label="Age">
<s:TextInput id="aa" text="{persion.age}" change="aa_changeHandler(event)"/>
</mx:FormItem>
<mx:FormItem label="Name">
<s:TextInput id="nn" text="{persion.name}" change="nn_changeHandler(event)"/>
</mx:FormItem>
</mx:Form>
</s:BorderContainer>
</mx:Box>
MainTest.mxml(监听冒泡的事件)
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:form="form.*"
minWidth="955" minHeight="600"
creationComplete="application1_creationCompleteHandler(event)">
<fx:Script>
<![CDATA[
import form.Persionform;
import mx.events.FlexEvent;
import events.MyCustomPersionEvent;
import vo.Persion;
protected function b_clickHandler(event:MouseEvent):void
{
var persion:Persion = new Persion();
persion.age = 18;
persion.name = "郭美美";
var persionForm:Persionform = new Persionform();
persionForm.persion = persion;
//persionForm.addEventListener(MyCustomPersionEvent.AGE_CHANGED,ageChangedHandler);
//persionForm.addEventListener(MyCustomPersionEvent.NAME_CHANGED,nameChangedHandler);
tn.addChild(persionForm);
tn.selectedChild = persionForm;
persion.id = "persion"+tn.selectedIndex;
}
private function ageChangedHandler(event:MyCustomPersionEvent):void{
if(event.target is Persionform){
var pf:Persionform = event.target as Persionform;
trace("Persionform ("+pf.label+") ageChanged:" + event.persion.age);
//this.dispatchEvent(event);
}
}
private function nameChangedHandler(event:MyCustomPersionEvent):void{
if(event.target is Persionform){
var pf:Persionform = event.target as Persionform;
trace("Persionform ("+pf.label+") nameChanged:" + event.persion.name);
}
}
protected function application1_creationCompleteHandler(event:FlexEvent):void
{
// 假设MainTest.mxml, 没有获得每个persionForm实例,那么将事件的监听,添加到自身上来!
this.addEventListener(MyCustomPersionEvent.AGE_CHANGED,ageChangedHandler);
this.addEventListener(MyCustomPersionEvent.NAME_CHANGED,nameChangedHandler);
}
]]>
</fx:Script>
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<s:VGroup width="100%" gap="20">
<s:Button id="b" label="add" click="b_clickHandler(event)">
</s:Button>
<mx:TabNavigator id="tn" width="70%" height="100%"/>
</s:VGroup>
</s:Application>
我对事件冒泡的个人理解:
- 事件冒泡的含义就是事件会向上传递。
- 叶子节点view的事件,如果派发(dispatchEvent())的时候,可以设置bubbles:Boolean=true让其冒泡。
- 举个例子:
- main页面,包含a页面,a页面包含b页面,b页面会包含z页面。
- z页面派发一个事件,并让该事件冒泡。
- 如果main, a, b 页面都监听了该事件。
- 当事件派发出来的时候, b, a , main 页面都会响应,
- 其事件的传递循序,是根据其层级关机,逐级向上传递,z-> b -> a -> main
Persion.as
package vo
{
[Bindable]
public class Persion
{
public var id:String;
public var age:int;
public var name:String;
public function Persion()
{
}
}
}
运行截图:
我的操作循序是:
- persion0,先修改age,再修改name
- persion1,先修改age,再修改name
- persion2,先修改age,再修改name
控制台显示结果:
Persionform (persion0) ageChanged:181
Persionform (persion0) nameChanged:郭美美1
Persionform (persion1) ageChanged:182
Persionform (persion1) nameChanged:郭美美2
Persionform (persion2) ageChanged:183
Persionform (persion2) nameChanged:郭美美4
Persionform (persion0) nameChanged:郭美美1
Persionform (persion1) ageChanged:182
Persionform (persion1) nameChanged:郭美美2
Persionform (persion2) ageChanged:183
Persionform (persion2) nameChanged:郭美美4