Customizing the layout of legend items

This question came up in a recent forum posting:

You can make the LegendItems in a Legend display horizontally in a long line or vertically in a long list, but how do I customize the layout of these LegendItems?

The answer took me a little while to figure out, so I’ll post it here so others can benefit from it… Yes, the answer is also posted as a response in the forums, but I can put running examples here so you can see what I mean.

First, an app that shows the problem. The ColumnChart control in the following app has 7 separate series, each with a separate entry in the legend. If you lay out the LegendItems vertically, you get a lot of extra white space to the right of the legend, below the chart, as the following example shows:

If you lay the LegendItems out horizontally, you get scroll bars at the bottom of the application, as the following example shows:

There’s no way (that I could figure out) to eliminate those scroll bars without clipping the LegendItems.

One solution, which can be adapted to any number of layout techniques, is to build the list of LegendItems in ActionScript and add them to a dynamic grid layout. I did this in a single loop that takes into account the preferred number of items per row:

for (var j:int = 0; j < numRows; j++) {
var gr:GridRow = new GridRow();
myGrid.addChild(gr);
for (var k:int = 0; k < rowSize; k++) {
if (z < myChart.series.length) {
var gi:GridItem = new GridItem();
gr.addChild(gi);
var li:LegendItem = new LegendItem();
// Apply the current series' displayName to the LegendItem's label.
...
// Get the current series' fill.
...
// Apply the current series' fill to the corresponding LegendItem.
...
// Add the LegendItem to the GridItem.
gi.addChild(li);
// Increment any time a LegendItem is added.
z++;
}
}

Before you do any of this, you have to remove all the existing GridRows and GridItems. You do this with a call to the removeAllChildren() method, like this:

myGrid.removeAllChildren();

There are a couple of gotchas in this approach: you have to be sure to grab the right labels so that your dynamic LegendItems match the series’ display names that show up in the chart. You do this by accessing the chart’s series array’s displayName property, as the following example shows:

li.label = myChart.series[z].displayName;

You also have to get the color (actually, it’s the “fill” property) from the series, and apply it to the corresponding LegendItem’s fill, as the following example shows:

var sc:SolidColor = myChart.series[z].getStyle("fill");
li.setStyle("fill", sc);

Here’s a running example that lays out the LegendItems in the selected number of rows. I also took the opportunity to apply some styles to make the LegendItems a little more attractive. They are of uniform width and their background colors are the same as the marker color, as the following example shows:

li.setStyle("textIndent", 5);
li.setStyle("labelPlacement", "left");
li.setStyle("fontSize", 9);
gi.setStyle("backgroundAlpha", "1");
gi.setStyle("backgroundColor", sc.color);
gi.width = 80;

Here’s the running example:

Here’s the source code:
Download ZIP (9K)