一个内部网站,要求10月前完成,我成功的拖到了12月……网站视觉样式是要求最多的,其实这个不用关心,东西像人脸,再漂亮连看三天也腻了,再难看看三天也习惯了。
整个网站比较繁琐(就我一个人完成,繁琐变成烦死)。包括文字信息和数据库查询两个部分组成。文字信息类似新闻,不是很困难,一个月里就做好大概,剩下就是照着意见修改。剩下数据库部分就是很麻烦了。因为查询的数据需要选取的条件十分多。查询出来的数据包括 时间 地区 指标名 3个属性,这也就意味着,结果表里会有维度选择。其次,对结果表还要作图(曲线图和直方图),导出excel,打印,计算小工具。开始准备使用js做,网上找到ext2,无奈调试困难(当时IE8没出来,firebug调试好的程序到了IE下又会出问题),IE下运行速度太慢(当chrome出来的时候,确实感到了JS的速度可以令人赏心悦目)。无奈只好选择了别的编程手段了。任何一个网页编程论坛都会在讨论JS的时候外加附带讨论Flash,讨论flash就会连带讨论Flex。
Flex
[flex builder 3] 3月发布 为了和 微软的银光对抗,玩家可以google到序列号。安装完后记得去adobe主站看看有没有新出补丁。Fb3 使用了Eclipse 所以java玩家可以光速上手,而且帮助文件模式基本一致。
学习flex现在已经有很多资源了,不像3月份的时候市面图书就一本flex第一步 ,基础学习只能看help。在这里可以下载到 中文视频教程 的确是十分不错的基础教程。Flex使用的as3.0语言对于学过任何一门面向对象语言,都可以很快熟悉。而对于界面编程的mxml 和 html 差不多,现在几乎小学就开始教如何制作做网页了。
Flex里面需要三个重点学习的 1、数据绑定 2、xml操作 3、mvc
这三个很普通,每种编程方式都会涉及(其中mvc只是中编程思想,连编程都算不上),但这团子三兄弟在flex里面可谓是大放异彩。我从我上面说的做的项目里介绍。
因为是个web工程,所以会分为浏览器端和服务端,使用flex后会感觉到这个两个东西会如此分开,因为后台还是使用asp 所以无法使用flash特有remote的数据流格式。只能通过httpservice,传输 xml格式数据。简单分割如下:
[取得参数->数据数组->生成xml]服务端->[获得数据->数据绑定->对xml操作->发送参数]客户端
流程很简单,然后继续分化:
服务端:数据库操作类 ,xml生成类,事物处理类,每件事物都有自己独立的处理asp文件。
客户端:公共httpservice对象,作图类,做表类,界面操作,xml数据处理。
分成那么多就可以每个单独完成了。服务端不用费心,几年经验自然顺风顺水,开始准备xml生成类 后来直接写成页面了,代码少了很多,出来的东西也能随意修改(这是缺点)。
生成xml以后就可以开始专心写前台flex里的代码了。
前台 用ViewStack分为两页 一页可以输入查询条件,另页显示结果,页面一的选择条件包括数据库(Tree)、地区(CheckTree)、指标大类(Tree)、具体指标(CheckList)、和被选择的地区(List)与指标列表(List)。页面二 ApplicationControlBar里面的一堆 维度、指标选择(combo)、小工具、作图(TitleWindow)、转置、打印、导出(button)、 数据表(Adatagrid)
CheckTree 从 Tree 继承来的,CheckList 也是从List里itemrender 。除了button外 每个控件都使用了数据绑定(我使用了XML格式),一切对控件的操作都通过被绑定的数据反应出来,同样一切对数据的操作控件也会反应出来。所以在我面前的不是一个个控件,而是一个个XML。
CheckTree 的xml是这样的:
- <folder label="地区">
- <folder label="全国">
- <node label="北京" dqbm="100000" state="unchecked" />
- <node label="上海" dqbm="200000" state="unchecked" />
- </folder>
- </folder>
很简单吧,只要将这个XML其绑定在CheckTree上就会出现很熟悉的带选择的树控件,将某个节点打上钩,那么XML里该节点state属性就变为了checked , 而他的父节点state属性就变为了schrodinger(猫猫,表示节点不完全选择,如果都选上了也是checked),选择好以后我只要把那些最子节点里state属性为checked的node 拿出来放到 被选择的地区(List)所绑定的XML里就行了,选择的地区控件就立即呈现我选择的内容。
这就显示出了数据绑定的伟大,以至于我在以后写一个vb的程序的时候,也十分热爱使用控件直接渲染一个数组,只不过每次操作玩数组还得再次渲染一下,没有flex里绑定那样得心应手。
Flex里的控件已经十分强大了,但是有些需要自己来做比如上面那个checktree 尽管看上去是 checkbox+tree 但直接用itemrender无法满足要求,tree里的getState updateDisplayList 等等都要override ,累述无益,代码如下:
- package
- {
- import mx.controls.Image;
- import mx.controls.Tree;
- import mx.controls.treeClasses.*;
- import mx.collections.*;
- import mx.controls.CheckBox;
- import mx.controls.listClasses.*;
- import flash.events.Event;
- import flash.events.MouseEvent;
- import mx.events.FlexEvent;
- import flash.display.DisplayObject;
- import flash.xml.*;
- import mx.core.IDataRenderer;
- public class CheckTreeRenderer extends TreeItemRenderer
- {
- protected var myImage:Image;
- // set image properties
- private var imageWidth:Number = 10;
- private var imageHeight:Number = 10;
- private var inner:String = "inner.png";
- protected var myCheckBox:CheckBox;
- static private var STATE_SCHRODINGER:String = "schrodinger";
- static private var STATE_CHECKED:String = "checked";
- static private var STATE_UNCHECKED:String = "unchecked";
- public function CheckTreeRenderer ()
- {
- super();
- mouseEnabled = false;
- }
- private function toggleParents (item:Object, tree:Tree, state:String):void
- {
- if (item == null)
- {
- return;
- }
- else
- {
- item.@state = state;
- toggleParents(tree.getParentItem(item), tree, getState (tree, tree.getParentItem(item)));
- }
- }
- private function toggleChildren (item:Object, tree:Tree, state:String):void
- {
- if (item == null)
- {
- return;
- }
- else
- {
- item.@state = state;
- var treeData:ITreeDataDescriptor = tree.dataDescriptor;
- if (treeData.hasChildren(item))
- {
- var children:ICollectionView = treeData.getChildren (item);
- var cursor:IViewCursor = children.createCursor();
- while (!cursor.afterLast)
- {
- toggleChildren(cursor.current, tree, state);
- cursor.moveNext();
- }
- }
- }
- }
- private function getState(tree:Tree, parent:Object):String
- {
- var noChecks:int = 0;
- var noCats:int = 0;
- var noUnChecks:int = 0;
- if (parent != null)
- {
- var treeData:ITreeDataDescriptor = tree.dataDescriptor;
- var cursor:IViewCursor = treeData.getChildren(parent).createCursor();
- while (!cursor.afterLast)
- {
- if (cursor.current.@state == STATE_CHECKED)
- {
- noChecks++;
- }
- else if (cursor.current.@state == STATE_UNCHECKED)
- {
- noUnChecks++
- }
- else
- {
- noCats++;
- }
- cursor.moveNext();
- }
- }
- if ((noChecks > 0 && noUnChecks > 0) || (noCats > 0))
- {
- return STATE_SCHRODINGER;
- }
- else if (noChecks > 0)
- {
- return STATE_CHECKED;
- }
- else
- {
- return STATE_UNCHECKED;
- }
- }
- private function imageToggleHandler(event:MouseEvent):void
- {
- //event.stopImmediatePropagation();
- myCheckBox.selected = !myCheckBox.selected;
- checkBoxToggleHandler(event);
- }
- private function checkBoxToggleHandler(event:MouseEvent):void{
- //event.stopImmediatePropagation();
- if (data)
- {
- var myListData:TreeListData = TreeListData(this.listData);
- var selectedNode:Object = myListData.item;
- var tree:Tree = Tree(myListData.owner);
- var toggle:Boolean = myCheckBox.selected;
- if (toggle)
- {
- toggleChildren(data, tree, STATE_CHECKED);
- }
- else
- {
- toggleChildren(data, tree, STATE_UNCHECKED);
- }
- var parent:Object = tree.getParentItem (data);
- toggleParents (parent, tree, getState (tree, parent));
- }
- }
- override protected function createChildren():void
- {
- super.createChildren();
- myCheckBox = new CheckBox();
- myCheckBox.setStyle( "verticalAlign", "middle" );
- myCheckBox.addEventListener( MouseEvent.CLICK, checkBoxToggleHandler );
- addChild(myCheckBox);
- myImage = new Image();
- myImage.source = inner;
- myImage.addEventListener( MouseEvent.CLICK, imageToggleHandler );
- myImage.setStyle( "verticalAlign", "middle" );
- addChild(myImage);
- }
- private function setCheckState (checkBox:CheckBox, value:Object, state:String):void
- {
- if (checkBox == null){
- return;
- }
- if (state == STATE_CHECKED)
- {
- checkBox.selected = true;
- }
- else if (state == STATE_UNCHECKED)
- {
- checkBox.selected = false;
- }
- else if (state == STATE_SCHRODINGER)
- {
- checkBox.selected = false;
- }
- else
- {
- checkBox.selected = false;
- }
- }
- override public function set data(value:Object):void
- {
- if (value == null) return;
- super.data = value;
- setCheckState (myCheckBox, value, value.@state);
- if(TreeListData(super.listData).item.@type == 'dimension')
- {
- setStyle("fontStyle", 'italic');
- }
- else
- {
- if (this.parent != null)
- {
- var _tree:Tree = Tree(this.parent.parent);
- _tree.setStyle("defaultLeafIcon", null);
- }
- setStyle("fontStyle", 'normal');
- }
- }
- override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
- {
- super.updateDisplayList(unscaledWidth, unscaledHeight);
- if(super.data)
- {
- if (super.icon != null)
- {
- myCheckBox.x = super.icon.x;
- myCheckBox.y = getStyle("fontSize")-2;
- super.icon.x = myCheckBox.x + myCheckBox.width + 17;
- super.label.x = super.icon.x + super.icon.width + 3;
- }
- else
- {
- myCheckBox.x = super.label.x;
- myCheckBox.y = getStyle("fontSize")-2;
- super.label.x = myCheckBox.x + myCheckBox.width + 17;
- }
- if (data.@state == STATE_SCHRODINGER)
- {
- myImage.x = myCheckBox.x+2;
- myImage.y = myCheckBox.y-5;
- myImage.width = imageWidth;
- myImage.height = imageHeight;
- }
- else
- {
- myImage.x = 0;
- myImage.y = 0;
- myImage.width = 0;
- myImage.height = 0;
- }
- }
- }
- }
- }
- <script language="javascript" runat="server" type="text/javascript">
- function decodeURI(furl){
- var a=furl;
- try{return decodeURIComponent(a)}catch(e){return 'undefined'};
- return '';
- }
- </script>
- <dataSet>
- <colSet freeze="1">
- <colObj columnName="@col1" headerText="指标/单位" width="120" tu="0" />
- <colGroup headerText="2008年一季度">
- <colObj columnName="@col2" headerText="绝对值" unit="亿元" zbname="2008年一季度绝对值" width="100" tu="1" />
- <colObj columnName="@col3" headerText="增长" unit="%" zbname="2008年一季度增长" width="100" tu="1" />
- </colGroup>
- <colGroup headerText="2008年上半年">
- <colObj columnName="@col4" headerText="绝对值" unit="亿元" zbname="2008年一季度绝对值" width="100" tu="1" />
- <colObj columnName="@col5" headerText="增长" unit="%" zbname="2008年一季度增长" width="100" tu="1" />
- </colGroup>
- <colGroup headerText="2008年全年">
- <colObj columnName="@col6" headerText="绝对值" unit="亿元" zbname="2008年一季度绝对值" width="100" tu="1" />
- <colObj columnName="@col7" headerText="增长" unit="%" zbname="2008年一季度增长" width="100" tu="1" />
- </colGroup>
- </colSet>
- <colData>
- <dataObj col1 = "GDP" col2 = "1725.10" col3 = "13.50" col4 = "4180.70" col5 = "14.20" col6 = "8990.80" col7 = "13.90"/>
- <dataObj col1 = "其中:一产" col2 = "196.40" col3 = "3.10" col4 = "565.80" col5 = "4.50" col6 = "1385.80" col7 = "4.30"/>
- <dataObj col1 = "二产" col2 = "819.20" col3 = "17.90" col4 = "2097.30" col5 = "18.80" col6 = "4194.60" col7 = "18.50"/>
- <dataObj col1 = "三产" col2 = "709.50" col3 = "11.80" col4 = "1517.60" col5 = "11.90" col6 = "3410.30" col7 = "11.90"/>
- <dataObj col1 = "规模以上工业" col2 = "648.70" col3 = "22.20" col4 = "1535.30" col5 = "24.90" col6 = "3071" col7 = "23"/>
- </colData>
- </dataSet>
- package
- {
- import mx.controls.AdvancedDataGrid;
- import mx.controls.advancedDataGridClasses.*;
- import mx.core.ClassFactory;
- import mx.utils.ObjectUtil;
- public class XmlToGrid
- {
- public function XmlToGrid():void{
- }
- public static function XmlGrid(adgrid:AdvancedDataGrid,InXml:XML):void{
- adgrid.visible=false;
- adgrid.groupedColumns=XmltoGCol(InXml.colSet);
- adgrid.lockedColumnCount=InXml.colSet.@freeze;
- adgrid.dataProvider=InXml.colData.dataObj;
- adgrid.visible=true;
- }
- private static function sortFunc(field:String):Function{
- return function(obj1:Object, obj2:Object):int{
- return ObjectUtil.numericCompare(obj1[field],obj2[field]);
- }
- }
- private static function XmltoColumns(InXml:XML):AdvancedDataGridColumn{
- var columns:Array=new Array();
- var item:Object=InXml
- var dgColumn:AdvancedDataGridColumn=new AdvancedDataGridColumn();
- //一个列头的内容
- dgColumn.dataField=item.@columnName;
- if (item.@unit!=undefined && item.@unit!=""){
- dgColumn.headerText=item.@headerText+"/n["+item.@unit+"]";
- //dgColumn.headerText=item.@headerText;
- }
- else{
- dgColumn.headerText=item.@headerText;
- }
- dgColumn.minWidth=item.@width;
- //var tempstr:String=item.@headerText.toString();
- //dgColumn.width=tempstr.length*10;
- dgColumn.headerWordWrap=true;
- dgColumn.wordWrap=true;
- dgColumn.sortCompareFunction=sortFunc(item.@columnName);
- dgColumn.itemRenderer=new ClassFactory(GridRenderer);
- return(dgColumn);
- }
- private static function XmltoGCol(InXml:XMLList):Array{
- var columns:Array=new Array();
- var item:Object=InXml
- //递归遍历
- for each(var sonXml:XML in InXml.children()){
- if (sonXml.children().length()!=0){
- var groupedColumns:AdvancedDataGridColumnGroup=new AdvancedDataGridColumnGroup();
- groupedColumns.headerText=sonXml.@headerText;
- groupedColumns.children=(XmltoGCol(XMLList(sonXml)));
- columns.push(groupedColumns);
- }
- else{
- columns.push(XmltoColumns(sonXml));
- }
- }
- return(columns);
- }
- }
- }