发表时间:2011-05-22
最后修改:2011-06-06
自制Flex开发框架NanoUI
---核心理念:小巧而犀利
大漠穷秋2011-05-21
1. NanoUI概述
1.1. NanoUI的设计目标
Nano是“小”、“迷你”的意思,因此诚如这个名称所包含的意义,NanoUI是一个“微小的”、结构松散的框架。或许叫“框架”有些装十三,我更愿意把它看做一个工具集。
NanoUI的目标是为Flex开发提供一组好用的工具类:
传输层的JSON/XML工具类;
事件工具类;
定时器工具类;
封装URLRequest使它变得更好用,编码更爽快;
提供强大的页面画板功能,可以使用鼠标直接在页面上绘图或者直接通过鼠标拖拽的方式绘制组件,并且,可以把页面绘制的图形导出成XML格式的字符串存储到后台,并且可以从后台加载这些XML文件恢复出一副“画面”。可以使用此功能实现“页面画板”、“流程图”、“网络拓扑图”等等绚丽的功能。因此,这是NanoUI最最核心的功能,
在设计过程中,NanoUI参考了Ext的很多设计思想,并且在一些工具类中直接使用Ext的代码。如果你熟悉Ext框架,那么使用NanoUI将会是非常爽快的体验,基本上不会遭遇任何大的学习阻力。
1.2. NanoUI不做什么
NanoUI不会提供对Flex原生组件的紧密封装,因为我们觉得那样做是非常不理智的行为,一旦Adobe大幅度更新Flex组件,我们将会被迫重新修订我们自己的代码。而且,从目前的情况看,这种状况会带来大量的兼容性问题。因此,除了扩展Panel提供两三个通用的组件和皮肤之外,NanoUI不会扩展Flex提供的原生组件。
另外,从个人感情上说,我非常不喜欢所谓“紧封装”的东西,那种包得像粽子一样的所谓“类库”和“框架”总是在无情地剥夺我们Coding的乐趣,企图把我们变成毫无价值的代码民工或者维护工程师,法克them!!!
因此,NanoUI从未致力于设计神马超级牛叉的框架或者类库,我们只希望提供一组犀利的小刀或者积木,你可以拿着它们任意组合、定制、修改,从而搞出你自己的应用。你也可以完全不理会NanoUI的提供工具类,只是参考这些代码和设计理念,从而搞出出你自己的类库。
1.3. 你可能需要的开发工具
你可能需要这些开发工具:
闪客精灵:用来反编译swf ,当你看到一些好用的 flash影片的时候,可以用这个工具把里面的图形资源神马的提取出来。
FlashCS4:用来制作flash并导出swc包供FlashBuilder使用,这个没有人不知道吧?
FlashBuilder4:嗯哼,这是FlexBuilder 的升级版,增加了很多好用的特性,比如代码自动重构等等。不得不说,FlashBuilder比之前的FlexBuilder要好用多了啊!!!缺点是占的资源比较多,编译过程会比较慢,因此最好的方式是把自动编译选项去掉,在Code完之后用Ctrl+B手动编译。另外,把其它不用的工程都关闭掉也会有助于提升编译速度。
注释问题
别让这货自动构建
FlashDevelop:开源的AS3和Flex开发工具,小巧而强大,主要利用它的注释自动生成功能。虽然FlashBuilder是架构在Eclipse之上的工具,但是很多Eclipse好用的贴心功能被它搞得面目全非,连自动完成注释功能都被弄没了,悲催啊,有木有!!!
flashdevelop
Adobe IllustratorCS5:这是Adobe的一个矢量图制作工具,用它导出FXG格式的文件可以直接供FlashBuilder使用。当然,主要还是用它来制作矢量图标。
TourdeFlex:一份比较好的Flex示例文档。
以上工具都可以从网上google到,某些工具体积相当庞大,本文就不提供了。其中某些工具可能需要序列号神马的,怎么处理你们都懂的,Good luck baby!!!
当然,你最需要的还有一份完整的Flex文档,由于Adobe发布的文档存在一定程度的混乱,因此,拥有一份完整好用的AS3和Flex文档将会让你的开发过程如虎添翼。本文最后提供一份比较完善的AS3和flex文档,请下载后放到你的Tomcat中运行。
2. NanoUI的设计细节
2.1. 包结构描述
NanoUI的包结构
NanoUI一共分了11个包,它们的大体功能如下:
core:
核心包。
这个包中有一组以Sys打头的类,提供如下功能,“配置文件处理”、“原生事件监听处理”、“键盘按键监听”、“数据加载器”、“景深处理”。
另外一些类和NanoUI的可视组件设计相关,它们是JComponent,这个类是所有NanoUI组件的基类。如你所知,NanoUI最强大的功能是图形对象的处理,因为它初期就是针对处理鼠标绘图、流程处理、网络拓扑这些功能而设计的。
入手NanoUI的初期,基本只会涉及SysConfMgr和SysDataMgr两个类,SysConfMgr被设计用来处理整个系统的配置文件。这个类只有两个方法:initConf()和getUrl()。initConf一般会在系统初始化时调用一次,它从后台指定位置加载一份配置文件,这份配置文件定义了NanoUI需要访问的所有url位置。这份文件的格式由你自行定义,最终只要返回给SysConfMgr一个JSON字符串即可。SysConfMgr会解析收到的JSON字符串,然后把这些url地址以名值对的形式存储到一个JHashMap数据结构中。我的系统中使用的是.properties文件,看起来就像这样:
前端涉及的所有url位置
有了这份配置文件之后,前端的AS3代码中就可以通过SysConfMgr.getUrl('loginUrl');这种形式获取需要访问的后台请求路径了。
这位说了,何必这么麻烦,直接在前台AS3代码里面写不就行了嘛!!!
嗯哼,如果你的系统非常小,并且请求的url地址小于10个,你可以直接写在AS3代码里面。但是,我们的系统可能会涉及到200多个url,并且它们由于某种原因会被经常改变或者重构。因此写在配置文件里面显然是最好的方式,这样当这些url发生变化之后,就不需要改动前台的AS3代码了。绝对的松耦合啊,有木有!!!
初期还需要入手的类还有一个SysDataMgr,这个类封装了数据加载操作,一般使用代码是这样的:
SysDataMgr.req({
shortUrl:"loginUrl",
param: {
userId:userId,
password:password
},
success: function(resultJson:String = null):void {
//这里处理加载成功后的逻辑
},
failure: function(...args):void{
//这里处理加载失败的逻辑
}
});
如你所见,这种使用方式和Ext非常类似。整个NanoUI应用全部使用SysDataMgr获取数据,只要知道这个类有一个req函数,给一个url就可以加载数据。
通过这种处理之后,整个应用的传输层就被完全封装在SysDataMgr一个类中了,框架使用者没有必要再为AS3底层的那些逻辑而烦恼。而且当需要重构或者升级时,只要更新SysDataMgr一个类即可。因此,强烈建议所有开发者在整个应用中全部使用工具类SysDataMgr加载/提交数据。
在下面第三小节中将会给出使用SysDataMgr提交和加载数据的基本实例。
当然,SysDataMgr还提供了使用socket进行消息推送的功能,不过这些功能与后台实现联系比较紧密,而且相对比较繁琐,不是一篇小文所能容纳的,就交给你自己去探索吧。
data:
数据结构包。
这个包提供了一组常用的数据结构,比如模拟出了大家最最喜爱的HashMap结构,这种结构简直是居家旅行之必备啊,有木有!!!
JHashMap将会是你使用最多的工具类,之一。
而且,我们使用Observable为它提供了牛×的事件机制支持。
另一个常用的JGraph(图)是为自动拓扑布局而设计的:
拓扑结构图
如果你不需要做复杂的拓扑结构,你基本不会使用到它。JGraph涉及到了一些基本的图算法,比如顶点、边之类的,这要求你有一定的数据结构基础,如果你想研究这个类,可以先google一下图的基本概念和算法。
dd:
提供完全自定义的拖拽支持。
这个包只需要关注DDMgr即可,DDMgr的代码稍显复杂,但是使用的底层代码却非常简单,只是一些事件处理而已,无非是鼠标的按下、移动、弹起。just so so ……
好消息是,一般的应用层代码不需要接触DDMgr,核心绘图工具JPen会自动使用DDMgr对界面上的图形对象进行拖拽管理。当然,你也可以法克DDMgr,完全无视它,提供你自己的拖拽逻辑。
flcomp:
这个包用来封装flash组件,使用FlashCS4制作出“元件”,然后导出SWC库之后,可以通过JIcon类把它们封装起来,然后就可以在flex组件中无缝地使用它们。所有使用FlashCS制作的元件都被视为一个“图标”,上面是一幅图,下面是一行文字表示这个图标的含义。就像你桌面上的图标一样:
JIcon
把FlashCS制作出来的“元件”统一地看做“图标”是一种非常取巧的设计,这样处理之后就可以对这些组件提供统一的处理逻辑,比如拖拽、事件支持等等。
flexcomp:
flex组件包。
嗯哼,如你所见,这个包用来收容你自己通过AS3代码扩展的Flex组件。
layout:
布局管理器包。
初期的时候,NanoUI视图设计出一套自己的布局算法取代Flex提供的布局。这就是JBorderLayout这些布局存在的原因。但是后来发现使用Flex提供的原生布局似乎更好一些。
这里需要关注的是子包graphiclayout中提供的布局算法,graphiclayout不是你常见的那种普通布局,而是“图形布局”,比如“星型结构”、“椭圆形布局”等等,就像这样:
这个包中最有意思的是JForceDirectedLayout,这是一种“引力斥力”布局方式,你把一堆图形对象放到界面上,然后JForceDirectedLayout会根据它们之间的引力和斥力把它们自动地“分散”开来,从而达到一种平衡状态。在制作拓扑结构应用时,这种布局算法具有非常重要的意义。
math:
数学工具函数。
plugin:
插件工具包。
这里面的组件之所以被称之为插件,是因为它们不属于NanoUI提供的图形继承结构,而是从flash的sprite类直接继承下来的,它们完全可以不受NanoUI管理。
serialization:
序列化工具包。
NanoUI提供两种序列化方式:JSON和XML。
util:
工具函数。
widgets:
页面绘图工具包。
使用这个包,你可以在页面上使用鼠标绘制:圆、椭圆、矩形、圆角矩形、五角星等等这些图形。
这个包中的组件比较对,但是内部机制是非常简单的,在2.2中你将会看到这些组件详细的继承结构图。
2.2. 核心图形类结构图
核心图形对象类结构图
核心管理工具类
序列化工具类
util工具类
2.3. NanoUI使用的其它flex类库
as3commons-reflect-1.3.swc:反射工具类,可以使用类名获取实例,就像Java的反射行为。
DoomsdayConsole64_2.0a_rev10.swc:一个调试工具控制台。
greensock.swc:绿袜子工具函数类,这个工具包几乎是居家旅行必备啊!!!它提供了大量的特效工具函数。
2.4. 使用的前后台技术架构
NanoUI是和后台技术无关的,因此,你的后台可以是JSP、ASP、PHP或者是WebService,或者是其它任何可以通过url获取动态数据的框架或者组件。NanoUI只需要知道一个url,通过这个url能获得JSON或者XML字符串即可。
3. examples
3.1. QuickStart
第一步:把FlashBuilder的编译主题设置为Halo,这一步是必须的。由于Flex升级的不平滑,导致了以前FB3中的某些样式属性不再支持。为了能复用旧版本的代码,我们使用之前老的Halo主题进行编译。
把编译主题设置为Halo
第二步:在FlashBuilder中建立一个Flex工程
新建一个Flex类型的工程
OK,建立工程之后,把NanoUI提供的代码全部拷贝到src下去,让后把需要使用的第三方swc库拷贝到libs目录下。整个工程的目录结构就像这样:
整个工程的目录结构
第三步:到此为止,就可以使用NanoUI提供的工具函数啦!!!你可以利用NanoUI提供的工具类编写自己的应用了。
3.2. 使用SysDataMgr提交和加载数据
3.2.1. 加载JSON数据
flex代码:
<?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">
<s:layout>
<s:BasicLayout/>
</s:layout>
<fx:Declarations>
<!-- 将非可视元素(例如服务、值对象)放在此处 -->
</fx:Declarations>
<fx:Script>
<![CDATA[
import com.nano.core.SysDataMgr;
import com.nano.serialization.json.JSON;
import mx.controls.Alert;
private var dataUrl:String="http://localhost:8083/NanoUI/loadData.jsp";
function loadData():void{
SysDataMgr.req({
url:dataUrl,
success:function(dataStr:String):void{
Alert.show(dataStr,"加载成功");
if(dataStr){
var obj:Object=JSON.decode(dataStr);
Alert.show(obj,"解析成AS对象");
}
},
failure:function(e:Object):void{
Alert.show("加载数据失败","警告");
}
});
}
]]>
</fx:Script>
<mx:HBox width="100%" height="100%" horizontalAlign="center" verticalAlign="middle">
<s:Button label="加载数据" click="loadData()">
</s:Button>
</mx:HBox>
</s:Application>
JSP代码:
<%@page language="java" import="java.util.*" pageEncoding="utf-8"%>
<%
System.out.println("开始加载数据");
String result=null;
out.println("{\"name\":\"大漠穷秋\",\"age\":\"26\"}");
%>
运行效果:
注意点:
虽然ActionScript也遵守ECMA262标准,但是它和JavaScript不同,尤其在对JSON的处理方式上。在JavaScript中处理JSON数据是非常轻松的事情,但是AS不同,我们需要借助于第三方的工具函数进行处理。并且,对回传的数据格式有比较多的限制。比如,返回的JSON字符串无论key还是value都必须使用双引号引起来,否则前台的JSON工具就无法解析。
3.2.2. 提交参数到后台
Flex代码:
<?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">
<s:layout>
<s:BasicLayout/>
</s:layout>
<fx:Declarations>
<!-- 将非可视元素(例如服务、值对象)放在此处 -->
</fx:Declarations>
<fx:Script>
<![CDATA[
import com.nano.core.NanoSystem;
import com.nano.core.SysDataMgr;
import com.nano.serialization.json.JSON;
import mx.controls.Alert;
private var dataUrl:String="http://localhost:8083/NanoUI/submitData.jsp";
function submitData():void{
SysDataMgr.req({
url:dataUrl,
param:{name:userName.text,age:password.text},
success:function(dataStr:String):void{
Alert.show("提交数据成功","提示信息");
},
failure:function(e:Object):void{
Alert.show("提交数据失败","警告");
}
});
}
]]>
</fx:Script>
<mx:VBox width="100%" height="100%" horizontalAlign="center" verticalAlign="middle">
<mx:TextInput id="userName">
</mx:TextInput>
<mx:TextInput id="password">
</mx:TextInput>
<s:Button label="提交数据" click="submitData()">
</s:Button>
</mx:VBox>
</s:Application>
JSP代码:
<%@page language="java" import="java.util.*" pageEncoding="utf-8"%>
<%
request.setCharacterEncoding("utf-8");
String name=request.getParameter("name");
String age=request.getParameter("age");
System.out.println(name);
System.out.println(age);
%>
运行效果:
这里只比上一个例子多了一个param参数,请注意后台JSP对request对象设置的编码类型。
3.3. 综合应用:页面画板
这是NanoUI最初设计的核心目标,也是NanoUI最强大最核心的功能点。请自行运行examples里面的NanoPainter.mxml查看效果,由于这个例子的代码比较多,这里就不贴出来了,只提示一下要点:
首先这个例子依赖一些配置文件,这些文件需要通过后台的loadSysConfig.jsp进行加载,因此,务必首先把你的Tomcat起来之后再运行Flex程序:
注意,Tomcat要起在8083端口上,当然也可以在其它端口,但是那样需要更改一下core包下的SysConfigMgr.as的代码并重新编译:
Tomcat起来之后就可以尝试精彩的NanoPainter啦!!!
界面整体效果
直接用鼠标绘制的图形
拖拽绘制的组件
利用NanoUI的flash插入机制,可以制作一些列的组件供这里使用。
图表组件也可以拖拽绘制哦!!!
模拟Ext的绚丽小Tip
使用拷贝/粘贴创建大量对象
组件序列化成向XML字符串
使用NanoUI,还可以把绘出的图形持久化成XML文件存到后台去,无论是直接通过鼠标绘制的对象,还是通过拖拽方式绘制的组件,还是图表组件。然后还可以把这些XML加载回来,通过NanoUI提供的解析器把XML重新解析成图形对象并显示在界面上。这个序列化、反解析的过程比较复杂,这篇小文无法容纳,待后续分解。
4. NanoUI在《云计费管理平台》中的应用
登录界面
主界面---桌面DeskTop
普通的Form表单
流程图
告警列表
实时告警和路由信息
虚拟Telnet终端
5. 如何扩展和定制NanoUI
你可以根据以上描述和例子自行探索NanoUI提供的其它特性并扩展之。
最后,这篇文章消耗了我6个小时左右的时间,如果你觉得对你有用,请顶帖:damoqiongqiu.javaeye.com,多谢。
最后的最后,不要试图联系哥,哥只是一个传说。
第二篇《如何将图形序列化和反解析》http://damoqiongqiu.iteye.com/blog/1063961