Skinning ChartItem objects

A ChartItem object represents a data point in a series. There is one ChartItem instance for each item in the series’ data provider. ChartItem objects contain details about the data for the data point as well as the renderer (or skin) to use when rendering that data point in the series. The ChartItem renderers define objects such as the icon that represents a data point in a PlotChart control or the box that makes up a bar in a BarChart control.

Each series has a default renderer that Flex uses to draw that series’ ChartItem objects. You can specify a new renderer to use with the series’ itemRenderer style property. This property points to a class that defines the appearance of the ChartItem object.

The following table lists the available renderer classes for the ChartItem objects of each chart type:

The appearance of most renderers is self-explanatory. The BoxItemRenderer class draws ChartItem objects in the shape of boxes. The DiamondItemRenderer class draws ChartItem objects in the shape of diamonds. The ShadowBoxItemRenderer and ShadowLineRenderer classes add shadows to the ChartItem objects that they draw.

You can use existing classes to change the default renderers of chart items. The DiamondItemRendererclass is the default renderer for ChartItem objects in a data series in a PlotChart control. The following example uses the default DiamondItemRenderer class for the first data series. The second series uses the CircleItemRendererclass, which draws a circle to represent the data points in that series. The third series uses the CrossItemRenderer class, which draws a cross shape to represent the data points in that series.

<?xml version="1.0"?>
<!-- charts/PlotRenderers.mxml -->
<s:Application 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    creationComplete="srv.send()"
    height="600">

    <fx:Declarations>
        <!-- View source of the following page to see the structure of the data that Flex uses in this example. -->
        <mx:HTTPService id="srv" url="http://aspexamples.adobe.com/chart_examples/expenses-xml.aspx"/>
        <!-- To see data in an HTML table, go to http://aspexamples.adobe.com/chart_examples/expenses.aspx -->  
    </fx:Declarations>

    <s:layout>
        <s:VerticalLayout/>
    </s:layout>

  <s:Panel title="Plot Chart">
     <s:layout>
         <s:VerticalLayout/>
     </s:layout>
     <mx:PlotChart id="myChart" 
        dataProvider="{srv.lastResult.data.result}"
        showDataTips="true">
        <mx:series>
           <!-- First series uses default renderer. -->
           <mx:PlotSeries 
                xField="expenses" 
                yField="profit"
                displayName="Plot 1"/>

           <!-- Second series uses CircleItemRenderer. -->
           <mx:PlotSeries 
                xField="amount" 
                yField="expenses"
                displayName="Plot 2" 
                itemRenderer="mx.charts.renderers.CircleItemRenderer"/>

           <!-- Third series uses CrossItemRenderer. -->
           <mx:PlotSeries 
                xField="profit" 
                yField="amount" 
                displayName="Plot 3" 
                itemRenderer="mx.charts.renderers.CrossItemRenderer"/>
        </mx:series>
     </mx:PlotChart>
     <mx:Legend dataProvider="{myChart}"/>
  </s:Panel>
</s:Application>

The executing SWF file for the previous example is shown below:

To apply a renderer to a series in ActionScript, you use the setStyle() method. In that method, you create a new ClassFactory and pass the renderer to its constructor. Flex generates an instance of this class to be the renderer. Be sure to import the appropriate classes when using renderer classes.

The following example sets the renderer for the second series to the CircleItemRenderer and the renderer for the third series to the CrossItemRenderer in ActionScript.

<?xml version="1.0"?>
<!-- charts/PlotRenderersAS.mxml -->
<s:Application 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    creationComplete="srv.send();initSeriesStyles();"
    height="600">

    <fx:Declarations>
        <!-- View source of the following page to see the structure of the data that Flex uses in this example. -->
        <mx:HTTPService id="srv" url="http://aspexamples.adobe.com/chart_examples/expenses-xml.aspx"/>
        <!-- To see data in an HTML table, go to http://aspexamples.adobe.com/chart_examples/expenses.aspx -->  
    </fx:Declarations>

  <fx:Script><![CDATA[
     import mx.charts.renderers.*;

    private function initSeriesStyles():void {
        // Second series uses CircleItemRenderer.
        series2.setStyle("itemRenderer", new ClassFactory(mx.charts.renderers.CircleItemRenderer));

        // Third series uses CrossItemRenderer.
        series3.setStyle("itemRenderer", new ClassFactory(mx.charts.renderers.CrossItemRenderer));
    }

  ]]></fx:Script>

    <s:layout>
        <s:VerticalLayout/>
    </s:layout>

  <s:Panel title="Plot Chart">
     <s:layout>
         <s:VerticalLayout/>
     </s:layout>
     <mx:PlotChart id="myChart" 
        dataProvider="{srv.lastResult.data.result}"
        showDataTips="true">
        <mx:series>
           <mx:PlotSeries 
                id="series1"
                xField="expenses" 
                yField="profit"
                displayName="Plot 1"/>

           <mx:PlotSeries 
                id="series2"
                xField="amount" 
                yField="expenses"
                displayName="Plot 2"/>

           <mx:PlotSeries 
                id="series3"
                xField="profit" 
                yField="amount" 
                displayName="Plot 3"/>
        </mx:series>
     </mx:PlotChart>
     <mx:Legend dataProvider="{myChart}"/>
  </s:Panel>
</s:Application>

The executing SWF file for the previous example is shown below:

Using multiple renderer classes

You can sometimes choose from more than one renderer for a chart series, depending on the series. These renderers let you change the appearance of your charts by adding shadows or graphics to the chart items.

Some series types require multiple renderers to completely render their data. For example, a LineSeries object has both an itemRenderer style property and a lineSegmentRenderer style property. The itemRenderer property specifies the renderer for the data items. The lineSegmentRenderer specifies the appearance of the line segments between items.

The other series type that requires two renderers is the AreaSeries. The areaRenderer property specifies the appearance of the area, and the itemRenderer specifies the appearance of the data items.

You can also specify the renderer to use for legends. The default is the class that the series’ itemRenderer property specifies. For more information, see Formatting Legend controls.

You can use multiple types of data series in a single chart. For example, you can use a ColumnSeries and a LineSeries to show something like a moving average over a stock price. In this case, you can use all the renderers supported by those series in the same chart. For more information on using multiple series, see Using multiple data series.

Creating custom renderers

You can replace the itemRenderer property of a chart series with a custom renderer. You define the renderer on the itemRenderer style property for the chart series. This renderer can be a graphical renderer or a class that programmatically defines the renderer.

Creating graphical renderers

You can use a graphic file such as a GIF or JPEG to be used as a renderer on the chart series. You do this by setting the value of the itemRenderer style property to be an embedded image. This method of graphically rendering chart items is similar to the graphical skimming method used for other components, as described in Creating graphical skins for MX components.

The following example uses the graphic file to represent data points on a PlotChart control:

<?xml version="1.0"?>
<!-- charts/CustomPlotRenderer.mxml -->
<s:Application 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    creationComplete="srv.send()"
    height="600">

    <fx:Declarations>
         <!-- View source of the following page to see the structure of the data that Flex uses in this example. -->
         <mx:HTTPService id="srv" url="http://aspexamples.adobe.com/chart_examples/expenses-xml.aspx"/>
         <!-- To see data in an HTML table, go to http://aspexamples.adobe.com/chart_examples/expenses.aspx -->  
    </fx:Declarations>

    <s:layout>
        <s:VerticalLayout/>
    </s:layout>

  <s:Panel title="Plot Chart">
        <s:layout>
            <s:VerticalLayout/>
        </s:layout>
     <mx:PlotChart id="myChart"
        dataProvider="{srv.lastResult.data.result}"
        showDataTips="true">
        <mx:series>
           <!-- First series uses embedded image for renderer. -->
           <mx:PlotSeries 
                xField="expenses" 
                yField="profit"
                displayName="Plot 1" 
                itemRenderer="@Embed(source='../assets/butterfly.gif')" 
                radius="20"
                legendMarkerRenderer="@Embed(source='../assets/butterfly.gif')"/>

           <!-- Second series uses CircleItemRenderer. -->
           <mx:PlotSeries 
                xField="amount" 
                yField="expenses"
                displayName="Plot 2" 
                itemRenderer="mx.charts.renderers.CircleItemRenderer"/>

           <!-- Third series uses CrossItemRenderer. -->
           <mx:PlotSeries 
                xField="profit" 
                yField="amount" 
                displayName="Plot 3" 
                itemRenderer="mx.charts.renderers.CrossItemRenderer"/>
        </mx:series>
     </mx:PlotChart>
     <mx:Legend dataProvider="{myChart}"/>
  </s:Panel>
</s:Application>

The executing SWF file for the previous example is shown below:

This example uses the butterfly.gif graphic to represent each data point on the plot chart. It controls the size of the embedded image by using the radius style property.

You are not required to set the value of the itemRenderer property inline. You can also embed a graphic file in ActionScript as a Class, pass it to the ClassFactory class’s constructor, and then reference it inline, as the following example shows:

<?xml version="1.0"?>
<!-- charts/CustomPlotRendererAS.mxml -->
<s:Application 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    creationComplete="srv.send()"
    height="600">

    <fx:Declarations>
         <!-- View source of the following page to see the structure of the data that Flex uses in this example. -->
         <mx:HTTPService id="srv" url="http://aspexamples.adobe.com/chart_examples/expenses-xml.aspx"/>
         <!-- To see data in an HTML table, go to http://aspexamples.adobe.com/chart_examples/expenses.aspx -->  
    </fx:Declarations>

  <fx:Script><![CDATA[
     import mx.core.BitmapAsset;
     
     [Bindable]
     [Embed(source="../assets/butterfly.gif")]
     public var myButterfly:Class;

     [Bindable]
     public var myButterflyFactory:ClassFactory = new ClassFactory(myButterfly);

  ]]></fx:Script>

    <s:layout>
        <s:VerticalLayout/>
    </s:layout>

  <s:Panel title="Plot Chart">
        <s:layout>
            <s:VerticalLayout/>
        </s:layout>
     <mx:PlotChart id="myChart" 
        dataProvider="{srv.lastResult.data.result}"
        showDataTips="true">
        <mx:series>

           <!-- First series uses custom class renderer. -->
           <mx:PlotSeries 
                id="series1"
                xField="expenses" 
                yField="profit"
                displayName="Plot 1" 
                itemRenderer="{myButterflyFactory}"
                legendMarkerRenderer="{myButterflyFactory}"
                radius="20"/>

           <!-- Second series uses CircleItemRenderer. -->
           <mx:PlotSeries 
                id="series2"
                xField="amount" 
                yField="expenses"
                displayName="Plot 2" 
                itemRenderer="mx.charts.renderers.CircleItemRenderer"/>

           <!-- Third series uses CrossItemRenderer. -->
           <mx:PlotSeries 
                id="series3"
                xField="profit" 
                yField="amount" 
                displayName="Plot 3" 
                itemRenderer="mx.charts.renderers.CrossItemRenderer"/>
        </mx:series>
     </mx:PlotChart>
     <mx:Legend dataProvider="{myChart}"/>
  </s:Panel>
</s:Application>

The executing SWF file for the previous example is shown below:

You can also use the setStyle() method to apply the custom class to the item renderer. The following example sets the itemRenderer and legendMarkerRenderer style properties to the embedded image:

<?xml version="1.0"?>
<!-- charts/CustomPlotRendererStyles.mxml -->
<s:Application 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    creationComplete="srv.send();setStylesInit();"
    height="600">

    <fx:Declarations>
         <!-- View source of the following page to see the structure of the data that Flex uses in this example. -->
         <mx:HTTPService id="srv" url="http://aspexamples.adobe.com/chart_examples/expenses-xml.aspx"/>
         <!-- To see data in an HTML table, go to http://aspexamples.adobe.com/chart_examples/expenses.aspx -->  
    </fx:Declarations>

  <fx:Script><![CDATA[
     import mx.core.BitmapAsset;
     
     [Bindable]
     [Embed(source="../assets/butterfly.gif")]
     public var myButterfly:Class;

    private function setStylesInit():void {
        series1.setStyle("itemRenderer", new ClassFactory(myButterfly));
        series1.setStyle("legendMarkerRenderer", new ClassFactory(myButterfly));
    }
  ]]></fx:Script>  

    <s:layout>
        <s:VerticalLayout/>
    </s:layout>

  <s:Panel title="Plot Chart">
        <s:layout>
            <s:VerticalLayout/>
        </s:layout>
     <mx:PlotChart id="myChart" 
        dataProvider="{srv.lastResult.data.result}"
        showDataTips="true">
        <mx:series>

           <!-- First series uses custom class renderer. -->
           <mx:PlotSeries 
                id="series1"
                xField="expenses" 
                yField="profit"
                displayName="Plot 1" 
                radius="20"/>

           <!-- Second series uses CircleItemRenderer. -->
           <mx:PlotSeries 
                id="series2"
                xField="amount" 
                yField="expenses"
                displayName="Plot 2" 
                itemRenderer="mx.charts.renderers.CircleItemRenderer"/>

           <!-- Third series uses CrossItemRenderer. -->
           <mx:PlotSeries 
                id="series3"
                xField="profit" 
                yField="amount" 
                displayName="Plot 3" 
                itemRenderer="mx.charts.renderers.CrossItemRenderer"/>
        </mx:series>
     </mx:PlotChart>
     <mx:Legend dataProvider="{myChart}"/>
  </s:Panel>
</s:Application>

The executing SWF file for the previous example is shown below:

Creating programmatic renderers

Creating a custom renderer class for your chart items can give you more control than creating simple graphical renderers. Using class-based renderers is very similar to using programmatic skins, as described in Creating programmatic skins for MX components.

One approach to is to extend the ProgrammaticSkin class and implement the IDataRenderer interface. In this approach, you can provide all of the logic for drawing chart items in your custom class, and maintain the greatest control over its appearance. For example, you use methods in the Graphics class to draw and fill the rectangles of the bars in a BarChart control.

When you implement the IDataRenderer interface, you must define a setter and getter method to implement the data property. This data property is of the type of the series item. In the case of a ColumnSeries, it is a ColumnSeriesItem. Other item types include BarSeriesItem, BubbleSeriesItem, LineSeriesItem, and PlotSeriesItem.

In your class, you override the updateDisplayList() method with the logic for drawing the chart item as well as setting any custom properties. You should also call the super.updateDisplayList() method.

The following example renders the chart items and uses an Array of colors to color each column in the ColumnChart control differently:

// charts/CycleColorRenderer.as

package { // Empty package.
  
  import mx.charts.series.items.ColumnSeriesItem;
  import mx.skins.ProgrammaticSkin;
  import mx.core.IDataRenderer;
  import flash.display.Graphics;
  
  public class CycleColorRenderer extends mx.skins.ProgrammaticSkin 
     implements IDataRenderer {
     
     private var colors:Array = [0xCCCC99,0x999933,0x999966];
     private var _chartItem:ColumnSeriesItem;
     
     public function CycleColorRenderer() {
        // Empty constructor.
     }
     
     public function get data():Object {
        return _chartItem;
     }

     public function set data(value:Object):void {
        _chartItem = value as ColumnSeriesItem; 
        invalidateDisplayList();
     }

     override protected function
        updateDisplayList(unscaledWidth:Number,unscaledHeight:Number):void {
           super.updateDisplayList(unscaledWidth, unscaledHeight);
           var g:Graphics = graphics;
           g.clear();  
           g.beginFill(colors[(_chartItem == null)? 0:_chartItem.index]);
           g.drawRect(0, 0, unscaledWidth, unscaledHeight);
           g.endFill();
     }
  } // Close class.
} // Close package.

In your Flex application, you use this class as the renderer by using the itemRenderer property of the ColumnSeries, as the following example shows:

<?xml version="1.0"?>
<!-- charts/ProgrammaticRenderer.mxml -->
<s:Application 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    creationComplete="srv.send()"
    height="600">

    <fx:Declarations>
        <!-- View source of the following page to see the structure of the data that Flex uses in this example. -->
        <mx:HTTPService id="srv" url="http://aspexamples.adobe.com/chart_examples/expenses-xml.aspx"/>
        <!-- To see data in an HTML table, go to http://aspexamples.adobe.com/chart_examples/expenses.aspx -->  
    </fx:Declarations>
  
    <s:layout>
        <s:VerticalLayout/>
    </s:layout>

  <s:Panel title="ColumnChart control with a programmatic ItemRenderer">
     <s:layout>
         <s:VerticalLayout/>
     </s:layout>
     <mx:ColumnChart id="myChart" 
        dataProvider="{srv.lastResult.data.result}" 
        showDataTips="true">
        <mx:horizontalAxis>
           <mx:CategoryAxis categoryField="month"/>
        </mx:horizontalAxis>
        <mx:series>
           <fx:Array>
            <mx:ColumnSeries
                xField="month"
                yField="expenses"
                displayName="Expenses"
                itemRenderer="CycleColorRenderer"/>
           </fx:Array>
        </mx:series>
     </mx:ColumnChart>
  </s:Panel>
</s:Application>

The executing SWF file for the previous example is shown below:

For more information on overriding the updateDisplayList() method, see Implementing the updateDisplayList() method.