September 13th, 2007 by Kyle
Tags: ActionScript, charting, data, extrapolation, Flex, flex-charting, graphing, graphs, linerenderer, linesegmentrenderer, mxml
Posted in: ActionScript, Flex
What should the LineChart do in this situation?
How can you draw a line with only 1 data point much less extrapolate the data??
Turns out that the Flex chart does not handle that very well. The solution?? Write your own lineSegmentRenderer.
Here is an example of a lineSegementRenderer that will just draw a circle for this case.
MyLineRenderer.as:
{
import mx.charts.renderers.LineRenderer;
import mx.charts.series.items.LineSeriesItem;
import mx.charts.series.renderData.LineSeriesRenderData;
import mx.charts.series.items.LineSeriesSegment;
import mx.charts.renderers.CircleItemRenderer;
import flash.display.Graphics;
import flash.geom.Rectangle;
import mx.charts.ChartItem;
import mx.charts.chartClasses.GraphicsUtilities;
import mx.core.IDataRenderer;
import mx.graphics.IFill;
import mx.graphics.IStroke;
public class MyLineRenderer extends LineRenderer
{
private static var rcFill:Rectangle = new Rectangle();
public function MyLineRenderer()
{
super();
}
override protected function updateDisplayList(unscaledWidth:Number,
unscaledHeight:Number):void
{
var l:int=(data as LineSeriesSegment).items.length;
if(l==1){
var item:LineSeriesItem=((data as LineSeriesSegment).items[0] as LineSeriesItem)
var fill:IFill = GraphicsUtilities.fillFromStyle(getStyle("fill"));
var stroke:IStroke = getStyle("stroke");
var w:Number = stroke ? stroke.weight / 2 : 0;
rcFill.right = 2;
rcFill.bottom = 2;
var g:Graphics = graphics;
g.clear();
if (stroke)
stroke.apply(g);
if (fill)
fill.begin(g, rcFill);
g.drawCircle(item.x, item.y, 3);
if (fill)
fill.end(g);
}
else{
super.updateDisplayList(unscaledWidth, unscaledHeight);
}
}
}
}
Here is the app that demonstrates using the renderer.
main.mxml:
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" xmlns="*">
<mx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
[Bindable]
private var DP1:ArrayCollection = new ArrayCollection( [
{ Month: "Jan", Profit: 2000},
{ Month: "Feb", Profit: 1000},
{ Month: "Mar", Profit: 1500, Expenses: 500},
{ Month: "Apr", Profit: 1800},
{ Month: "May", Profit: 2400, Expenses: 700 } ]);
[Bindable]
private var DP2:ArrayCollection = new ArrayCollection( [
{ Month: "Jan", Profit: 2000},
{ Month: "Feb", Profit: 1000, Expenses: 500},
{ Month: "Mar", Profit: 1500},
{ Month: "Apr", Profit: 1800},
{ Month: "May", Profit: 2400 } ]);
private function changeDP(e:Event):void {
if(e.target.selected)
linechart2.dataProvider = DP2;
else
linechart2.dataProvider = DP1;
}
]]>
</mx:Script>
<mx:Panel width="80%" height="80%">
<mx:LineChart id="linechart2" height="100%" width="100%"
showDataTips="true" dataProvider="{DP1}" >
<mx:horizontalAxis>
<mx:CategoryAxis categoryField="Month"/>
</mx:horizontalAxis>
<mx:series>
<mx:LineSeries yField="Profit" form="curve" displayName="Profitp" interpolateValues="true" lineSegmentRenderer="MyLineRenderer"/>
<mx:LineSeries yField="Expenses" form="curve" displayName="Expenses" interpolateValues="true" lineSegmentRenderer="MyLineRenderer"/>
</mx:series>
</mx:LineChart>
<mx:ControlBar>
<mx:Button label="changed dp" click="changeDP(event)" toggle="true" />
</mx:ControlBar>
</mx:Panel>
</mx:Application>
The app starts out with a dataProvider that has several points of data for 2 lineSeries. Click the button in the app and the dataProvider changes to one that has a lineSeries with only 1 point of data. See what happens? The 1 data point is now rep[resented by a small circle. SO much nicer, don’t you think?
Here is a link to a Flex Builder 2.0.1 project (compiled with SDK hotfix2) containing a sample demonstrating the solution described above.











April 11th, 2008 at 7:28 am
Great tips, thanks!
May 5th, 2008 at 3:11 pm
Didn’t work for me on Flex 3.
But it was a great hint for me, turns out you just have to set the itemrenderer property to diamondrenderer for example.
May 20th, 2008 at 3:18 am
excellant hint for me
August 27th, 2008 at 2:07 pm
Thanks!
And yes, it works on flex 3.