隨筆-67  評(píng)論-522  文章-0  trackbacks-0
              本文根據(jù)項(xiàng)目實(shí)例,詳解如何使用Flex技術(shù)開(kāi)發(fā)時(shí)空線形圖。
              大象目前從事的是工程管理方面的開(kāi)發(fā),在土木工程建設(shè)行業(yè)有一種時(shí)空線形圖,它是用實(shí)線,虛線,圖形等方式,來(lái)描述出工程進(jìn)度計(jì)劃中的數(shù)據(jù)信息的方法。呈現(xiàn)出來(lái)的一般是報(bào)表形式,多采用Excel來(lái)實(shí)現(xiàn)。
              對(duì)于客戶來(lái)說(shuō),手動(dòng)繪制時(shí)空線形圖是比較麻煩的。因?yàn)橐嬀€,畫圖片。如果知道甘特圖或使用過(guò)Microsoft Project的朋友應(yīng)該很快就能明白,對(duì)于一個(gè)工程的進(jìn)度計(jì)劃來(lái)說(shuō),它包含有很多的任務(wù)項(xiàng),而這些任務(wù)項(xiàng)都需要在線形圖上表示出來(lái),因此手動(dòng)做的話工作量是很大的。現(xiàn)在有了系統(tǒng),就可以把這一任務(wù)交給計(jì)算機(jī)來(lái)完成,客戶只需要輸入數(shù)據(jù)即可。
              系統(tǒng)中的時(shí)空線形圖最早是采用JavaScript腳本編寫的,后來(lái)由我改成了Flex版。先說(shuō)下實(shí)現(xiàn)的思路。因?yàn)橹皇秋@示和打印,沒(méi)有其它的要與用戶交互的需求。所以后臺(tái)數(shù)據(jù)是直接以XML流文件的形式,通過(guò)異步加載獲得。然后再利用Flex強(qiáng)大的XML解析能力處理這些數(shù)據(jù),最后就是根據(jù)這些數(shù)據(jù)使用FlashAPI畫圖。呵呵,是不是很簡(jiǎn)單? 先看幾張截圖大致了解下。

                      
                      
                      
                      

              本文的AS代碼是基于ActionScript 3.0,下面進(jìn)行詳細(xì)說(shuō)明。
              1、獲得數(shù)據(jù)
              我采用URLLoader.load方法實(shí)現(xiàn)。通過(guò)傳遞請(qǐng)求地址,注冊(cè)監(jiān)聽(tīng)器,然后在回調(diào)函數(shù)中就可以獲得XML數(shù)據(jù)了。當(dāng)然你也可以采用RemoteObject方式。
          public function LoadXML():void {
              
          var loader:URLLoader = new URLLoader();
              
          loader.load(new URLRequest("/baseAction.do?method=ajaxData")); //這個(gè)地址可以直接是一個(gè)xml文件
              
          loader.addEventListener(Event.COMPLETE, handleComplete);
          }
          private function handleComplete(event:Event):void {
              var xml:XML = new XML(event.target.data);
              
          ……
          }
              這是個(gè)異步加載過(guò)程,當(dāng)數(shù)據(jù)在后臺(tái)獲取完成,并轉(zhuǎn)換為XML格式向前臺(tái)發(fā)送后,注冊(cè)的Event.COMPLETE事件類型就會(huì)觸發(fā)回調(diào)函數(shù)handleComplete,然后我們就能得到數(shù)據(jù)并將它轉(zhuǎn)型為XML對(duì)象。
              2、布局
              我們現(xiàn)在有了數(shù)據(jù),下一步就是布局。本項(xiàng)目中的時(shí)空線形圖上有這些基本信息:序號(hào)、工程名稱、工期、標(biāo)段劃分和圖例。這其中的工期是一個(gè)時(shí)間跨度,對(duì)應(yīng)進(jìn)度計(jì)劃的最早開(kāi)始日期和最晚結(jié)束日期。我們?nèi)〉氖悄?/span>+月的形式來(lái)表示。而圖例則是對(duì)線形圖中的任務(wù)項(xiàng)列出對(duì)應(yīng)的說(shuō)明。大象當(dāng)時(shí)為了方便,使用FlexGridGridRowGridItem來(lái)生成布局框架,要是考慮性能方面的原因,應(yīng)該換成其它方法實(shí)現(xiàn),因?yàn)檫@種方式速度會(huì)有點(diǎn)慢,好在沒(méi)有其它的交互情況。那么先讓大象把做法講完,性能調(diào)優(yōu)的話題已經(jīng)超出本文的范圍了。
              這里就是要不停的生成單元格,由單元格組成行,再由多行組成Grid,這跟html中的tabletrtd是一個(gè)道理。

          var grid:Grid = new Grid(); //Grid只需要一個(gè)
          var orderRow:GridRow = new GridRow(); //序號(hào)行
          var projectRow:GridRow = new GridRow(); //工程名稱行
          ……
              創(chuàng)建了行,我們同時(shí)還要向里面添加單元格,也即GridItem。這時(shí)就要根據(jù)得到的數(shù)據(jù)來(lái)處理了,是一個(gè)循環(huán)。
          var spaces:* = xml.spaces.space; //可以直接用.符號(hào)來(lái)獲取XML文件的節(jié)點(diǎn)
          for each(var space:Object in spaces){
              orderRow.addChild(createTitleColItem(space.order,space.id)); //循環(huán)加載序號(hào)

              projectRow.addChild(createVerTitleColItem(space.projectName); //循環(huán)加載工程名稱
          }
              createTitleColItemcreateVerTitleColItem里面封裝了單元格的創(chuàng)建,寫成方法方便調(diào)用。
              工期的日期時(shí)間和標(biāo)段劃分也是同理,對(duì)于工期來(lái)說(shuō),數(shù)據(jù)都顯示在最左側(cè)一欄,其余的單元格都為空,這樣做是當(dāng)完成框架布局后,在這空白的區(qū)域繪圖。
              好了,到這里布局完成,將grid加入到上一層容器,我用的是Canvas并且將它的寬度和高度設(shè)置和grid一樣,然后在Canvas外面再加上一層容器,比如VBox。然后給VBox一個(gè)合適的寬高(比較好的做法是取當(dāng)前顯示器的寬高,否則頁(yè)面顯示會(huì)有點(diǎn)問(wèn)題)。讓它比Canvas要小(一般來(lái)講,Canvas會(huì)比VBox大很多),這時(shí)就會(huì)出現(xiàn)滾動(dòng)條,拖動(dòng)滾動(dòng)條進(jìn)行查看。
              3、計(jì)算坐標(biāo)

              在這個(gè)應(yīng)用當(dāng)中,最重要的應(yīng)該就是計(jì)算坐標(biāo)了,不管是畫線還是畫圖,都需要坐標(biāo)來(lái)定位。那到底怎么計(jì)算坐標(biāo)呢?其實(shí)也不難,通過(guò)ID與日期就能把它們算出來(lái)。
              ①、X坐標(biāo)
              注意在設(shè)置第一行序號(hào)的時(shí)候,createTitleColItem(space.order,space.id)這個(gè)方法的第二個(gè)參數(shù)就是一個(gè)ID,每個(gè)序號(hào)項(xiàng)的GridItemid屬性都會(huì)保存space.id值,而任務(wù)數(shù)據(jù)項(xiàng)中也有一個(gè)這樣的ID,所以就可以通過(guò)查找ID來(lái)計(jì)算出它的水平位置來(lái)。
          private function getX(position:String,orderChilds:Array):int{
              var x:int = title_width; //最左側(cè)的標(biāo)題列寬
              for(var i:int=1;i<orderChilds.length-1;i++){
                  if((child[i] as GridItem).id==position) break;
                  x += col_width; //普通列寬
              }
              return x;
          }
              position是任務(wù)項(xiàng)IDorderChilds orderRow.getChildren()得到第一行的所有列返回的數(shù)組。任務(wù)項(xiàng)中會(huì)有兩個(gè)position,起始和終止。通過(guò)這兩個(gè)值可以求出x1x2坐標(biāo)。
              ②、Y坐標(biāo)
              y坐標(biāo)的是通過(guò)任務(wù)項(xiàng)包含的開(kāi)始日期和結(jié)束日期與整個(gè)計(jì)劃的最晚日期進(jìn)行比較計(jì)算出來(lái)的。
          private function getY(date:Date):int{
              var rowSize:int = 5; //行數(shù),序號(hào)和工程名稱占據(jù)了五行
              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);
          }
              在應(yīng)用里我將行高都設(shè)為相同的值,工程名稱這行是四倍的行高。生成的布局框架里,日期時(shí)間是倒排序,它們之間的間隔是一個(gè)月。dateDiff就是計(jì)算當(dāng)前日期與最晚日期(this.endDate)之間相隔多少個(gè)月,而每相隔一個(gè)月,就是一行。因此通過(guò)這種方式計(jì)算出y坐標(biāo)。
              我補(bǔ)充說(shuō)明一下,因?yàn)樽罱K展現(xiàn)結(jié)果的是Canvas容器,所以坐標(biāo)的計(jì)算都是相對(duì)Canvas的,因此這個(gè)xy的坐標(biāo)是相對(duì)Canvas內(nèi)的絕對(duì)坐標(biāo)。

              4、繪圖
              繪圖分為畫線和畫圖形。如果是畫線,則可以直接利用Flashgraphics來(lái)畫圖。畫圖形要麻煩點(diǎn),得先取得圖片,再用圖片來(lái)畫圖。不管是畫線還是畫圖形,都是和數(shù)據(jù)有關(guān)的。數(shù)據(jù)中會(huì)有一個(gè)類型,是說(shuō)明到底這個(gè)任務(wù)項(xiàng)是線還是圖。如果是線,會(huì)有一個(gè)顏色值,為了防止沒(méi)有設(shè)置顏色值,我們應(yīng)該給定一個(gè)默認(rèn)的顏色值。如果是圖,會(huì)有個(gè)圖片的相對(duì)地址,錄數(shù)據(jù)的時(shí)候會(huì)上傳圖片,不過(guò)為了防止沒(méi)有上傳圖片,我們應(yīng)該準(zhǔn)備一個(gè)默認(rèn)圖片。
              ①、畫線
          var line:UIComponent = new UIComponent();
          line.graphics.lineStyle(thickNum,color,1);//設(shè)置粗細(xì)、顏色、透明度
          line.graphics.moveTo(x1,y1); //從某個(gè)坐標(biāo)開(kāi)始
          line.graphics.lineTo(x2,y2); //畫到某個(gè)坐標(biāo)
              ②、畫圖
          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); //用圖片源的寬高定義一個(gè)位圖對(duì)象
              bitmap.draw(source); //將圖片源在位圖上繪制出來(lái)
              shape.graphics.beginBitmapFill(bitmap); //用位圖填充繪圖區(qū)域
              shape.graphics.drawRect(options.x1, options.x2,rect_width,rect_height); //定義矩形繪圖區(qū)域,這塊區(qū)域?qū)⒂脠D片填充
              shape.graphics.endFill(); //應(yīng)用填充
          }
              load方法就是用來(lái)獲取圖片。注冊(cè)監(jiān)聽(tīng)器,這里我們還是使用Event.COMPLETE事件類型,當(dāng)圖片加載完成后,會(huì)調(diào)用回調(diào)函數(shù),并將參數(shù)也一起傳給回調(diào)函數(shù)。畫圖工作是在回調(diào)函數(shù)中進(jìn)行。另外有一點(diǎn)要說(shuō)明的是,Loader是用來(lái)加載SWF文件或圖像(JPGPNG GIF)文件,URLLoader 類則是加載文本或二進(jìn)制數(shù)據(jù),請(qǐng)大家使用的時(shí)候要注意這點(diǎn)區(qū)別。
              另外這里不管是畫線還是畫圖都用的是UIComponent類,這是因?yàn)樗幸粋€(gè)toolTip屬性,當(dāng)鼠標(biāo)移至圖或線上時(shí),會(huì)顯示你設(shè)置的信息。如果用Sprite則沒(méi)有這屬性。UIComponentFlex定義的,而Sprite則是Flash本身的。
              畫圖例的方法是一樣的,畫好的圖需要加入到父容器中顯示出來(lái)。到此整個(gè)做法就講完了,其實(shí)也不是很復(fù)雜,關(guān)鍵是要理清思路。因?yàn)樯婕暗缴虡I(yè)項(xiàng)目和保密原則,請(qǐng)?jiān)彺笙蟛荒軐⒋嗽创a拿出來(lái)給大家分享,不過(guò)我已經(jīng)提供了部分代碼,而且這些代碼是整個(gè)應(yīng)用中關(guān)鍵點(diǎn)。只要大家能從中得到一點(diǎn)幫助,那么我就很滿足了。
              本文為菠蘿大象原創(chuàng),如要轉(zhuǎn)載請(qǐng)注明出處。
          posted on 2010-02-25 21:13 菠蘿大象 閱讀(5473) 評(píng)論(5)  編輯  收藏 所屬分類: Flex

          評(píng)論:
          # re: 使用Flex開(kāi)發(fā)時(shí)空線形圖實(shí)例詳解 2010-02-25 21:16 | 菠蘿大象
          我今天發(fā)現(xiàn)寫掉了一部分,就是第三點(diǎn),坐標(biāo)計(jì)算,現(xiàn)在補(bǔ)上了,請(qǐng)大家不要怪我又重新發(fā)布一次,我希望讓先看過(guò)的人再看一下,對(duì)不起大家了。  回復(fù)  更多評(píng)論
            
          # re: 使用Flex開(kāi)發(fā)時(shí)空線形圖實(shí)例詳解[未登錄](méi) 2010-02-26 12:08 | lazy
          學(xué)習(xí)一下
          另外能否貼一下xml數(shù)據(jù)的格式?
          var spaces:* = xml.spaces.space;
          我對(duì)這一行代碼也不太明白  回復(fù)  更多評(píng)論
            
          # re: 使用Flex開(kāi)發(fā)時(shí)空線形圖實(shí)例詳解 2010-02-26 13:18 | 菠蘿大象
          @lazy
          謝謝提醒,是我疏忽了,我在后臺(tái)用jdom封裝xml,具體怎么定義格式完全要看你在應(yīng)用中怎么方便取值,以及需要用到哪些數(shù)據(jù)。我帖上應(yīng)用中的部分
          <?xml version="1.0" encoding="UTF-8"?>
          <project>
          <spaces>
          <space>
          <id>266</id>
          <order>1</order>
          <projectName>金銀潭站</projectName>
          <segment>1標(biāo)</segment>
          <stationType>1</stationType>
          </space>
          <space>
          <id>267</id>
          <order>2</order>
          <projectName>金常區(qū)間</projectName>
          <segment>2標(biāo)</segment>
          <stationType>2</stationType>
          </space>
          </spaces>
          <tasks>
          <task parent_id="933">
          <id>934</id>
          <name>征地拆遷等前期工程</name>
          <beginDate>2007-09-01</beginDate>
          <endDate>2008-01-01</endDate>
          <shapeState>3</shapeState>
          <position1>266</position1>
          <position2>266</position2>
          <frontColor>#000000</frontColor>
          <backColor>#000000</backColor>
          <designPath>/upload/Image/1238725740078_7741.jpg</designPath>
          </task>
          </tasks>
          </project>  回復(fù)  更多評(píng)論
            
          # re: 使用Flex開(kāi)發(fā)時(shí)空線形圖實(shí)例詳解[未登錄](méi) 2010-02-26 13:45 | lazy
          @菠蘿大象

          謝謝,有了xml格式就明白了,原來(lái)as里對(duì)xml的引用這么方便  回復(fù)  更多評(píng)論
            
          # re: 使用Flex開(kāi)發(fā)時(shí)空線形圖實(shí)例詳解 2010-02-26 16:19 | 菠蘿大象
          @lazy
          在ActionScript3.0中,解析xml確實(shí)非常方便,要引用屬性可以這樣寫:task.@parent_id,引用元素直接用.(點(diǎn))。詳細(xì)的資料,網(wǎng)上有很多,我就不寫了。  回復(fù)  更多評(píng)論
            
          主站蜘蛛池模板: 伊春市| 铜陵市| 扎囊县| 英山县| 金山区| 西吉县| 玉溪市| 贵溪市| 河池市| 宁河县| 咸丰县| 德令哈市| 南丹县| 甘泉县| 德惠市| 抚松县| 祁阳县| 波密县| 呈贡县| 凯里市| 南雄市| 忻城县| 威海市| 邻水| 华安县| 澜沧| 佛坪县| 邳州市| 扶风县| 张家口市| 凤翔县| 孝义市| 砚山县| 沧州市| 上思县| 黔南| 龙州县| 自治县| 股票| 老河口市| 民权县|