一般情况下需要组件重写都是由于以下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自定义组件开发的更多相关文章

  1. Flex自定义组件开发之日周月日期选择日历控件

    原文:Flex自定义组件开发之日周月日期选择日历控件         使用过DateField的我们都知道,DateField 控件是用于显示日期的文本字段,字段右侧带有日历图标.当用户在控件边框内的 ...

  2. Flex自定义组件开发 - jackyWHJ

    一般情况下需要组件重写都是由于以下2个原因: 1.在FLEX已有组件无法满足业务需求,或是需要更改其可视化外观等特性时,直接进行继承扩展. 2.为了模块化设计或进一步重用,需要对FLEX组件进行组合. ...

  3. Flex自定义组件、皮肤,并调用

    标签:Flex  自定义组件  自定义皮肤  主应用调用模块 本程序样例学习自flex 实战教程.但因原教程代码不全,且根据个人需求有更改. 1文件列表 自定义as类Reveal.as,该类实现组件的 ...

  4. flex 自定义组件的编写

    使用flex也很久了,也改过别人写的flex自定义组件,但是就是没有系统的研究下flex组件的编写步骤,和要注意的东西,在这里我参照一本书中的例子,好好的理解下,也为了巩固下自己对flex的理解! 1 ...

  5. 在Vue前端项目中,附件展示的自定义组件开发

    在Vue前端界面中,自定义组件很重要,也很方便,我们一般是把一些通用的界面模块进行拆分,创建自己的自定义组件,这样操作可以大大降低页面的代码量,以及提高功能模块的开发效率,本篇随笔继续介绍在Vue&a ...

  6. 自定义组件开发:使用v-model封装el-pagination组件

    1.前言 通过封装el-pagination组件开发自定义分页组件的类似文章网上已经有很多了,但看了一圈,总是不如意,于是决定还是自己动手搞一个. 2.背景 2.1.常规分页处理方法 利用el-pag ...

  7. jquery自定义组件开发

    jquery的组件已经有很多,但是有可能找不到符合我们需求的组件,所以我们可以动手自己封装一个jquery组件. 第一步要知道封装jquery组件的基本语法 (function ($) { $.fn. ...

  8. 使用vue的extend自定义组件开发

    index.js import Vue from 'vue' import tip from './tip.vue' const Constructor = Vue.extend(tip); cons ...

  9. 微信小程序自定义组件实现

    官方从 1.6.3 开始对于自定义组件这一块有了比较大的变动,首先比较明显的感觉就是文档比以前全多了,有木有!,现在小程序支持简洁的组件化编程,可以将页面内的功能模块抽象成自定义组件,以便在不同的页面 ...

随机推荐

  1. Intent之对象传递(Parcelable传递对象和对象集合)

    接着上一篇文章,以下我们讨论一下怎样利用Parcelable实现Intent之间对象的传递 一.实现对象传递 首先创建User.java实现Parcelable接口: package org.yayu ...

  2. 強大的Selector框架

    代码地址如下:http://www.demodashi.com/demo/12648.html 前言 在开发的过程中,我们经常会遇到给view设置背景,什么圆形背景啊,圆角背景啊,点击变色背景啊之类的 ...

  3. shell脚本检测网络是否畅通

    shell初始化安装脚本执行时,需从网络上安装一些rpm包,所有需要先检测网络的畅通性, 代码 #检测网络链接&&ftp上传数据 function networkAndFtp() { ...

  4. 02-1设置第一启动项--电脑怎么进入BIOS的方法集合

    电脑怎么进入BIOS的方法集合 很多时候为了对电脑进行相关设置,我们必须进入电脑的bios界面,但是不同的电脑进入bios的方法各不相同,小编今天就在这儿将各种电脑进入bios的方法汇总一下,希望对你 ...

  5. wcf 入门示例

    刚开始学习wcf,根据官方网站的说明写下的代码 第一步: 建立一个类库项目GettingStartedLib,首先添加wcf引用System.ServiceModel; 添加接口ICalculator ...

  6. 用unity3d实现简单的主server连接

    用unity3d实现简单的主server连接 參考自鹰大的网络实例 -------------------------------------------------华丽的切割线----------- ...

  7. An error occurred while searching for implementations of method

    1:在我安装完scala的插件后,在打开方法的实现类(open implementactions)的时候,抛出这个异常,后来发现这个异常是因为我的scala的插件跟我eclipse版本不兼容导致的. ...

  8. .Net中多线程类的使用和总结

    lock, Monitor, Thread, Join, BackGroundWorker.   消费者和生产者.Async 委托Invoke TypeHandle中BlockIndex. http: ...

  9. object.Equals与object.ReferenceEquals方法

    object.Equals方法表达的是语义判等,不一定是引用判等. object.ReferenceEquals方法是肯定是引用判等. 怎么实现一个对象的值语义的 Equals方法?实验. MyCla ...

  10. Linq系列(7)——表达式树之ExpressionVisitor

    大家好,由于今天项目升级,大家都在获最新代码,所以我又有时间在这里写点东西,跟大家分享. 在上一篇的文章中我介绍了一个dll,使大家在debug的时候可以可视化的看到ExpressionTree的Bo ...