var spaces:* = xml.spaces.space; //可以直接用.符号来获取XML文件的节点
for each(var space:Object in spaces){
orderRow.addChild(createTitleColItem(space.order,space.id)); //循环加载序号
projectRow.addChild(createVerTitleColItem(space.projectName); //循环加载工程名称
}
createTitleColItem和createVerTitleColItem里面封装了单元格的创建,写成方法方便调用。
工期的日期时间和标段划分也是同理,对于工期来说,数据都显示在最左侧一栏,其余的单元格都为空,这样做是当完成框架布局后,在这空白的区域绘图。
好了,到这里布局完成,将grid加入到上一层容器,我用的是Canvas并且将它的宽度和高度设置和grid一样,然后在Canvas外面再加上一层容器,比如VBox。然后给VBox一个合适的宽高(比较好的做法是取当前显示器的宽高,否则页面显示会有点问题)。让它比Canvas要小(一般来讲,Canvas会比VBox大很多),这时就会出现滚动条,拖动滚动条进行查看。
3、计算坐标
在这个应用当中,最重要的应该就是计算坐标了,不管是画线还是画图,都需要坐标来定位。那到底怎么计算坐标呢?其实也不难,通过ID与日期就能把它们算出来。
①、X坐标
注意在设置第一行序号的时候,createTitleColItem(space.order,space.id)这个方法的第二个参数就是一个ID,每个序号项的GridItem的id属性都会保存space.id值,而任务数据项中也有一个这样的ID,所以就可以通过查找ID来计算出它的水平位置来。
private function getX(position:String,orderChilds:Array):int{
var x:int = title_width; //最左侧的标题列宽
for(var i:int=1;i<orderChilds.length-1;i++){
if((child[i] as GridItem).id==position) break;
x += col_width; //普通列宽
}
return x;
}
position是任务项ID,orderChilds 是orderRow.getChildren()得到第一行的所有列返回的数组。任务项中会有两个position,起始和终止。通过这两个值可以求出x1与x2坐标。
②、Y坐标
y坐标的是通过任务项包含的开始日期和结束日期与整个计划的最晚日期进行比较计算出来的。
private function getY(date:Date):int{
var rowSize:int = 5; //行数,序号和工程名称占据了五行
rowSize += Math.abs(dateDiff("m",date,this.endDate));
var day:int = date.getDate();
day = day > 30 ? 0 : 30-day;
return rowSize*title_height+Math.round((day%30/30)*title _height);
}
在应用里我将行高都设为相同的值,工程名称这行是四倍的行高。生成的布局框架里,日期时间是倒排序,它们之间的间隔是一个月。dateDiff就是计算当前日期与最晚日期(this.endDate)之间相隔多少个月,而每相隔一个月,就是一行。因此通过这种方式计算出y坐标。
我补充说明一下,因为最终展现结果的是Canvas容器,所以坐标的计算都是相对Canvas的,因此这个x和y的坐标是相对Canvas内的绝对坐标。
4、绘图
绘图分为画线和画图形。如果是画线,则可以直接利用Flash的graphics来画图。画图形要麻烦点,得先取得图片,再用图片来画图。不管是画线还是画图形,都是和数据有关的。数据中会有一个类型,是说明到底这个任务项是线还是图。如果是线,会有一个颜色值,为了防止没有设置颜色值,我们应该给定一个默认的颜色值。如果是图,会有个图片的相对地址,录数据的时候会上传图片,不过为了防止没有上传图片,我们应该准备一个默认图片。
①、画线
var line:UIComponent = new UIComponent();
line.graphics.lineStyle(thickNum,color,1);//设置粗细、颜色、透明度
line.graphics.moveTo(x1,y1); //从某个坐标开始
line.graphics.lineTo(x2,y2); //画到某个坐标
②、画图
public function load (url:String,callback:Function,options:*):void{
var loader:Loader = new Loader();
loader.load(new URLRequest(encodeURI(url))); //图片地址
loader.contentLoaderInfo.addEventListener(Event.COMPLETE,
function(e:Event):void {callback(e.currentTarget.content,options);});
}
load(url,paintRect,{"x1":x1,"y1":y1,"x2":x2,"y2":y2});
public function paintRect(source:*,options:*):void{
var shape:UIComponent = new UIComponent();
var rect_width:int = Math.abs(options.x2–options.x1); //图形宽度
var rect_height:int = Math.abs(options.y2–options.y1); //图形高度
var bitmap:BitmapData = new BitmapData(source.width, source.height); //用图片源的宽高定义一个位图对象
bitmap.draw(source); //将图片源在位图上绘制出来
shape.graphics.beginBitmapFill(bitmap); //用位图填充绘图区域
shape.graphics.drawRect(options.x1, options.x2,rect_width,rect_height); //定义矩形绘图区域,这块区域将用图片填充
shape.graphics.endFill(); //应用填充
}
load方法就是用来获取图片。注册监听器,这里我们还是使用Event.COMPLETE事件类型,当图片加载完成后,会调用回调函数,并将参数也一起传给回调函数。画图工作是在回调函数中进行。另外有一点要说明的是,Loader类是用来加载SWF文件或图像(JPG、PNG 或 GIF)文件,而URLLoader 类则是加载文本或二进制数据,请大家使用的时候要注意这点区别。
另外这里不管是画线还是画图都用的是UIComponent类,这是因为它有一个toolTip属性,当鼠标移至图或线上时,会显示你设置的信息。如果用Sprite则没有这属性。UIComponent是Flex定义的,而Sprite则是Flash本身的。
画图例的方法是一样的,画好的图需要加入到父容器中显示出来。到此整个做法就讲完了,其实也不是很复杂,关键是要理清思路。因为涉及到商业项目和保密原则,请原谅大象不能将此源码拿出来给大家分享,不过我已经提供了部分代码,而且这些代码是整个应用中关键点。只要大家能从中得到一点帮助,那么我就很满足了。
本文为菠萝大象原创,如要转载请注明出处。