1、在FLEX已有組件無法滿足業務需求,或是需要更改其可視化外觀等特性時,直接進行繼承擴展。
2、為了模塊化設計或進一步重用,需要對FLEX組件進行組合。
而Flex組件開發有2種方式:AS方式和MXML方式。對於上述第一個原因我一般采用AS方式,通過繼承UIComponent來開發,而針對原因2我一般使用的是MXML方式。本文主要講的是AS開發方式。
重寫一個組件依次調用的方法 :
1)Constructor構造方法,初始化屬性,默認值 在這個方法中使用最好。
2)createChildren() 創建子對象,在組件中添加子對象。是使用addChild方法添加子對象
3)commitProperties 用在處理屬性值和更新。(多個屬性值更新后統一處理入口和單值多次修改后處理入口)
4)measure()設置組件的默認大小(以便Flex布局管理器能正確知道該組件的大小,給其分配適當空間)
5)updateDisplayList()用來重繪組件,子對象布局邏輯等
我們通過這樣一個例子來講解(鼠標划過彈出數據顯示)。

首先是Constructor方法:
public function MultilayerHorizontalBarChart() { super(); }
由於該例不需要在構造初始化屬性(一般簡單數據類型在定義的時候就已經賦值),所以構造方法沒內容;
接着是createChildren方法:
override protected function createChildren():void { super.createChildren(); if (_mXis == null) { _mXis = new Group(); addChild(_mXis); } if (_mYis == null) { _mYis = new Group(); addChild(_mYis); } if (_mHBarIDLable == null ) { _mHBarIDLable = new Group(); addChild(_mHBarIDLable); } if (_gridLines == null) { _gridLines = new Group(); addChild(_gridLines); } if (_mMainHDraw == null) { _mMainHDraw = new Group(); addChild(_mMainHDraw); } if (_mEventGroup == null){ _mEventGroup = new Group(); addChild(_mEventGroup); _mEventGroup.addEventListener(MouseEvent.MOUSE_OUT, deleDataHandler); _mEventGroup.addEventListener(MouseEvent.MOUSE_MOVE,showDataHandler); } if (mFloatIsShow) { if (floatDataPanel == null) { floatDataPanel = new BorderContainer(); floatInerPanel = new BorderContainer(); floatDataPanel.setStyle("cornerRadius",15); floatInerPanel.setStyle("cornerRadius",15); var vs:SolidColor = new SolidColor(); vs.color = 0x6A726B; vs.alpha = 0.3; floatDataPanel.backgroundFill = vs; floatDataPanel.visible = false; _mEventGroup.addElement(floatDataPanel); } } }
這里涉及到組件子對象的划分,本例子中,具有的子對象有_mXis X軸容器對象,_mYis Y軸容器對象,_mHBarIDLable 柱子標簽容器對象(最上方月份提示那個),_gridLines 網格容器對象,_mMainHDraw 主畫布對象(用來畫柱狀圖的容器),_mEventGroup 鼠標事件監聽容器對象,floatDataPanel、floatInerPanel 是顯示數據的浮動框容器對象。在該方法里我們初始化這些子對象並把他們添加到組件容器中。
接下來是commitProperties 用在處理屬性值和更新,本例子中這些事情都在updateDisplayList處理,重點掌握updateDisplayList方法的重寫。
接着是measure方法的重寫(本例中,重繪的時候都會重新調整自對象的位置和大小,所以該方法的重寫也可以省略):
override protected function measure():void { super.measure(); measuredMinHeight = measuredHeight = DEFAULT_HEIGHT; measuredMinWidth = measuredWidth = DEFAULT_WIDTH; }
接下來是本例的重頭戲,updateDisplayList方法的重寫:
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void { if (_isReflash) { super.updateDisplayList(unscaledWidth, unscaledHeight); if (dataProvider == null) { return; } for (var i:int = 0; i < dataProvider.ColumnName.length; i++) { //如果存在空數據就設定標識退出循環 if (null == dataProvider.Data[dataProvider.ColumnName[i]]) { _checkData = false; break; } } if (_checkData) { drawLayouts(unscaledWidth,unscaledHeight); drawXAxis(); drawYAxis(); drawGrid(); legendHBarLine(); drawHBar(); } _isReflash = false; } }
該方法中,先對數據源進行檢驗,只有但數據源不為空並且是新的數據源才進行重繪,否則不作處理。重繪做的事情主要是: drawLayouts(unscaledWidth,unscaledHeight)子對象布局,子對象位置和大小的確定;drawXAxis()重繪X軸容器自對象;drawYAxis()重繪Y軸容器子對象;legendHBarLine()重繪數據標簽提示自對象;drawGrid()、drawHBar()主畫布區域子對象重繪,畫網格和柱狀圖;
drawLayouts方法:
protected function drawLayouts(pW:Number, pH:Number):void { this._mXis.setActualSize(pW - this.mYAxisWidth, this.mXAxisHeight); _mXis.move(this.mYAxisWidth, pH - this.mXAxisHeight); this._mYis.setActualSize(this.mYAxisWidth, pH - this.mXAxisHeight - this.mHBarIDLableHeight); this._mYis.move(0,this.mHBarIDLableHeight); //X軸標簽位置 this._mHBarIDLable.setActualSize(pW - this.mYAxisWidth, this.mHBarIDLableHeight); this._mHBarIDLable.move(this.mYAxisWidth, -10); this._gridLines.setActualSize(pW - this.mYAxisWidth, pH - this.mXAxisHeight - this.mHBarIDLableHeight); this._gridLines.move(this.mYAxisWidth, this.mHBarIDLableHeight); this._mMainHDraw.setActualSize(pW - this.mYAxisWidth, pH - this.mXAxisHeight - this.mHBarIDLableHeight); this._mMainHDraw.move(this.mYAxisWidth, this.mHBarIDLableHeight); this._mEventGroup.setActualSize(pW - this.mYAxisWidth, pH - this.mXAxisHeight - this.mHBarIDLableHeight); this._mEventGroup.move(this.mYAxisWidth, this.mHBarIDLableHeight); }
確定各子對象的寬高和x,y坐標,對整個組件容器區域進行划分。
drawXAxis方法:
protected function drawXAxis():void { var _mXMaxValue:Number =100; // TODO Auto Generated method stub var vGroup:Group = this._mXis; var vG:Graphics = vGroup.graphics; vGroup.removeAllElements(); vG.clear(); vG.lineStyle(1, 0x000000,0.8); vG.moveTo(0, 0); vG.lineTo(vGroup.width,0); _mXCachedTicks = new Vector.<Number>(); _mXCacheValueTicks = new Vector.<Number>(); //將X軸分成xbisectNum等份 默認為10 var vCount:int = xBisectNum; var vGap:Number = vGroup.width / vCount; var vGapValue:Number = _mXMaxValue / vCount; var vNum:Number; var vX:Number for(var i:int=0; i <= vCount; i++){ vX = i * vGap; vG.moveTo(vX, 0); vG.lineTo(vX,6); _mXCachedTicks.push(vX); var vTextField:Text = new Text(); vNum = i*vGapValue; vTextField.text = vNum.toString() + "%"; var vTlm:TextLineMetrics = measureText(vTextField.text); vTextField.move( vX - vTlm.width / 2, 10); vGroup.addElement(vTextField); } vGroup = null; vG = null; }
畫坐標軸並添加上刻度值數據標簽。
drawYAxis方法:
protected function drawYAxis():void { var vGroup:Group = this._mYis; var vG:Graphics = vGroup.graphics; vGroup.removeAllElements(); vG.clear(); vG.lineStyle(1, 0x000000,0.8); vG.moveTo(vGroup.width, 0); vG.lineTo(vGroup.width, vGroup.height); _mYCachedTicks = new Vector.<Number>(); //Y軸數據的個數 var vCount:int = this.dataProvider.RowCount; var vGap:Number = vGroup.height / vCount; //存儲Y軸刻度高度,供之后畫圖使用 this._mYGap = vGap; vG.moveTo(vGroup.width - 6, 0); vG.lineTo(vGroup.width, 0); for (var i:int = 0; i <= vCount; i++) { var vY:Number = (i) * vGap; vG.moveTo(vGroup.width-6, vY); vG.lineTo(vGroup.width, vY); _mYCachedTicks.push(vY); var vTextField:Label = new Label(); if (i < vCount) { //把Y軸的顯示的標簽加上 vTextField.text = this.dataProvider.Data[categoryField][i]; var vTlm:TextLineMetrics = measureText(vTextField.text); vTextField.move(vGroup.width - vTlm.width - 20, vY + vGap/2 - vTlm.height / 2); vGroup.addElement(vTextField); } } vGroup = null; vG = null; }
畫Y軸坐標軸並添加數據指標標簽。
legendHBarLine方法:
protected function legendHBarLine():void { var vGroup:Group = this._mHBarIDLable; var vIDLableLen:Number = vGroup.width/this._mHBarCenNum; var vGap:Number = vIDLableLen / 6; var vHLineLen:Number = vGap*0.6; var vHY:Number = vGroup.height /2; var vG:Graphics =vGroup.graphics; _mHBarIDLable.removeAllElements(); vG.clear(); var vLBuffef:Number = 0; for(var i:int=0; i < this._mHBarCenNum; i++) { var vName:Label = new Label(); vG.lineStyle(0.2, this.mHBarColorArray[i%this.mHBarColorArray.length]); //對顏色數組取余可防止顏色數組不足時index越界 vG.beginFill(mHBarColorArray[i]); vG.drawRect(vLBuffef,vHY,vHLineLen,vHLineLen); vG.endFill(); vName.text = this.dataProvider.ColumnName[i+1]; vName.x = vLBuffef + vHLineLen; vName.y = vHY/2; _mHBarIDLable.addElement(vName); vLBuffef += vIDLableLen; } vGroup = null; vG = null; }
容器內頂部畫添加數據提示標簽。
drawGrid方法:
protected function drawGrid():void { var vGroup:Group = _gridLines; var vG:Graphics = vGroup.graphics; vG.clear(); var vColor:uint = 0xBFBFBF; var vAlpha:Number = 0.3; vG.lineStyle(1, vColor, vAlpha); var vLen:Number; var vPoint:Point; var i:int; var pos:int; //畫橫線 if (_mYCachedTicks && _mYCachedTicks.length > 0) { vLen = vGroup.width / 10; var vgHeight:Number; for (i = 0; i < _mYCachedTicks.length - 1; i++) { vgHeight = _mYCachedTicks[i]; for (pos = 0; pos < vLen; pos++) { vG.moveTo(pos * 10, vgHeight); vG.lineTo(pos * 10 + 6, vgHeight); } } } //畫豎線 if (_mXCachedTicks && _mXCachedTicks.length > 0) { vLen = vGroup.height / 10; var vMax:int = _mXCachedTicks.length; for (i = 1; i < vMax; i++) { var vWidth:Number = _mXCachedTicks[i]; for (pos = 0; pos < vLen; pos++) { vG.moveTo(vWidth, pos * 10); vG.lineTo(vWidth, pos * 10 + 6); } } } }
畫網格,虛線,這里以10個像素為單位,畫6個像素點,空4個像素點也就成了虛線。
drawHBar方法:
protected function drawHBar():void { //矩形高度 var vHBarHeight:Number = this._mYGap * 2 / 5; _mMainHDraw.removeAllElements(); var vGroup:Group = this._mMainHDraw; vGroup.removeAllElements(); var vG:Graphics = vGroup.graphics; vG.clear(); var vColumnNameTemp:String = null; //從左到右從上到下畫矩形 var columnLen:int = this.dataProvider.ColumnName.length; //用來存放遍歷到的主要數據 var vValue:Number = 0; for(var vIndex:int = 0; vIndex < this.dataProvider.RowCount; vIndex++) { //累積的矩形寬度,最長不能超過100% var vSumWidth:Number = 0; //第一個數組0放的是Y軸標簽,直接略過 for(var vColumnIndex:int = 1; vColumnIndex < columnLen; vColumnIndex++) { vColumnNameTemp = this.dataProvider.ColumnName[vColumnIndex]; //vColumnIndex是從1開始的,跳過“yAxisData”字段,這里要減去1 var vColor:uint = this.mHBarColorArray[(vColumnIndex-1) % this.mHBarColorArray.length]; vValue = Number(this._mPercentData[vColumnNameTemp][vIndex]); if( vSumWidth>1) { vSumWidth = 1; } vG.beginFill(vColor); vG.drawRect(vSumWidth*this._mMainHDraw.width, this._mYCachedTicks[vIndex] + this._mYGap/2 - vHBarHeight/2 , vValue*this._mMainHDraw.width, vHBarHeight); vG.endFill(); vSumWidth += vValue; } } vGroup = null; vG = null; }
根據數據源計算出個柱子的位置和大小並在主畫布區域畫出來。
還有鼠標事件處理方法就不一一列出,具體看完整代碼。
MultilayerHorizontalBarChart.as文件:
package LineChartTableTest { import DataEntity.DataTable; import flash.display.Graphics; import flash.events.MouseEvent; import flash.geom.Point; import flash.text.TextLineMetrics; import flash.utils.Dictionary; import mx.controls.Label; import mx.controls.Text; import mx.core.UIComponent; import mx.graphics.SolidColor; import spark.components.BorderContainer; import spark.components.Group; import spark.layouts.VerticalLayout; /** * 水平疊柱狀圖 * @author haojie.wang * @date 2013-4-27 * @version 1.0 * <b> * X軸為百分比,Y軸為各指標\n * 這里必須注意的是categoryField的設置,dataTable的column[0]必須是_categoryField的值 * </b> */ public class MultilayerHorizontalBarChart extends UIComponent { protected static const DEFAULT_HEIGHT:Number = 300; protected static const DEFAULT_WIDTH:Number = 960; //=========================== // 可通過外部屬性定義從而改變控件屬性start //=========================== /** * X軸的高度 */ protected var _mXAxisHeight:Number = 50; /** * Y軸寬度 */ protected var _mYAxisWidth:Number = 100; /** * 等分值,把X軸設成多少等分,默認為10 */ private var _xBisectNum:int = 10; /** * X軸單位 */ private var _mXUnit:String; /** * Y軸單位 */ private var _mYUnit:String; /** * 圖表名稱 */ private var _mChartName:String; /** * 是否顯示懸浮框 */ protected var _mFloatIsShow:Boolean = true; //=========================== // 可通過外部屬性定義從而改變控件屬性end //=========================== //=========================== // 接收控件所需要的數據參數start //=========================== /** * 主要數據源 */ protected var _dataProvider:DataTable; /** * 字段參數 ,默認為“yAxisData” */ protected var _categoryField:String ="yAxisData"; /** * 顏色數組 */ protected var _mHBarColorArray:Array; //=========================== // 接收控件所需要的數據參數end //=========================== //=========================== // 控件內部使用屬性,不需要外部傳值start //=========================== /** * 每行的總和數組,統計每行各數值的和 */ protected var _mRowSumArray:Array; /** * 存放DataTable里data數據的百分比格式 */ protected var _mPercentData:Dictionary; /** *需要畫的柱子的層數,默認為6 */ protected var _mHBarCenNum:int = 6; /** * X軸度量值間隔 */ protected var _mXGap:Number; /** * Y軸度量值間隔 */ protected var _mYGap:Number; /** * 暫存X軸刻度數據 */ protected var _mXCachedTicks:Vector.<Number> /* 數字 */ = null; /** * 暫存X軸數據,累計刻度值 */ protected var _mXCacheValueTicks:Vector.<Number> /* 數字 */ = null; /** * X軸刻度相關,距離為1像素,暫時不給外部輸入,無關緊要,setter和getter方法注銷掉了 */ protected var _mPadding:Number = 1; /** * Y刻度緩存數組 */ protected var _mYCachedTicks:Vector.<Number> = null; /** * Y軸的最大值 */ protected var _mYMaxValue:Number; /** * 當前Y軸光標索引 */ protected var _countIndexY:int = 0; /** * 檢查數據是否為空,true表示數據不為空 */ protected var _checkData:Boolean = true; /** * 直方圖數量 */ protected var _mHBarNum:int; /** * X軸 */ private var _mXis:Group; /** * Y軸 */ private var _mYis:Group; /** * 直方圖上方的數據刷新顯示區域 */ private var _mHBarIDLable:Group; /** * Y軸上ID標簽的高度 */ protected var _mHBarIDLableHeight:int = 25; /** * 網格 */ private var _gridLines:Group; /** * 主要數據顯示區域 */ private var _mMainHDraw:Group; /** * 事件監聽層 */ private var _mEventGroup:Group; /** * 是否為刷新 */ protected var _isReflash:Boolean = true; /** * 浮動框外層 */ private var floatDataPanel:BorderContainer; /** * 浮動框內層 */ private var floatInerPanel:BorderContainer; /** * 浮動框內層顯示的內容 */ private var vLabel1: Text= new Text(); //=========================== // 控件內部使用屬性,不需要外部傳值end //=========================== public function MultilayerHorizontalBarChart() { super(); } override protected function createChildren():void { super.createChildren(); if (_mXis == null) { _mXis = new Group(); addChild(_mXis); } if (_mYis == null) { _mYis = new Group(); addChild(_mYis); } if (_mHBarIDLable == null ) { _mHBarIDLable = new Group(); addChild(_mHBarIDLable); } if (_gridLines == null) { _gridLines = new Group(); addChild(_gridLines); } if (_mMainHDraw == null) { _mMainHDraw = new Group(); addChild(_mMainHDraw); } if (_mEventGroup == null){ _mEventGroup = new Group(); addChild(_mEventGroup); _mEventGroup.addEventListener(MouseEvent.MOUSE_OUT, deleDataHandler); _mEventGroup.addEventListener(MouseEvent.MOUSE_MOVE,showDataHandler); } if (mFloatIsShow) { if (floatDataPanel == null) { floatDataPanel = new BorderContainer(); floatInerPanel = new BorderContainer(); floatDataPanel.setStyle("cornerRadius",15); floatInerPanel.setStyle("cornerRadius",15); var vs:SolidColor = new SolidColor(); vs.color = 0x6A726B; vs.alpha = 0.3; floatDataPanel.backgroundFill = vs; floatDataPanel.visible = false; _mEventGroup.addElement(floatDataPanel); } } } override protected function commitProperties():void { super.commitProperties(); //顯示浮動窗口 if (mFloatIsShow) { var vsIner:SolidColor = new SolidColor(); vsIner.color = 0x000000; vsIner.alpha = 0.8; var vver:VerticalLayout = new VerticalLayout(); vver.paddingBottom = 2; vver.paddingLeft = 2; vver.paddingRight = 2; vver.paddingTop =2; floatDataPanel.layout = vver; floatInerPanel.backgroundFill = vsIner; floatInerPanel.setStyle("color",0xFFFFFF); floatInerPanel.addElement(vLabel1); vLabel1.horizontalCenter = 0; floatDataPanel.addElement(floatInerPanel); floatInerPanel.move(2,2); floatDataPanel.setActualSize(200,vLabel1.height+50); floatInerPanel.setActualSize(vLabel1.width,vLabel1.height); } } override protected function measure():void { super.measure(); measuredMinHeight = measuredHeight = DEFAULT_HEIGHT; measuredMinWidth = measuredWidth = DEFAULT_WIDTH; } override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void { if (_isReflash) { super.updateDisplayList(unscaledWidth, unscaledHeight); if (dataProvider == null) { return; } for (var i:int = 0; i < dataProvider.ColumnName.length; i++) { //如果存在空數據就設定標識退出循環 if (null == dataProvider.Data[dataProvider.ColumnName[i]]) { _checkData = false; break; } } if (_checkData) { drawLayouts(unscaledWidth,unscaledHeight); drawXAxis(); drawYAxis(); drawGrid(); legendHBarLine(); drawHBar(); } _isReflash = false; } } /** * 安排布局 */ protected function drawLayouts(pW:Number, pH:Number):void { this._mXis.setActualSize(pW - this.mYAxisWidth, this.mXAxisHeight); _mXis.move(this.mYAxisWidth, pH - this.mXAxisHeight); this._mYis.setActualSize(this.mYAxisWidth, pH - this.mXAxisHeight - this.mHBarIDLableHeight); this._mYis.move(0,this.mHBarIDLableHeight); //X軸標簽位置 this._mHBarIDLable.setActualSize(pW - this.mYAxisWidth, this.mHBarIDLableHeight); this._mHBarIDLable.move(this.mYAxisWidth, -10); this._gridLines.setActualSize(pW - this.mYAxisWidth, pH - this.mXAxisHeight - this.mHBarIDLableHeight); this._gridLines.move(this.mYAxisWidth, this.mHBarIDLableHeight); this._mMainHDraw.setActualSize(pW - this.mYAxisWidth, pH - this.mXAxisHeight - this.mHBarIDLableHeight); this._mMainHDraw.move(this.mYAxisWidth, this.mHBarIDLableHeight); this._mEventGroup.setActualSize(pW - this.mYAxisWidth, pH - this.mXAxisHeight - this.mHBarIDLableHeight); this._mEventGroup.move(this.mYAxisWidth, this.mHBarIDLableHeight); } /** * 畫百分比X軸 */ protected function drawXAxis():void { var _mXMaxValue:Number =100; // TODO Auto Generated method stub var vGroup:Group = this._mXis; var vG:Graphics = vGroup.graphics; vGroup.removeAllElements(); vG.clear(); vG.lineStyle(1, 0x000000,0.8); vG.moveTo(0, 0); vG.lineTo(vGroup.width,0); _mXCachedTicks = new Vector.<Number>(); _mXCacheValueTicks = new Vector.<Number>(); //將X軸分成xbisectNum等份 默認為10 var vCount:int = xBisectNum; var vGap:Number = vGroup.width / vCount; var vGapValue:Number = _mXMaxValue / vCount; var vNum:Number; var vX:Number for(var i:int=0; i <= vCount; i++){ vX = i * vGap; vG.moveTo(vX, 0); vG.lineTo(vX,6); _mXCachedTicks.push(vX); var vTextField:Text = new Text(); vNum = i*vGapValue; vTextField.text = vNum.toString() + "%"; var vTlm:TextLineMetrics = measureText(vTextField.text); vTextField.move( vX - vTlm.width / 2, 10); vGroup.addElement(vTextField); } vGroup = null; vG = null; } /** * 畫Y軸 */ protected function drawYAxis():void { var vGroup:Group = this._mYis; var vG:Graphics = vGroup.graphics; vGroup.removeAllElements(); vG.clear(); vG.lineStyle(1, 0x000000,0.8); vG.moveTo(vGroup.width, 0); vG.lineTo(vGroup.width, vGroup.height); _mYCachedTicks = new Vector.<Number>(); //Y軸數據的個數 var vCount:int = this.dataProvider.RowCount; var vGap:Number = vGroup.height / vCount; //存儲Y軸刻度高度,供之后畫圖使用 this._mYGap = vGap; vG.moveTo(vGroup.width - 6, 0); vG.lineTo(vGroup.width, 0); for (var i:int = 0; i <= vCount; i++) { var vY:Number = (i) * vGap; vG.moveTo(vGroup.width-6, vY); vG.lineTo(vGroup.width, vY); _mYCachedTicks.push(vY); var vTextField:Label = new Label(); if (i < vCount) { //把Y軸的顯示的標簽加上 vTextField.text = this.dataProvider.Data[categoryField][i]; var vTlm:TextLineMetrics = measureText(vTextField.text); vTextField.move(vGroup.width - vTlm.width - 20, vY + vGap/2 - vTlm.height / 2); vGroup.addElement(vTextField); } } vGroup = null; vG = null; } /** * 畫網格 */ protected function drawGrid():void { var vGroup:Group = _gridLines; var vG:Graphics = vGroup.graphics; vG.clear(); var vColor:uint = 0xBFBFBF; var vAlpha:Number = 0.3; vG.lineStyle(1, vColor, vAlpha); var vLen:Number; var vPoint:Point; var i:int; var pos:int; //畫橫線 if (_mYCachedTicks && _mYCachedTicks.length > 0) { vLen = vGroup.width / 10; var vgHeight:Number; for (i = 0; i < _mYCachedTicks.length - 1; i++) { vgHeight = _mYCachedTicks[i]; for (pos = 0; pos < vLen; pos++) { vG.moveTo(pos * 10, vgHeight); vG.lineTo(pos * 10 + 6, vgHeight); } } } //畫豎線 if (_mXCachedTicks && _mXCachedTicks.length > 0) { vLen = vGroup.height / 10; var vMax:int = _mXCachedTicks.length; for (i = 1; i < vMax; i++) { var vWidth:Number = _mXCachedTicks[i]; for (pos = 0; pos < vLen; pos++) { vG.moveTo(vWidth, pos * 10); vG.lineTo(vWidth, pos * 10 + 6); } } } } /** * 畫數據標簽提示 */ protected function legendHBarLine():void { var vGroup:Group = this._mHBarIDLable; var vIDLableLen:Number = vGroup.width/this._mHBarCenNum; var vGap:Number = vIDLableLen / 6; var vHLineLen:Number = vGap*0.6; var vHY:Number = vGroup.height /2; var vG:Graphics =vGroup.graphics; _mHBarIDLable.removeAllElements(); vG.clear(); var vLBuffef:Number = 0; for(var i:int=0; i < this._mHBarCenNum; i++) { var vName:Label = new Label(); vG.lineStyle(0.2, this.mHBarColorArray[i%this.mHBarColorArray.length]); //對顏色數組取余可防止顏色數組不足時index越界 vG.beginFill(mHBarColorArray[i]); vG.drawRect(vLBuffef,vHY,vHLineLen,vHLineLen); vG.endFill(); vName.text = this.dataProvider.ColumnName[i+1]; vName.x = vLBuffef + vHLineLen; vName.y = vHY/2; _mHBarIDLable.addElement(vName); vLBuffef += vIDLableLen; } vGroup = null; vG = null; } /** * 畫直方圖 */ protected function drawHBar():void { //矩形高度 var vHBarHeight:Number = this._mYGap * 2 / 5; _mMainHDraw.removeAllElements(); var vGroup:Group = this._mMainHDraw; vGroup.removeAllElements(); var vG:Graphics = vGroup.graphics; vG.clear(); var vColumnNameTemp:String = null; //從左到右從上到下畫矩形 var columnLen:int = this.dataProvider.ColumnName.length; //用來存放遍歷到的主要數據 var vValue:Number = 0; for(var vIndex:int = 0; vIndex < this.dataProvider.RowCount; vIndex++) { //累積的矩形寬度,最長不能超過100% var vSumWidth:Number = 0; //第一個數組0放的是Y軸標簽,直接略過 for(var vColumnIndex:int = 1; vColumnIndex < columnLen; vColumnIndex++) { vColumnNameTemp = this.dataProvider.ColumnName[vColumnIndex]; //vColumnIndex是從1開始的,跳過“yAxisData”字段,這里要減去1 var vColor:uint = this.mHBarColorArray[(vColumnIndex-1) % this.mHBarColorArray.length]; vValue = Number(this._mPercentData[vColumnNameTemp][vIndex]); if( vSumWidth>1) { vSumWidth = 1; } vG.beginFill(vColor); vG.drawRect(vSumWidth*this._mMainHDraw.width, this._mYCachedTicks[vIndex] + this._mYGap/2 - vHBarHeight/2 , vValue*this._mMainHDraw.width, vHBarHeight); vG.endFill(); vSumWidth += vValue; } } vGroup = null; vG = null; } /** * 顯示數據處理 * @param pEvent * */ protected function showDataHandler(pEvent:MouseEvent):void { countIndexY = int(pEvent.localY / this._mYGap); //第幾個柱子 if (countIndexY >= this._mHBarNum) { return; } //鼠標光標在柱子上時候,柱子高度占刻度值的1/3 if (pEvent.localY > (countIndexY+1.5/5) * this._mYGap && pEvent.localY < (countIndexY + 3.5 / 5) * this._mYGap) { //如果顯示浮動框,要設置好位置,防止出界 if (mFloatIsShow) { // trace("Y:" + pEvent.localY + "X:" + pEvent.localX); if (pEvent.localY > this._mMainHDraw.height - floatDataPanel.height- 10 ) { if (pEvent.localX > this._mMainHDraw.width - floatDataPanel.width) { floatDataPanel.move(pEvent.localX - floatDataPanel.width, pEvent.localY -floatDataPanel.height-10); } else { floatDataPanel.move(pEvent.localX , pEvent.localY -floatDataPanel.height-10); } } else { if (pEvent.localX > this._mMainHDraw.width - floatDataPanel.width) { floatDataPanel.move(pEvent.localX - floatDataPanel.width, pEvent.localY + 10); } else { floatDataPanel.move(pEvent.localX, pEvent.localY +10); } } updHBarLabelData(countIndexY,pEvent.localX); } } else { updHBarLabelData(-1,0); } } /** * 更新顯示數據 * @param mHBarIndexY 當前是第幾個柱子 * @param pMouseX 鼠標的位置 */ protected function updHBarLabelData(mHBarIndexY:int, pMouseX:Number):void { if (mHBarIndexY == -1) { if(mFloatIsShow) { floatDataPanel.visible = false; } } else { //防止數組越界 if (mHBarIndexY < this.dataProvider.RowCount) { var vDataString:String = "XXX"; var vSourceDataString:String = "XX"; var vColumnName:String = "OO"; var vSumNum:Number = 0; //累計柱子各段長百分比 var vSumNumArr:Array = new Array(); //累計柱子各段長百分比數組,用來判斷光標在那段柱子上 vSumNumArr[0] = vSumNum; var vIndex:int = 1; for each(var vItem:String in this.dataProvider.ColumnName) { //categoryField字段是Y軸標簽,這里跳過 if (categoryField != vItem) { vSumNum += this._mPercentData[vItem][mHBarIndexY]; vSumNumArr[vIndex] = vSumNum; //找到對應的區間 if (vSumNumArr[vIndex - 1] * this._mMainHDraw.width < pMouseX && pMouseX <= vSumNumArr[vIndex] * this._mMainHDraw.width) { vColumnName = vItem; vSourceDataString = this.dataProvider.Data[vItem][mHBarIndexY].toString(); vDataString = percentDataFormatter(this._mPercentData[vItem][mHBarIndexY] * 100); break; } vIndex++; } } if (mFloatIsShow) { if (this.mChartName != null && this._mYUnit != null) { vLabel1.text = this.dataProvider.Data[categoryField][mHBarIndexY] + this._mYUnit + " " +vColumnName + " " + this.mChartName + " : " +vDataString + "(" + vSourceDataString + "/" + this._mRowSumArray[mHBarIndexY] + ")"; } else { vLabel1.text = this.dataProvider.Data[categoryField][mHBarIndexY] + " : " +vDataString + "(" + vSourceDataString + "/" + this._mRowSumArray[mHBarIndexY] + ")"; } vLabel1.horizontalCenter = 0; vLabel1.verticalCenter = 0; //獲取鼠標所在的柱子的顏色 var vHFloatColor:uint = this.mHBarColorArray[(this.dataProvider.ColumnName.indexOf(vColumnName)-1)%this.mHBarColorArray.length]; changeFloatBackColor(vHFloatColor); floatDataPanel.visible = true; } } } } /** * * 鼠標移除事件 */ protected function deleDataHandler(pEvent:MouseEvent):void { removeEventListener(MouseEvent.MOUSE_MOVE,showDataHandler); if (mFloatIsShow) { floatDataPanel.visible = false; } } /** * 動態改變浮動框背景 **/ protected function changeFloatBackColor(pColor:uint):void { var vs:SolidColor = new SolidColor(); vs.color = pColor; vs.alpha = 0.8; floatInerPanel.backgroundFill = vs; } /** * 將傳入的數值格式化為百分比形式 * @param pNumber 要格式化的數值 * @return 以百分比形式顯示的字符串 * */ protected function percentDataFormatter(pNumber:Number):String { var vNumber:Number = pNumber; return vNumber.toFixed(2) + "%"; } //=========================== // 屬性定義的setter和getter方法start //=========================== /** * X軸的高度 */ public function get mXAxisHeight():Number { return _mXAxisHeight; } public function set mXAxisHeight(value:Number):void { _mXAxisHeight = value; } /** * Y軸寬度 * @return */ public function get mYAxisWidth():Number { return _mYAxisWidth; } public function set mYAxisWidth(value:Number):void { _mYAxisWidth = value; } /** * 當前光標Y軸索引 * @return */ public function get countIndexY():int { return _countIndexY; } public function set countIndexY(value:int):void { _countIndexY = value; } /** * 直方圖上方標簽的高度 * @return _mHBarIDLableHeight; */ public function get mHBarIDLableHeight():int { return _mHBarIDLableHeight; } public function set mHBarIDLableHeight(value:int):void { _mHBarIDLableHeight = value; } /** * 數據源 * @return _dataProvider; */ public function get dataProvider():DataTable { return _dataProvider; } public function set dataProvider(value:DataTable):void { this._dataProvider = value; this._mHBarNum = this.dataProvider.RowCount; this._mHBarCenNum = this.dataProvider.ColumnName.length -1; this._mRowSumArray = new Array(this._mHBarNum); var vIndex:int = 0; //統計個行數據的和,並把它存放進總和數組 for (var i:int = 0; i < this.dataProvider.RowCount; i++) { var vSum:Number = 0; for each (var vItem:String in this.dataProvider.ColumnName) { //統計各行數據的和 if (vItem != this.categoryField) { vSum += Number(this.dataProvider.Data[vItem][i]) ; } } this._mRowSumArray[vIndex] = vSum; vIndex ++; } //把dataTable的Data數據轉換成相對應的百分比 this._mPercentData = new Dictionary(); for each (var vItem1:String in this.dataProvider.ColumnName) { if (vItem1 != this.categoryField) { this._mPercentData[vItem1] = new Array(); for (var j:int = 0; j < this.dataProvider.RowCount; j++) { this._mPercentData[vItem1][j] = Number(this.dataProvider.Data[vItem1][j]) / this._mRowSumArray[j]; } } } this.invalidateDisplayList(); } /** * 字段參數 默認為“yAxisData” * @return _categoryField; */ public function get categoryField():String { return _categoryField; } public function set categoryField(value:String):void { _categoryField = value; } /** * 是否顯示懸浮框 * @return _mFloatIsShow; */ public function get mFloatIsShow():Boolean { return _mFloatIsShow; } /** * @private */ public function set mFloatIsShow(value:Boolean):void { _mFloatIsShow = value; } /** * X軸單位 * @return _mXUnit; */ public function get mXUnit():String { return _mXUnit; } /** * @private */ public function set mXUnit(value:String):void { _mXUnit = value; } /** * 左邊Y軸單位 * @return _mYUnit; */ public function get mYUnit():String { return _mYUnit; } /** * @private */ public function set mYUnit(value:String):void { _mYUnit = value; } /** * 等分值,把X軸設成多少等分,默認為10 */ public function get xBisectNum():int { return _xBisectNum; } /** * @private */ public function set xBisectNum(value:int):void { _xBisectNum = value; } /** * 圖標名稱 */ public function get mChartName():String { return _mChartName; } /** * @private */ public function set mChartName(value:String):void { _mChartName = value; } /** * 顏色數組 */ public function get mHBarColorArray():Array { return _mHBarColorArray; } /** * @private */ public function set mHBarColorArray(value:Array):void { _mHBarColorArray = value; } //=========================== // 屬性定義的setter和getter方法end //=========================== } }
附上另一作品,有想法的話歡迎交流,qq:719810496