NanoUI第二弹,如何将图形序列化和反解析
????????????????????????????????? NanoUI第二弹:如何将图形序列化和反解析
?????????????????????????????????????????????????????? 大漠穷秋 版权所有
??????????????????????????????????????????????????????? 2011-05-28 周日
??? 书接上回,在第一篇文章中,介绍了NanoUI的详细设计和核心功能示例。其中遗留了一个非常关键的问题:界面图元的序列化和反解析。本篇按照从易到难的顺序详细解析其用法、原理、关键点。
目录
1.??? 先上实例??? 2
1.1.??? 序列化??? 4
1.1.1.??? 序列化鼠标绘制的组件??? 4
1.1.2.??? 序列化定制组件??? 13
1.2.??? 反解析??? 16
2.??? 核心原理??? 17
2.1.??? 反解析:AS3的反射??? 17
2.1.1.??? 核心原理??? 17
2.1.2.??? 注意点??? 18
2.2.??? 序列化:图形和XML??? 20
2.2.1.??? 核心原理??? 20
2.2.2.??? 注意点??? 21
3.??? 注意点和设计改良??? 22
1.??? 先上实例
??? 首先请注意组件序列化的核心流程:
??? 前台界面:组件解析成XML字符串?通过SysDataMgr提交到后台JSP;
??? 后台:根据前台的文件名参数,在服务端写XML文件。
??? 在上一篇文章的基础上,本篇新增两个JSP文件:
?
?
??? getFileList.jsp用来读取目前已经保存的页面文件;
??? savePage.jsp用来写新文件。
??? 这两个JSP的代码非常简单,列举如下:
??? getFileList.jsp:
?
??? savePage.jsp:?
??? 请注意一下默认读写文件的目录,服务端会把XML文件都写在应用根目录下的modules目录下:
?
?
??? 此路径当然是可以改变的,找到以上两个JSP文件中对应的部分,改成你需要读写文件的路径即可。当然,最好的方式是把这个路径做成配置文件,为了简化后台的代码,这里就不做这个配置了。毕竟,这里关注的是前端Flex的应用,而不是怎么去开发服务端。
1.1.??? 序列化
1.1.1.??? 序列化鼠标绘制的组件
??? 以下例子会涉及到两个mxml文件,它们是位于examples/common/目录下的SavaPage.mxml和OpenFile.mxml。这两个文件的代码都不多,列举如下:
??? SavaPage.mxml???? OpenFile.mxml
?
??? 显然,这两个文件中最精华的部分,一个是把图元解析成XML的部分,一个是把收到的XML字符串解析成图元的部分。
??? 实质上这两个文件自己并没有干活,而是把序列化和反解析的过程代理给了工具类CompXmlParser来完成:
?
?
??? CompXmlParser提供了两个核心工具函数xmlEncode()和xmlDecode()分别对应序列化和反解析两个过程。CompXmlParser的实现代码稍显复杂,在第二小节中会详细解析其中的核心原理,这里先来看使用效果:
?
?
???????????????????????????????? 在界面上用鼠标绘制两个图形
?
?
???????????????????????????????? 点击“保存”按钮弹出界面
?
?
?????????????????????????????????????????????? 保存数据成功
?
?
???????????????????????????????????????? 服务端创建的XML文件
?
?
????????????????????????????????????? 查看一下XML文件的内容
??? 从XML文件的内容很容易理解这里的过程,保存的时候会把图元的属性都获取出来,这些属性完整描述了图元的特性,因此,在后面加载的时候,利用这份XML文件一定可以把图元重新恢复出来。
??? 当然,把图元序列化成XML串的过程并不是那么简单的事情,里面会涉及一定的取舍和包装,反解析的过程也是如此,在后面的第二小节中会详细解析。
???
1.1.2.??? 序列化定制组件
??? 所谓“定制组件”,指的是使用FlashCS4制作的元件,然后以SWC库的方式导入到Flex应用中的那些组件。显然,这类组件和使用鼠标直接在“画板”上绘制的东西实质上是不同的。但是,序列化的过程和前面一模一样,还是用SavaPage.mxml这个公共的组件来保存界面,并不需要修改任何代码。请看运行状况截图:
?
?
????????????????????????????????? 拖一台主机到界面上
?
?
?????????????????????????? 保存到后台
?
?
???????????????????????????????????????? 新增的文件
?
?
??????????????????????????????????? 依然是一份XML文件
??? 当然,如果你需要,可以同时在界面上用鼠标绘制和定制组件,保存的过程是一样一样的:
?
?
????????????????????????????????????? 混合模式
?
?
?????????????????????????????????????? 保存数据成功
?
?
??????????????????????????? 再来一个带连接线的
?
?
?????????????????????????????????????????????? 再来一个流动的连接线
??? 注意:以上的“流动连接线”和简单的箭头线是不同的,流动连接线是更绚丽的一种连接效果,它内部的小块块可以“流动”起来,产生一种“数据流”的效果。并且,这种连接线的流速、颜色、宽度都可以根据实际数据流的状况发生相应的变化。因此,这个组件非常适合用来制作网络拓扑图。在后续的文章中将会详细解析这一组件的实现原理。
??? 总结:从以上例子可以看出,对于使用NanoUI的代码来说,序列化鼠标绘制的组件和序列化定制组件的代码是完全一样的。因此,应用层的代码不会感知到底层处理的不同。
1.2.??? 反解析
??? OK,前面我们已经把很多界面保存到了服务端:
?
?
??? 现在,就可以尝试把它们“加载”回来,在页面上恢复出“画面”了,请自行运行示例查看效果。
2.??? 核心原理
??? 本节前置知识:熟练使用AS3操纵XML数据。(参考本文附件《flash官方指南》第十一章)。
??? 通过上一节中的例子,我们已经可以轻松进行画面到XML字符串之间的双向转换,那么这个过程的核心原理是神马呢?本节将会从易到难解析这一过程。
??? 本节实质上是解析CompXmlParser和SysClassMgr这两个工具类的实现思路,所有的序列化和反解析过程都是由这两个核心工具类配合完成的。
??? 第一节中的例子是先序列化,然后再反解析,而本节的目的是解析这些操作背后原理,因此我们需要采用的过程恰好相反。只有明确了在反解析的时候需要哪些信息,才能指导序列化的过程如何进行处理。
??? 以练功类比,所谓“设计”,是一个“意领气行”的过程;而“实现”的过程则是“气领意行”。做好一个设计,需要对底层的机制有非常充分的理解,并且需要根据实际应用做出取舍和妥协,这也是为什么能做设计的人如此之少,而代码工总是如此之多的原因。当然,这里并没有贬低Coder的意思,只是为了说明这两个过程的差异。毕竟,图纸画好了还需要有技艺精湛的工人去把它变成可以触摸的产品,工匠的技艺的高低会直接决定最终产品的质量。
??? OK,以上是一坨屁话,下面开始对核心原理和设计过程进行解析。
2.1.??? 反解析:AS3的反射
2.1.1.??? 核心原理
??? 我们知道,在Java中有“反射”一说,通过一个类的类名可以获得类的类型定义信息,就像这样:
??? Class.forName(classNameStr);
??? 在AS3中也有类似的机制:
??? flash.utils.getDefinitionByName(className);
??? getDefinitionByName这个工具函数会返回一个类型为Class的对象,我们使用这个对象就可以获得一个类的新实例,一般的用法如下:
??? var MyClass:Class= flash.utils.getDefinitionByName(className);
??? var instance: MyClass=new MyClass();
??? 这样一来,只要我们拿到了一个类的名称,就可以根据这个名称创建类的实例。再来复习一下第一节中所产生的XML的内容:
?
?
?????????????????????????????????????????????? XML文件里的类型信息
??? 显然,我们只要把这样的XML字符串加载到前台,然后根据type属性所指定的类型名,就可以重新创建出图形实例啦!!!在实例创建之后,再把其它属性设置到新建的实例上,这样就可以恢复出图元啦!!!
??? 但是,大湿有云:理想很丰满,现实很骨感。虽然我们明白了前进的大方向,但是实际的实施过程还是困难重重,以下是把理论投入实际所需要的注意点。
2.1.2.??? ??? 注意点
2.1.2.1.??? ??? 第一个注意点:编译器问题
??? 我们首先遭遇的是该死的flash编译器,比如你定义了一个类:com.namo.Test,但是在你的应用中并没有使用Test这个类,flash编译器为了保证编译出来的swf文件足够小巧,它不会把Test编译到swf文件里面去。这时候问题就产生了:即使你知道了Test的完整类型名,还是无法通过flash.utils.getDefinitionByName(className)获取到Class对象!!!
??? 那么,如何强制编译器把这些类都编译进来呢????
??? 我们在SysClassMgr中定义一些空属性,这些属性分别指向我们需要反射的所有类型。虽然我们实际上并没有创建实例( 不占内存),但是编译器却以为我们使用了这些类,通过这样的“把戏”就可以顺利戏弄编译器,让它把我们需要反射的类都编译进来。详细代码实现请看SysConfMgr:
????
?
?????????????????????????????????????????????????????? SysClassMgr中的小把戏
2.1.2.2.??? ??? 第二个注意点:构造函数的参数
??? var MyClass:Class= flash.utils.getDefinitionByName(className);
??? var instance: MyClass=new MyClass();
??? 如上所示,我们拿到MyClass这个类型之后,使用new MyClass()的方式创建实例,但是,如果MyClass定义的构造函数有参数,此时就会抛出异常啦!!!
??? 这位说了,那还不简单,我们模仿Java,给每个类加一个默认的无参构造函数不就得了嘛。
??? But很可惜,AS3并不支持函数重载,构造函数也不行!!!因此,如下的代码是无法通过编译的:
????
????
?
????????????????????????????????????????????? 产生的XML字符串
??? 嗯哼,有了这样的原生工具,我们距离目标似乎又近了一步,但是道路依然不是那么平坦。
2.2.2.??? 注意点
??? 仔细观察以上的XML字符串你会发现,原生工具产生的XML非常庞大!!!里面包含了大量的信息,而我们的应用中并不需要这些东西。
??? 这位可能会说:不用也没关系啊,放在里面不管就行了。
??? 当然,如果你是内网应用,对网络带宽和最终产生的XML文件体积没有什么限制,直接无视这些冗余信息也是可以的。但是,如果是门户型应用,对传输速度和文件体积会有比较严格的要求。因此,我们最好还是对这些信息做出剪裁,这就是为什么在CompXmlParser.xmlEncode头部会有这样一堆delete调用的原因:
?
?
?
??????????????????????????????????????????????????? 删掉我们不需要的信息
??? 通过这一组delete,我们可以删掉大量冗余的信息---不是全局。当然,如果你愿意,还可以进行进一步的细致剪裁,在剪裁之前需要对产生的XML结构做出更详细的分析,以确定哪些信息需要保留。
??? 这里,CompXmlParser为了能给反解析的过程带来便利,并没有使用原生获得的XML结构,而是在剪裁之后,把信息格式化成了这样的结构:?
??? 所有的属性都被格式化成name、value、type这三个属性。经过这样一番处理之后,所获得的XML字符串的体积已经大大减小,此时我们就可以把它提交到服务端啦!!!
3.??? 注意点和设计改良
??? 至此,我们已经完成了图元序列化和反解析的整个流程。当然,在整个处理链条中一定还存在可以重构和改良的地方。这些,就留给聪明的你去完成吧!!!
??? NanoUI系列文章还会有如下主题,近期释出,敬请关注:
??? 工具函数的设计;
??? 如何设计高级语义事件机制;?????? UI组件的继承结构以及UI组件一般化设计;
??? 如何设计自己的拓扑布局算法;
??? And so on...
1 楼 50980487 2011-06-08 很好很强大...学习了... 2 楼 fnet 2011-06-23 有些问题一下子豁然开朗了,学习了~。 3 楼 wangyj0898 2011-06-23 讲的好详细啊,不过工作中没有用到,先了解下! 4 楼 pestwei1 2011-07-04 太强大了啊 ,顶啊,再顶,还是要顶,楼主的无私奉献
???
??? 有相关兴趣者可入脚本娃娃3号群进行讨论:91508669。注意:请仔细阅读脚本娃娃在线社区的交流协议,违者一律杀无赦。
??? 最后的最后,请不要尝试联系哥,哥只是一个传说。