2012年,刚刚毕业参加的工作的时候,在公司里面学习了Flex技术。由于本人是做Java的,Flex中的ActionScript语法和Java挺类似,所以学起来还比较顺手的。大概做了一年多和Flex相关的软件开发,然后就跳槽专攻Java了。现在Flex貌似已经不算使用非常广泛的框架了,但是存在即合理,我相信使用Flex技术的公司或者兴趣爱好者还是不少的。由于本人对Flex研究也是皮毛,利用Flex做出特别NB的软件也没有。现在这里分享一下自己独立做的一个Flex项目--甘特图。
甘特图就是一个缩小版的Project,里面的功能也不是很复杂。但是当初以我的水平,开发起来还是步步都是坑。当你以一个新手入场的时候,你才会知道挨的每一拳到底有多痛。当初开发甘特图的时候,基本上就我一个,网上找相关的资料,特别是和Flex有关的资料,那是相当的少。基本上没有看到我想要的。所以在利用Flex开发甘特图这方面的问题也全靠自己去琢磨,去解决。
现在回头看看甘特图的功能,整体而言并没有特别突出的亮点,但是开发的这段经历却一直让我记忆犹新。一个人奋斗了大半年,基本上每天加班。也享受问题得到解决后的喜悦,也是这段经历让我对以后的码农之路坚定了信心。
当初甘特图是作为一个插件为另一个项目服务的,但是另一个项目开发失败,导致甘特图也白开发了,当时是无比的心塞!!!!呕心沥血大半年,一下回到解放前!!!
将3年前的东西分享出来,的确有些扯淡了,算是一个小小的总结吧。
重点开始!
甘特图和服务交互采用WebService,我相信做开发的人对WebService都很熟悉,不用赘述。
甘特图中最困难的当属任务条的绘制和渲染,还有就是连接任务关系等相关的线条绘制。同时还有网络图的绘制也比较麻烦。
甘特图任务渲染使用高级表格AdvancedDataGrid,并重写了里面的一些方法。
<span > </span>override protected function mouseDoubleClickHandler(event:MouseEvent):void
{
var dataGridEvent:AdvancedDataGridEvent;
var r:IListItemRenderer;
var dgColumn:AdvancedDataGridColumn;
r=mouseEventToItemRenderer(event);
if (r && r != itemEditorInstance)
{
try
{
var dilr:IDropInListItemRenderer=IDropInListItemRenderer(r);
if (columns[dilr.listData.columnIndex].editable)
{
dgColumn=columns[dilr.listData.columnIndex];
dataGridEvent=new AdvancedDataGridEvent(AdvancedDataGridEvent.ITEM_EDIT_BEGINNING, false, true);
// ITEM_EDIT events are cancelable
dataGridEvent.columnIndex=dilr.listData.columnIndex;
dataGridEvent.dataField=dgColumn.dataField;
dataGridEvent.rowIndex=dilr.listData.rowIndex + verticalScrollPosition;
dataGridEvent.itemRenderer=r;
dispatchEvent(dataGridEvent);
}
}
catch(error:Error)
{
}
}
super.mouseDoubleClickHandler(event);
}
/**
*鼠标释放事件
*/
override protected function mouseUpHandler(event:MouseEvent):void
{
var r:IListItemRenderer;
var dgColumn:AdvancedDataGridColumn;
r=mouseEventToItemRenderer(event);
if (r)
{
if (r is AdvancedHeaderRenderColumnMc || r is AdvancedHeaderRenderColumn)
{
return;
}
try
{
var dilr:IDropInListItemRenderer=IDropInListItemRenderer(r);
if (columns[dilr.listData.columnIndex].editable)
{
dgColumn=columns[dilr.listData.columnIndex];
dgColumn.editable=false;
}
}
catch(error:Error)
{
}
}
super.mouseUpHandler(event);
if (dgColumn)
dgColumn.editable=true;
}
对于表格中可移动的组件需要使用自定义组件,大部分时间都花在了调试自定义组件上面。
<mx:LinkButton width="16"
height="16"
enabled="true"
click="doClick()"
y="{(GanttProperties.RowHeight - 16)/4 - 3}"
id="openbutton"
x="{offSize-20}"
icon="@Embed('icon/gantt_open.png')">
</mx:LinkButton>
<!--
{GanttProperties.GanttEditor}
-->
<s:HSlider id="ganttitem"
enabled="{GanttProperties.GanttEditor}"
skinClass="com.gantt.diycolumn.MyHSlider"
minWidth="1"
width="{ganttWidth}"
height="{GanttProperties.GanttItemHeight}"
y="{(GanttProperties.RowHeight - GanttProperties.GanttItemHeight)/4 -4}"
x="{offSize}"
minimum="0"
value="{data.percent}"
thumbRelease="release()"
thumbDrag="releaseing()"
thumbPress="releasePress()"
maximum="100"
dragEnter="doDragEnter(event)"
dragDrop="doDragDrop(event)"/>
<mx:LinkButton x="{offSize+ganttWidth-14}"
enabled="false"
y="-2"
id="lingxing"
icon="{GanttProperties.stoneIcon}">
</mx:LinkButton>
<diycolumn:GanttItem id="actualGanttItem"
width="{actualDateWidth}"
visible="{GanttProperties.ActualDate}"
includeInLayout="{GanttProperties.ActualDate}"
height="7"
y="{(GanttProperties.RowHeight - 13)}"
x="{actualDateOffSize}">
</diycolumn:GanttItem>
<diycolumn:GanttItem id="estimatedGanttItem"
width="{estimatedTimeLineWidth}"
visible="{GanttProperties.EstimatedDate}"
includeInLayout="{GanttProperties.EstimatedDate}"
height="2"
bdcolor="0x12345ab"
y="{(GanttProperties.RowHeight - 8)}"
x="{actualDateOffSize}">
</diycolumn:GanttItem>
<mx:HBox id="assignment"
width="{assignmentWidth}"
height="{GanttProperties.RowHeight}"
visible="{GanttProperties.TaskAssignment}"
includeInLayout="{GanttProperties.TaskAssignment}"
horizontalGap="20"
y="0"
x="{offSize+ganttWidth+14}">
</mx:HBox>
网络图中,需要用各种形状来表示不同类型的任务。每种形状也全是用UIComponent画出来的。
/**画出一个矩形*/
public function drawRectangle():void
{
sprite.graphics.beginFill(color);
sprite.graphics.lineStyle(2, bdcolor, 1, true);
sprite.graphics.drawRoundRect(0, 0, width, height, 10, 10);
//sprite.graphics.drawCircle(width,height/2,radio);
sprite.graphics.endFill();
leftPoint.x=0;
leftPoint.y=height/2;
rightPoint.x = width;
rightPoint.y = height/2;
}
/**画出一个菱形*/
public function drawRhombus():void
{
sprite.graphics.beginFill(color);
var vec:Vector.<Number>=new Vector.<Number>();
vec.push(0, height / 2);
vec.push(width / 6, 0);
vec.push(width * 5 / 6, 0);
vec.push(width, height / 2);
vec.push(width * 5 / 6, height);
vec.push(width / 6, height);
var vec2:Vector.<int>=new Vector.<int>();
vec2.push(0, 1, 5);
vec2.push(2, 3, 4);
sprite.graphics.drawTriangles(vec, vec2);
sprite.graphics.drawRect(width / 6, 0, width * 2 / 3, height);
//sprite.graphics.drawCircle(width,height/2,radio);
sprite.graphics.endFill();
drawLine(vec);
leftPoint.x=0;
leftPoint.y=height/2;
rightPoint.x = width;
rightPoint.y = height/2;
}
/**画三角形*/
public function drawTrangle():void
{
sprite.graphics.beginFill(color);
var vec:Vector.<Number>=new Vector.<Number>();
vec.push(0, height / 2);
vec.push(width / 2, 0);
vec.push(width, height / 2);
sprite.graphics.drawTriangles(vec);
sprite.graphics.endFill();
drawLine(vec);
}
甘特图里面,本人最不满意的就是表格中的线条绘制。因为在用线连接跨表格的任务时,没有找到很好的方法让绘制出来的线条和表格合二为一。我使用的绘制方法是将线条用Shape画在表格之上。从而导致线条都是悬浮在表格之上,看着怪怪的。我也没有找到什么解决办法,希望大牛能够多出对策。
public function draw():void
{
graphics.clear();
with (graphics)
{
lineStyle(_thinkness, _color, _alpha, _pixelHinting, _scaleMode);
moveTo(start.x, start.y);
lineTo(end.x, end.y);
}
if (hasArrowHead)
{
drawArrowHead();
}
}
甘特图里面还有一个最短路径算法,这个算法网上也很多,只是改成了ActionScript实现而已。
/**
*
* 遍历从开始活动到结束活动的所有可能的路径:采用递归方式(如果后继任务集合为空,则表明到达结束,是一条成功路径,否则就继续遍历直到某个任务没有后继节点)
* <功能详细描述>
* @param allActivity
* @param pos
* @param arrOnePath
* @param arrAllPath
* @see [类、类#方法、类#成员]
*/
public static function goAllPath(allActivity:Array,pos:int,arrOnePath:Array):void{
arrOnePath.push(allActivity[pos]);
var t:Object=allActivity[pos];
var arrSucc:Array=t.successor;
if(arrSucc.length==0){
//成功到达终点,是一条路径
var completePath:Array=new Array();
for each(var tt:Object in arrOnePath){
completePath.push(tt);
}
CommonUtil.arrAllPath.push(completePath)
return;
}else{//对后继的每个活动进行检查
for(var i:int=0;i<arrSucc.length;i++){
var activity:Object=arrSucc[i];
var k:int=lookInActivityDatasForPos(allActivity,activity);
goAllPath(allActivity,k,arrOnePath);
var nowNumber:int=arrOnePath.length;
//将最新加入到arrOnePath的活动弹出,选择其他活动重新开始搜索
if(nowNumber>=1)
arrOnePath.pop();
}
}
}
/**
*
* 对所有查找到的路径进行选择找出最关键路径(即耗时间最长的路径,可能有多条)
* <功能详细描述>
* @param allActivity
* @param allPath
* @see [类、类#方法、类#成员]
*/
public static function markCriticalPath(allActivity:Array):void{
var pathSum:Number=0;
var maxpathSum:Number=0;
var k:int=0;
for(var i:int=0;i<CommonUtil.arrAllPath.length;i++){
var criticalPath:Array=CommonUtil.arrAllPath[i];
for(var j:int=0;j<criticalPath.length;j++){
var activity:Object=criticalPath[j];
pathSum+=parseFloat(activity.cxsj);
}
if(maxpathSum<pathSum){
k=i;
maxpathSum=pathSum;
CommonUtil.pathnum.splice(0,CommonUtil.pathnum.length);
CommonUtil.pathnum.push(k);
}else if(maxpathSum==pathSum){
k=i;
CommonUtil.pathnum.push(k);
}
pathSum=0;
}
}
以上只是挑了一些重点说了一下,现在就是上图了,没有最终效果的展示,说得再多也是耍流氓。
最终效果就是这样,现在看看,还是比较丑的。