Flex自定義組件開發


一般情況下需要組件重寫都是由於以下2個原因:
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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM