但是这次我要说的 Mate Flex Framework,在网络查找了一下,虽然有一些关于 Mate的中文资料,但是基本上都介于介绍和宣传之类的问题,几乎没有关于 Mate在实际应用中的作用。
1、与Cairngorm类似也是基于event handler,但是也不同与Cairngorm,因为它没有备受争议的frontControler和频繁的使用单例模式。
2、我个人认为不太适用于大型Flex Framework Project。因为它的优势EventMap在大型项目的情况下会变成它的缺点
==================================================================================
本篇文章是结合实际的flex project来分别的说一下这个三个特性:(本篇文章只介绍EventMap)
一、EventMap(事件地图)
我不知道官方的中文叫法是什么?但是我还是比较中意这种直译的方式:事件地图。
举例子说明:
我有三个MXML文件:A.mxml、B.mxml、C.mxml。而它们之间的关系是:A包含B,B包含C。
现在我想在C中触发一个事件,而最终会反映到A中,因此我做了如下的设定。
我定义如下一个customer event:MyEvent(请看下面的片段代码)
-
public
class MyEvent extends
Event
{
-
-
public static const CLICK_ME
:
String =
"clickMe";
-
-
public
function SupporterListEvent
(
type
:
String,
bubbles
:
Boolean=
false,
cancelable
:
Boolean=
false
)
{
-
super
(
type,
bubbles,
cancelable
);
-
}
其中A.mxml里面监听了此方法。
-
this.
addEventListener
( MyEvent.CLICK_ME , myEventHandler
);
那么我在C.mxml里面触发了这个event:
-
dispatchEvent
(
new MyEvent
( MyEvent.CLICK_ME
)
);
那么通常的情况是有A与C之间没有任何关系,因此C虽然触发了这个event,但是无法正常的传递到A中(我只说通常的情况)
因此,我们需要在B.mxml里面也写上如下的内容:
-
//监听C传递过来的event
-
//注意这是在B中监听的
-
this.
addEventListener
( MyEvent.CLICK_ME , myEventHandler
);
因此当C触发MyEvent.CLICK_ME后,会被B监听,当B监听到C的事件后,再次使用
-
//注意:这是在B中触发的
-
dispatchEvent
(
new MyEvent
( MyEvent.CLICK_ME
)
);
最终由于A中已经监听了MyEvent.CLICK_ME,因此就可以收到C中触发的事件了(通过B进行了一次传递)。
虽然这样也能完成最初的目的:C → A,那么我们需要在B里面写上一些代码,这样做的缺陷是增加了coding时间,同时也降低了source的可读性。
同时也增加了A、B、C的耦合性,因此上述方案不是一个较好的resolution。
而Mate Flex Framework给我们提供的解决方案:使用EventMap
那么我们如何利用EventMap呢?
1、首先需要引入mate swc。地址:http://mate.asfusion.com/page/downloads
2、定义一个folder:maps
然后在其中定义一个EventMap:MyEventMap.mxml(片段代码如下)
-
<EventMap xmlns
:mx=
"http://www.adobe.com/2006/mxml"
-
xmlns=
"http://mate.asfusion.com/"
>
-
-
<mx
:Script
>
-
<!
[CDATA
[
-
import com.wonlen.
test.A;
-
]
]
>
-
</mx
:Script
>
-
-
<EventHandlers
type=
"{ MyEvent.CLICK_ME }"
>
-
<MethodInvoker generator=
"{ A }"
method=
"myEventHandler"
/>
-
</EventHandlers
>
-
-
</EventMap
>
请注意:
1、A:就是我我们要被触发的目标位置。
2、myEventHandler:我们之前定义的一个event handler
-
this.
addEventListener
( MyEvent.CLICK_ME , myEventHandler
);
那么我来解释一下上诉的代码:
首先在MyEventMap里面定义如下的结构:
-
<EventMap xmlns
:mx=
"http://www.adobe.com/2006/mxml"
-
xmlns=
"http://mate.asfusion.com/"
-
xmlns
:extensions=
"com.asfusion.weather.mate.extensions.*"
>
-
</EventMap
>
然后就是我们这次的重头戏了:
-
<EventHandlers
type=
"{ MyEvent.CLICK_ME }"
>
-
<MethodInvoker generator=
"{ A }"
method=
"myEventHandler"
/>
-
</EventHandlers
>
上面的结构我用中文说一下:
1、当在C中触发了dispatchEvent( new MyEvent( MyEvent.CLICK_ME ));
2、会通过EventMap”寻找到“到类型为 MyEvent.CLICK_ME的eventHandler(即上图定义的结构)
3、只后会寻找到这段结构:
它的结构是:定义了接受event handler之后的对象A,然后调用的methode:myEventHandler
最后一个步骤:
我们在这个project的主文件下面(即有
然后我们可以去掉在A中的监听:
-
this.
addEventListener
( MyEvent.CLICK_ME , myEventHandler
);
通过以上以上的代码,那么最终的过程:
当C触发MyEvent.CLICK_ME后,会通过EevntMap找到type为MyEvent.CLICK_ME的结构,触发完毕后会调用的目标对象A中的myEventHandler函数。
也就说通过EventMap,我们可以让MyEvent.CLICK_ME传递到任意地方:MXML文件里面、AS文件里面都是完全可行的。
EventMap的特点:
它会帮助你的project 和 Flex来管理这些乱七八的事件,也就是只要是在同一个project里面,我可以通过EventMap将任何两个mxml之间通过事件传导的方式联系起来。
EventMap将事件(行为)与最终事件处理(动作)联系了起来。
由于它将行为与动作绑定起来了,因此也带来了两个缺点:
1、当一个project里面有100个行为与动作的话,那么我们要在MyEventMap写100段如上的结构,这就变成了一种负担。理所当然的解决方法:我们可以把这些EventMap分开。
例如:ChatEventMap(负责聊天的EventMap)、DrawEventMap(负责绘画的EventMap)等等。
2、由于它利用了Event type来对Event进行区分,因此我们每一个行为与动作都要设定一个唯一的Event type。
如果有100个行为与动作的话,需要设定100个event name,这样的方式的确比较恐怖一些。
以上就是 Mate Flex Framework的EventMap,下次给大家介绍 Mate的Injectors(依赖注入机制),通过这种方式可以真正实现mvc的分离,而不需要使用备受Cairngorm、PureMVC用户痛苦的单例模式。
=====================================================================
经过上两篇文章的洗礼,希望大家可以对 Mate Flex framework有了一些初步的了解。那么今天我们进行 Mate Flex Framework的重头戏:Injectors(依赖注入机制)、
二、Injectors(依赖注入机制)
那么什么时候Injectors(依赖注入机制)呢?
具体可以看这里,人家已经描述的很清楚了,我就没有必要再重复一遍了:)
http://www.adobe.com/devnet/flex/articles/dependency_injection.html
如何使用 Mate Flex Framework来完成Injectors(依赖注入机制):
好吧,我们接着上篇文章的一个例子来说明一下,当C.mxml触发了dispatchEvent( new MyEvent( MyEvent.CLICK_ME ));后,会很容易的被EventMap传递到任何我想传递的地方。
代码如下:
-
<EventMap xmlns
:mx=
"http://www.adobe.com/2006/mxml"
-
xmlns=
"http://mate.asfusion.com/"
>
-
-
<mx
:Script
>
-
<!
[CDATA
[
-
import com.wonlen.
test.A;
-
]
]
>
-
</mx
:Script
>
-
-
<EventHandlers
type=
"{ MyEvent.CLICK_ME }"
>
-
<MethodInvoker generator=
"{ A }"
method=
"myEventHandler"
/>
-
</EventHandlers
>
-
-
</EventMap
>
而这个时候,我增加了一个需求:
我想让C不仅可以传递消息还想让A得到C传递过来的消息后,显示到UI上面。
例如:在A上面显示如下的内容:C跟你打了声招呼。我是A,我得到了你传递过来的消息。
为了降低A与C的耦合性,因此A是不知道C的存在的,而C也只会把自己的消息传递出去,而不会理会到底是A接受到,还是D接收到。
上面的一个需求其是就将mvc的特征体现出来到了,即显示层(v)、控制层(c)、逻辑层(m)独立分开。
okay,由于需要传递参数,所以需要改写MyEvent.as,增加一个public variable :name,代码如下:
-
public
class MyEvent extends
Event
{
-
-
public static const CLICK_ME
:
String =
"clickMe";
-
-
public
var
name
:
String;
-
-
public
function SupporterListEvent
(
type
:
String,
bubbles
:
Boolean=
false,
cancelable
:
Boolean=
false
)
{
-
super
(
type,
bubbles,
cancelable
);
-
}
注意:真正在实际应该用,不用直接采用public的方式,而是应该采用setter、getter的方式,这样可以更好的控制闭包。
okay,我们已经增加了一个变量:name,它的作用是用来保存触发者的名字。因此C.mxml的代码也需要稍微的修改一下,主要对name进行赋值。
-
var myEvent
: MyEvent =
new MyEvent
( MyEvent.CLICK_ME
);
-
myEvent.
name =
"C";
-
dispatchEvent
( myEvent
);
上面的代码比较简单,不做过多介绍。
okay,既然现在已经把名字保存起来了,那么我们需要一个逻辑对这个传入的参数进行一些处理。我们增加一个叫做business的文件(注意这里的名称与Cairngorm、PureMVC是相似而的)
然后在business文件夹下面增加一个AS文件:MyController.as,然后里面的内容如下:
-
package com.wonlen.
test.business
-
{
-
public
class MyController
{
-
-
[Bindable
]
-
public
var talk
:
String;
-
-
[Bindable
]
-
public
var
type
:
String;
-
-
[Bindable
]
-
public
var
name
:
String;
-
-
-
// ----------------------------------------------------------
-
public
function settingTalks
(
)
:
void
{
-
-
if
(
type ==
"clickMe"
)
{
-
talk =
name
+
"跟你打了声招呼。";
-
}
-
else
{
-
talk =
name
+
"离开了。";
-
}
-
}
-
}
-
}
上面的代码,也是比较简单的,增加了三个属性,type 和 name。它们的意义上分别是:
1、type:类型。
2、name:名字。
3、talk:保存经过设定后的字符串。
经过上面的修改,我们完成了事件携带值的修改和定义了一个controller性质的AS:MyController
接下来在A.mxml里面增加一些内容,例如如下的source:
-
<?xml
version=
"1.0" encoding=
"utf-8"
?>
-
<mx
:Panel xmlns
:mx=
"http://www.adobe.com/2006/mxml" title=
"A Panel"
>
-
-
<mx
:Script
>
-
<!
[CDATA
[
-
[Bindable
]
-
public
var othertalks
:
String;
-
]
]
>
-
</mx
:Script
>
-
-
<mx
:Label
text=
"{ othertalks + '我是A,我得到了你传递过来的消息' }"
/>
-
-
</mx
:Panel
>
我定义了一个变量:othertalks,它的作用是用于显示接收的内容。
那么MyEventMap应该如下修改呢?请看下面的代码:
-
<EventMap xmlns
:mx=
"http://www.adobe.com/2006/mxml"
-
xmlns=
"http://mate.asfusion.com/"
>
-
-
<mx
:Script
>
-
<!
[CDATA
[
-
import com.wonlen.
test.A;
-
]
]
>
-
</mx
:Script
>
-
-
<EventHandlers
type=
"{ MyEvent.CLICK_ME }"
>
-
<MethodInvoker generator=
"{ MyController }"
method=
"settingTalks"
>
-
<Properties
type=
"{ MyEvent.CLICK_ME }"
name=
"{ 'C' }"
/>
-
</MethodInvoker
>
-
</EventHandlers
>
-
-
<Injectors
target=
"{ A }"
>
-
<PropertyInjector targetKey=
"othertalks"
source=
"{ MyController }" sourceKey=
"talk"
/>
-
</Injectors
>
-
-
</EventMap
>
这次MyEventMap改动就比较大了,不过都是比较好理解的,我现在给大家解释一下:
-
<MethodInvoker generator=
"{ MyController }"
method=
"settingTalks"
>
-
<Properties
type=
"{ MyEvent.CLICK_ME }"
name=
"{ 'C' }"
/>
-
</MethodInvoker
>
它定义了一个段,这个段上次我应该已经讲过了,只不过上次的generator = A,而这次是:MyController而已。
让我们在回顾一下,MyController的内容:
1、一个method:settingTalks
2、三个参数:type、name(传入的两个properties)、talk(这是用于保存字符串的变量)
既然需要传入两个参数,所以我们就定义了一个属性段,它在Mate里面是专门负责用于对XXX的属性进行赋值的工作。
那么整个这个段的作用是什么?
当触发MyEvent.CLICK_ME后,同时传入了两个参数:type 和 name。然后会调用MyController里面的method:settingTalks。
当这一切都完成后,Mate就会调用里面的内容,让我们看一下的内容
-
<Injectors
target=
"{ A }"
>
-
</Injectors
>
首先注入的目标对象是A,接下来的代码;
-
<PropertyInjector targetKey=
"othertalks"
source=
"{ MyController }" sourceKey=
"talk"
/>
我们分析一下上面的代码,定义了要被注入的属性,也就是A中那个变量被进行了注入操作。
targetKey="othertalks",说明了被注入的属性是A.mxml里面的othertalks。
source="{ MyController }" sourceKey="talk":说明了使用的MyController里面的属性talk作为注入属性,即将MyController.talk 赋值给A.othertalks。
okay,这就是MyEventMap增加的内容。那么截止到目前所有增加的内容都已经说明过了。
我现在重述一下上面的内容:
当在C中触发了MyEvent.CLICK_ME后,会在EventMap里面调用MyController,并且传入了type 和 name,然后通过settingTalks,最后得到了字符串talk。
然后将MyController.talk通过注入方式赋值给A.othertalks,最终显示在A上面:C跟你打了声招呼。我是A,我得到了你传递过来的消息。
其实经过以上的coding,就形成了一些简单的mvc方式。C只负责触发MyEvent.CLICK_ME,而它无须理会到底是给谁的。
同时A也只负责接收MyEvent.CLICK_ME的消息,而无须理会具体的逻辑是什么,只是单纯的显示得到的结果。
MyController封装了主要的业务逻辑,如果一但有需求变更的话,那么我们只负责修改MyController里面的逻辑即可。
由于这个例子比较小,因此我只使用了变量来代替vo。