如上图示,在linechart中添加红色Y线,拖动该线的过程中,经过数据点时,会实时更新datagrid中对应的X、Y值数据。
实现要点:
1.linechart添加Y线
继承mx.charts.chartClasses.ChartElement,自定义Y线。
package
{
import flash.display.Graphics;
import flash.geom.Point;
import flash.text.TextField;
import mx.charts.chartClasses.CartesianChart;
import mx.charts.chartClasses.CartesianTransform;
import mx.charts.chartClasses.ChartElement;
import mx.charts.chartClasses.ChartState;
import mx.charts.chartClasses.IAxis;
public class DashedLines extends ChartElement
{
public function DashedLines()
{
super();
}
private var _yValue:Number = NaN;
private var _xValue:Date = null;
public function get xValue():Date
{
return _xValue;
}
public function set xValue(value:Date):void
{
_xValue = value;
invalidateDisplayList();
}
public function get yValue():Number
{
return _yValue;
}
public function set yValue(value:Number):void
{
_yValue = value;
invalidateDisplayList();
}
/**
* 实线部分的长度
* @default 10
*/
public var length:Number = 10;
/**
* 空白部分的长度
* @default 5
*/
public var gap:Number = 0;
/**
* 线条的宽度
* @default 1
*/
public var lineThickness:Number = 3;
/**
* 线条的颜色
* @default 黑色
*/
public var lineColor:uint = 0;
private var _displayName:String;
/**
* 该线所对应的数值名称(平均值,最大值等等)
* @default
*/
public function get displayName():String
{
return _displayName;
}
/**
* @private
*/
public function set displayName(value:String):void
{
_displayName = value;
invalidateDisplayList();
}
protected var label:TextField;
override protected function createChildren():void
{
super.createChildren();
if(!label)
{
label = new TextField();
label.mouseEnabled = false;
addChild(label);
}
}
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
{
super.updateDisplayList(unscaledWidth, unscaledHeight);
if (!chart||
chart.chartState == ChartState.PREPARING_TO_HIDE_DATA ||
chart.chartState == ChartState.HIDING_DATA)
{
return;
}
var g:Graphics = this.graphics;
g.clear();
// 如果没有设置数据,不显示
if(xValue == null)
{
return;
}
var w:Number = unscaledWidth;
var h:Number = unscaledHeight;
var vAxis:IAxis = CartesianChart(this.chart).verticalAxis;
var x:Number = dataToLocal(xValue,0).x;
var pFrom:Point = new Point(x,0);
var pTo:Point = new Point(x,h);
GraphicUtils.drawDashed(g, pFrom, pTo, this.length, this.gap, this.lineThickness, this.lineColor);
label.text = (displayName ? (displayName + " : ") : "") + xValue;
label.x = x > 21 ? x - 21 : x + 1;
label.y = 1;
}
// 这个方法复制自LineSeries
override public function dataToLocal(... dataValues):Point
{
var data:Object = {};
var da:Array /* of Object */ = [ data ];
var n:int = dataValues.length;
if (n > 0)
{
data["d0"] = dataValues[0];
dataTransform.getAxis(CartesianTransform.HORIZONTAL_AXIS).
mapCache(da, "d0", "v0");
}
if (n > 1)
{
data["d1"] = dataValues[1];
dataTransform.getAxis(CartesianTransform.VERTICAL_AXIS).
mapCache(da, "d1", "v1");
}
dataTransform.transformCache(da,"v0","s0","v1","s1");
return new Point(data.s0 + this.x,
data.s1 + this.y);
}
}
}
该类中主要用到dataTransform,他提供了数据点与坐标空间的对应转换,主类中通过xValue将Y线处的x值传入,通过该方法转换为Y线需要绘制的坐标位置。
该类用到工具类GraphicUtils
package
{
import flash.display.Graphics;
import flash.geom.Point;
/**
* 一些绘图相关的方法
* @author lip
*/
public class GraphicUtils
{
public function GraphicUtils()
{
}
/**
* 画虚线
* @param graphics 你懂的
* @param pFrom 起点
* @param pTo 终点
* @param length 实线段的长度
* @param gap 实线段的间距
* @param thickness 线的宽度
* @param color 线的颜色
*/
public static function drawDashed(graphics:Graphics, pFrom:Point, pTo:Point, length:Number = 5, gap:Number = 5, thickness:Number = 1, color:uint = 0):void
{
var max:Number = Point.distance(pFrom, pTo);
var l:Number = 0;
var p3:Point;
var p4:Point;
graphics.lineStyle(thickness, color);
while (l < max)
{
p3 = Point.interpolate(pTo, pFrom, l / max);
l += length;
if (l > max)
l = max;
p4 = Point.interpolate(pTo, pFrom, l / max);
graphics.moveTo(p3.x, p3.y)
graphics.lineTo(p4.x, p4.y)
l += gap;
}
}
}
}
请参考博文:datagrid样式
3、组件主类
<?xml version="1.0" encoding="utf-8"?>
<s:BorderContainer xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:customcomponent="com.bjrz.view.centerview.customcomponent.*"
xmlns:amcharts="http://www.amcharts.com/com_internal"
width="100%" height="100%"
creationComplete="generateChartData()" xmlns:local="*">
<fx:Script>
<![CDATA[
import mx.charts.ChartItem;
import mx.charts.chartClasses.IAxis;
import mx.charts.chartClasses.Series;
import mx.charts.series.items.LineSeriesItem;
import mx.collections.ArrayCollection;
import mx.controls.Alert;
import mx.formatters.DateFormatter;
[Bindable]
private var attributes:ArrayCollection = new ArrayCollection(
[{lineColor:"0xFF6600", dataItem:"label",time:"北京", currentValue:"label",unit:"北京", show:"true",describe:"北京"},
{lineColor:"0x00ff00", dataItem:"label",time:"北京", currentValue:"label",unit:"北京", show:"true",describe:"北京"}]);
private var dateFormat:DateFormatter = new DateFormatter();
private function generateChartData():void
{
dateFormat.formatString = "HH:NN:SS";
}
[Bindable]
private var expensesAC:ArrayCollection = new ArrayCollection( [
{ Month: "Tue May 20 16:23:50 GMT+0800 2014", Profit: 2000, Expenses: 1500, Amount: 450 },
{ Month: "Tue May 20 16:23:51 GMT+0800 2014", Profit: 1000, Expenses: 200, Amount: 600 },
{ Month: "Tue May 20 16:23:52 GMT+0800 2014", Profit: 1500, Expenses: 500, Amount: 300 },
{ Month: "Tue May 20 16:23:53 GMT+0800 2014", Profit: 1800, Expenses: 1200, Amount: 900 },
{ Month: "Tue May 20 16:23:54 GMT+0800 2014", Profit: 2400, Expenses: 575, Amount: 500 },
{ Month: "Tue May 20 16:23:55 GMT+0800 2014", Profit: 1000, Expenses: 200, Amount: 600 },
{ Month: "Tue May 20 16:23:56 GMT+0800 2014", Profit: 1500, Expenses: 500, Amount: 300 },
{ Month: "Tue May 20 16:23:57 GMT+0800 2014", Profit: 1800, Expenses: 1200, Amount: 900 },
{ Month: "Tue May 20 16:23:58 GMT+0800 2014", Profit: 2400, Expenses: 575, Amount: 500 },
{ Month: "Tue May 20 16:23:59 GMT+0800 2014", Profit: 1000, Expenses: 200, Amount: 600 },
{ Month: "Tue May 20 16:24:00 GMT+0800 2014", Profit: 1500, Expenses: 500, Amount: 300 },
{ Month: "Tue May 20 16:24:01 GMT+0800 2014", Profit: 1800, Expenses: 1200, Amount: 900 },
{ Month: "Tue May 20 16:24:02 GMT+0800 2014", Profit: 2400, Expenses: 575, Amount: 500 },
{ Month: "Tue May 20 16:24:03 GMT+0800 2014", Profit: 1000, Expenses: 200, Amount: 600 },
{ Month: "Tue May 20 16:24:04 GMT+0800 2014", Profit: 1500, Expenses: 500, Amount: 300 },
{ Month: "Tue May 20 16:24:05 GMT+0800 2014", Profit: 1800, Expenses: 1200, Amount: 900 },
{ Month: "Tue May 20 16:24:06 GMT+0800 2014", Profit: 2400, Expenses: 575, Amount: 500 },
{ Month: "Tue May 20 16:24:07 GMT+0800 2014", Profit: 1000, Expenses: 200, Amount: 600 },
{ Month: "Tue May 20 16:24:08 GMT+0800 2014", Profit: 1500, Expenses: 500, Amount: 300 },
{ Month: "Tue May 20 16:24:09 GMT+0800 2014", Profit: 1800, Expenses: 1200, Amount: 900 },
{ Month: "Tue May 20 16:24:10 GMT+0800 2014", Profit: 2400, Expenses: 575, Amount: 500 },
{ Month: "Tue May 20 16:24:11 GMT+0800 2014", Profit: 1000, Expenses: 200, Amount: 600 },
{ Month: "Tue May 20 16:24:12 GMT+0800 2014", Profit: 1500, Expenses: 500, Amount: 300 },
{ Month: "Tue May 20 16:24:13 GMT+0800 2014", Profit: 1800, Expenses: 1200, Amount: 900 },
{ Month: "Tue May 20 16:24:14 GMT+0800 2014", Profit: 2400, Expenses: 575, Amount: 500 },
{ Month: "Tue May 20 16:24:15 GMT+0800 2014", Profit: 1000, Expenses: 200, Amount: 600 },
{ Month: "Tue May 20 16:24:16 GMT+0800 2014", Profit: 1500, Expenses: 500, Amount: 300 },
{ Month: "Tue May 20 16:24:17 GMT+0800 2014", Profit: 1800, Expenses: 1200, Amount: 900 },
{ Month: "Tue May 20 16:24:18 GMT+0800 2014", Profit: 2400, Expenses: 575, Amount: 500 },
{ Month: "Tue May 20 16:24:19 GMT+0800 2014", Profit: 1000, Expenses: 200, Amount: 600 }]);
[Bindable]
private var expensesAC2:ArrayCollection = new ArrayCollection( [
{ Month: "Tue May 20 16:23:50 GMT+0800 2014", Profit: 2100, Expenses: 1500, Amount: 460 },
{ Month: "Tue May 20 16:23:51 GMT+0800 2014", Profit: 1100, Expenses: 200, Amount: 660 },
{ Month: "Tue May 20 16:23:52 GMT+0800 2014", Profit: 1100, Expenses: 500, Amount: 340 },
{ Month: "Tue May 20 16:23:53 GMT+0800 2014", Profit: 1100, Expenses: 1200, Amount: 960 },
{ Month: "Tue May 20 16:23:54 GMT+0800 2014", Profit: 2100, Expenses: 575, Amount: 500 },
{ Month: "Tue May 20 16:23:55 GMT+0800 2014", Profit: 1100, Expenses: 200, Amount: 630 },
{ Month: "Tue May 20 16:23:56 GMT+0800 2014", Profit: 1100, Expenses: 500, Amount: 360 },
{ Month: "Tue May 20 16:23:57 GMT+0800 2014", Profit: 1100, Expenses: 1200, Amount: 930 },
{ Month: "Tue May 20 16:23:58 GMT+0800 2014", Profit: 2400, Expenses: 575, Amount: 500 },
{ Month: "Tue May 20 16:23:59 GMT+0800 2014", Profit: 1100, Expenses: 200, Amount: 600 },
{ Month: "Tue May 20 16:24:00 GMT+0800 2014", Profit: 1500, Expenses: 500, Amount: 320 },
{ Month: "Tue May 20 16:24:01 GMT+0800 2014", Profit: 1800, Expenses: 1200, Amount: 300 },
{ Month: "Tue May 20 16:24:02 GMT+0800 2014", Profit: 2410, Expenses: 575, Amount: 500 },
{ Month: "Tue May 20 16:24:03 GMT+0800 2014", Profit: 1010, Expenses: 200, Amount: 650 },
{ Month: "Tue May 20 16:24:04 GMT+0800 2014", Profit: 1500, Expenses: 500, Amount: 360 },
{ Month: "Tue May 20 16:24:05 GMT+0800 2014", Profit: 1100, Expenses: 1200, Amount: 900 },
{ Month: "Tue May 20 16:24:06 GMT+0800 2014", Profit: 2100, Expenses: 575, Amount: 500 },
{ Month: "Tue May 20 16:24:07 GMT+0800 2014", Profit: 1000, Expenses: 200, Amount: 600 },
{ Month: "Tue May 20 16:24:08 GMT+0800 2014", Profit: 1500, Expenses: 500, Amount: 360 },
{ Month: "Tue May 20 16:24:09 GMT+0800 2014", Profit: 1800, Expenses: 1200, Amount: 900 },
{ Month: "Tue May 20 16:24:10 GMT+0800 2014", Profit: 2400, Expenses: 575, Amount: 570 },
{ Month: "Tue May 20 16:24:11 GMT+0800 2014", Profit: 1100, Expenses: 200, Amount: 600 },
{ Month: "Tue May 20 16:24:12 GMT+0800 2014", Profit: 1100, Expenses: 500, Amount: 300 },
{ Month: "Tue May 20 16:24:13 GMT+0800 2014", Profit: 1100, Expenses: 1200, Amount: 940 },
{ Month: "Tue May 20 16:24:14 GMT+0800 2014", Profit: 2400, Expenses: 575, Amount: 540 },
{ Month: "Tue May 20 16:24:15 GMT+0800 2014", Profit: 1000, Expenses: 200, Amount: 600 },
{ Month: "Tue May 20 16:24:16 GMT+0800 2014", Profit: 1500, Expenses: 500, Amount: 340 },
{ Month: "Tue May 20 16:24:17 GMT+0800 2014", Profit: 1100, Expenses: 1200, Amount: 940 },
{ Month: "Tue May 20 16:24:18 GMT+0800 2014", Profit: 2400, Expenses: 575, Amount: 500 },
{ Month: "Tue May 20 16:24:19 GMT+0800 2014", Profit: 1100, Expenses: 200, Amount: 640 }]);
protected function dashedlines1_mouseDownHandler(event:MouseEvent):void
{
this.addEventListener(MouseEvent.MOUSE_MOVE,dashedLine_mouseMoveHandler);
this.addEventListener(MouseEvent.MOUSE_UP,dashedlines1_mouseUpHandler);
}
protected function dashedlines1_mouseUpHandler(event:MouseEvent):void
{
this.removeEventListener(MouseEvent.MOUSE_MOVE,dashedLine_mouseMoveHandler);
this.removeEventListener(MouseEvent.MOUSE_UP,dashedlines1_mouseUpHandler);
}
[Bindable]
private var linePosition:Number = 2;
protected function dashedLine_mouseMoveHandler(event:MouseEvent):void
{
dashedLine.xValue = new Date(Number((((linechart.mouseX - 50)*((hAxis.maximum as Date).time-(hAxis.minimum as Date).time)/(linechart.width-50)
+(hAxis.minimum as Date).time)/1000).toFixed(0))*1000);
var allSeries:Array = linechart.series;
//由于只有一条线条,所以数组长度为1,这里得到线条的series
var series:Series = allSeries[0] as Series;
for(var i:int = 0;i<expensesAC.length;i++){
var item:ChartItem = series.items[i];
//图表中一个item唯一对应的就是数据源里的一个对象
var LCI:LineSeriesItem= item as LineSeriesItem;
if(dashedLine.xValue.toString() == LCI.xValue.toString()){
//符合要求就加到series的dataTipItems集合里面,以便最后显示
historyDatagrid.dataProvider.getItemAt(0).currentValue = LCI.yValue;
historyDatagrid.dataProvider.getItemAt(0).time = LCI.xValue;
}
}
var series2:Series = allSeries[1] as Series;
for(var j:int = 0;j<expensesAC.length;j++){
var item2:ChartItem = series2.items[j];
//图表中一个item唯一对应的就是数据源里的一个对象
var LCI2:LineSeriesItem= item2 as LineSeriesItem;
if(dashedLine.xValue.toString() == LCI2.xValue.toString()){
//符合要求就加到series的dataTipItems集合里面,以便最后显示
historyDatagrid.dataProvider.getItemAt(1).currentValue = LCI2.yValue;
historyDatagrid.dataProvider.getItemAt(1).time = LCI2.xValue;
}
}
historyDatagrid.dataProvider.itemUpdated(null,"currentValue");
historyDatagrid.dataProvider.itemUpdated(null,"time");
}
protected function button1_clickHandler(event:MouseEvent):void
{
/* dashedLine.xValue =2.5; */
dashedLine.xValue =new Date(2014,4,20,16,23,51);
}
/**
*
* 转换x轴坐标的显示 ,按HH:NN:SS 格式显示
* @return
* @param labelValue 时间
* @author
* @date
*/
private function mylabel(labelValue:Object, previousValue:Object, d:IAxis):String
{
dateFormat.formatString = "HH:NN:SS";
var str:String = dateFormat.format(labelValue);
return str;
}
]]>
</fx:Script>
<fx:Declarations>
<mx:SolidColorStroke id = "s1" color="0xFF6600" weight="2"/>
<mx:SolidColorStroke id = "s2" color="0x00ff00" weight="2"/>
</fx:Declarations>
<s:VGroup width="100%" height="100%">
<s:VGroup width="100%" height="60%" paddingBottom="10" paddingTop="10" paddingLeft="10" paddingRight="10">
<mx:LineChart id="linechart" height="100%" width="100%"
paddingLeft="5" paddingRight="5"
showDataTips="true">
<mx:backgroundElements>
<mx:GridLines gridDirection="both"/>
<local:DashedLines id="dashedLine"
mouseDown="dashedlines1_mouseDownHandler(event)" mouseUp="dashedlines1_mouseUpHandler(event)"
lineColor="0xFF0000" xValue="{new Date(2014,4,20,16,23,52)}"/>
</mx:backgroundElements>
<mx:verticalAxisRenderers>
<mx:AxisRenderer axis="{vAxis}" placement="left" width="50" showLine="true" tickPlacement="none" showLabels="true"/>
</mx:verticalAxisRenderers>
<mx:horizontalAxisRenderers>
<mx:AxisRenderer axis="{hAxis}" placement="bottom" showLine="true" tickPlacement="none" showLabels="true"/>
</mx:horizontalAxisRenderers>
<mx:verticalAxis>
<mx:LinearAxis id="vAxis" baseAtZero="false" maximum="2400" minimum="0" interval="200" />
</mx:verticalAxis>
<mx:horizontalAxis>
<!--<mx:LinearAxis id="hAxis" baseAtZero="false" maximum="4" minimum="0" interval="1" />-->
<local:RzDateTimeAxis id="hAxis" displayLocalTime="true" autoAdjust="false" dataUnits="seconds" minimum="{new Date(2014,4,20,16,23,50)}" maximum="{new Date(2014,4,20,16,24,19)}" labelFunction="mylabel" />
</mx:horizontalAxis>
<mx:series>
<mx:LineSeries xField="Month" yField="Profit" form="curve" displayName="Profit" lineStroke="{s1}" horizontalAxis="{hAxis}" verticalAxis="{vAxis}" dataProvider="{expensesAC}"/>
<mx:LineSeries xField="Month" yField="Profit" form="curve" displayName="Profit" lineStroke="{s2}" horizontalAxis="{hAxis}" verticalAxis="{vAxis}" dataProvider="{expensesAC2}"/>
</mx:series>
</mx:LineChart>
</s:VGroup>
<customcomponent:RZHistoryDatagrid id="historyDatagrid" width="100%" height="40%" dataProvider="{attributes}"/>
</s:VGroup>
</s:BorderContainer>因为linechart只提供了数据点转坐标空间,没有找到坐标空间转数据点的方法。所以需要将鼠标位置mouseX通过计算转化为数据点x,然后作为xValue传入。
同时,根据数据点x获取对应的y值。这是出现一个问题:
mouseX是连续的,而数据点非连续。
因为我的需求中linechart数据点十分密集,极近于连续点。所以我采用的方法是,将在计算转化的时候,取近似值。只将时间为整秒的数据做转化传入。这样并不影响最终使用效果。如果有更好的处理方式,望留言告知。
Linechart + Datagrid 互动展示数据 (Linechart自定义数据点选择线),布布扣,bubuko.com
Linechart + Datagrid 互动展示数据 (Linechart自定义数据点选择线)
原文:http://blog.csdn.net/zhongyuan_1990/article/details/26448329