标签:linechart + datagrid 自定义数据点选择线
如上图示,在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自定义数据点选择线)
标签:linechart + datagrid 自定义数据点选择线
原文地址:http://blog.csdn.net/zhongyuan_1990/article/details/26448329