备份一下资料,哈哈
?
一个多月之前,发了篇有关用flex开发了一个流程设计器原型的博文,详见http://blog.csdn.net/james999/archive/2008/09/22/2963367.aspx 。这一个多月来,主要忙于这边引擎的重构,以及Flex流程设计器的实际性开发工作。目前基本已经成形,所以将一些心得写出来,分享一下。
目前的外在表现,以及内部构架,已经与原型几乎不同了,进行了很大的重构。如下图,是目前的外观,是按照BPMN
Modeler的样式构造。当然,这个外观是很容易改变的,因为底层提供了一套基本的 Flex GEF for
Process构架。
?
在原型系统中,还只是一个Model-Figure的模式,而目前已经完全依照Model-EditPart-Figure来这个MVC思想来构架的。
?
?
接下来就这套框架的实现思路和思想,与大家分享一下。我们先从大的设计思想来入手,再讲解较为细微的设计之处。
?
?
注:以下会很多地方提到Figure这个词,在Flex中并没有这个对象,用Figure只是表示是一个图元。这个图元的实现,可以是Canvas、Image,或别的什么DisplayObject。
?
?
一定要采用MVC的构架。
?
Flex本身已经提供了很好的对UIComponent的操作,所以用Flex本身技术去实现绘图并不难,只需要把握好Drag和Mouse事件操作的影响即可。
?
但如果仅仅只是操作图形,则会使你的应用非常死板,也不便于后续扩展。特别对于“流程图”这样的依赖于应用数据的绘制操作。你需要解决Model与Figure之间的关系。――但流程图又涉及到一个Model的图形化显示,可能会存在多种显示的情况:设计视图、开发视图、监控视图等等。单一的Model-Figure这种关系,可能无法进行解耦,所以在中间增加EditPart一层,是比较理想的设计模式。
?
你能想象下面这种图,与上面的图,实现与统一套GEF for
Process构架之上吗,两套图形实现只是Model与FigureFactory做了不同的扩展。
?
?
使用“玻璃板”技术
?
最早接触Glass Pane这个名词是在Swing中接触过,在Flex中并没有这个说法。我们是用Glass Pane这种模式,在整个图形编辑的Editor上“罩”上一个Canvas,利用这个Canvas拦截所有的key和mouse的事件,拦截之后,计算当前鼠标操作正在操作的Figure对象,并分析行为――这两者一结合,即可控制当前的图形操作逻辑。
?
?
很多人开发这种Figure操作的时候,喜欢直接在UIComponent上注册MouseEvent Listener,来响应鼠标的行为。简单的图形操作,这个没有任何问题,但是对于复杂的多元图形操作,则变得非常繁琐。
?
而且,Flex本身的MouseEvent会层层穿透响应,所以过多的由Figure本身控制Event,就容易造成Event处理混乱,对于多元多层图形的情况下,这种错误一旦发生,是非常难以解决和调试的。
?
?
使用Command模式
?
这个就不多过的讲解了,图形化操作,这个Command模式是基本组成结构之一。解决undo、redo的问题。
?
?
使用Layer模式
?
这就是将各种图元,按照类别、行为、影响等等因素,放置于不同的Layer上。比如将Grid放置于最底层、连接线的layer放置在较低的Layer等等。
?
当然,实现这种Layer的技术是很简单的,每个Layer都是一个Canvas,这些Canvas按层次顺序加入到另外一个Container中即可。
?
?
Editor与EditDomain
?
这是模仿Eclipse GEF中的思路,用于记录当前操作图形的Editor相关的一些基础信息,以及暂存一些公用信息。比如当前的Command Stack、SelectionManager,Model与EditPart对应关系,以及当前的行为等等。这样每个Editor就会维护自己的工作空间对象。
?
?
大的实现模式上,大体就是这些。可能熟悉Eclipse GEF的朋友,会奇怪没有提到诸如Palette、Request、ToolEntry、EditPart Policy等等。
?
在我的这套框架中,暂时是没有的这些。一方面我对Eclipse GEF不熟,另一方面,我故意不考虑Palette的实现。对于Flex这种应用来说,提供一套Palette的基础实现意义不大,而且反而会限制Palette的展示效果开发。
?
因为有Glass Pane模式存在,所以上层开发,可以覆盖DragEnter和DragDrop等事件,来操作当图形拖入到Editor中的行为。当然,在我的Flex GEF for Process框架中,是提供了基础的ToolEntry对象的描述,但仅仅只是用于描述其所对应的需要创建的Model对象类型,而此ToolEntry是需要被放入DragSource中。
?
?
今天写到这儿,后续再讲解细化的设计和实现。