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:

package
{
    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:

<?xml version="1.0" encoding="utf-8"?>
<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.

Share and Enjoy:
  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Mixx
  • Google Bookmarks
  • Slashdot
  • StumbleUpon
  • Technorati
  • TwitThis



9 Comments »