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

 

你可能感兴趣的:(Flex)