首页 > 其他 > 详细

Linechart + Datagrid 互动展示数据 (Linechart自定义数据点选择线)

时间:2014-05-21 13:49:58      阅读:431      评论:0      收藏:0      [点我收藏+]

bubuko.com,布布扣

如上图示,在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;
			}
		}
	}
}

2、datagrid对应样式的定义

   请参考博文: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>

该类中,主要方法是针对Y线的拖拽处理。基本拖拽处理不再赘述。在mouseMove的处理中,需要对xValue执行重新赋值,以便刷新绘制Y线。

因为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

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!