One of the coolest and most useful features I use in Flex are item renderers. They allow us to customize how lists and datagrids display information. This tutorial is going to show how to use three different types of item renderers, drop-in, inline, and custom components. I actually used item renderers in an earlier tutorial on using Advanced Datagrid Topics to get song information. But this tutorial is going to go a bit more in depth on the topic.
Below is what we are going to build today. A simple datagrid which we display information about a couple songs and whether they are a favorite of ours. This example uses all three types of item renderers. It uses a drop-in for the checkbox, an inline for the picture and name, and finally a more complex custom item renderer for the rating. This only touches on the possibilities of what can be done though.
Starting with the user interface as usual we can setup a basic structure for our application. The initial setup is a simple application with a panel and a single datagrid component. The datagrid has three columns but only one has a specified datafield, the last one. Also at this point I added some data by manually creating an ArrayCollection inside the dataProvider property of the datagrid to just create some dummy song information. Each item inside the Array is a general object with a few properties set. So the start of our working code looks like:
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"
width="530" height="405">
<mx:Panel x="0" y="0" width="530" height="405" layout="absolute"
title="Item Renderers">
<mx:DataGrid x="0" y="0" width="510" height="365">
<mx:columns>
<mx:DataGridColumn headerText="Song"/>
<mx:DataGridColumn headerText="Rating" width="125"/>
<mx:DataGridColumn headerText="Favorite" dataField="isFavorite" width="75"/>
</mx:columns>
<mx:dataProvider>
<mx:ArrayCollection>
<mx:Array>
<mx:Object title="Stairway to Heaven" artist="Led Zepplin"
art="/files/led_zep.jpg" rating="4.5" isFavorite="true" />
<mx:Object title="How to Save a Life" artist="Fray"
art="/files/fray.jpg" rating="4" isFavorite="false" />
<mx:Object title="Sinnerman" artist="Nina Simone"
art="/files/nina_sim.jpg" rating="5" isFavorite="true" />
<mx:Object title="Take On Me" artist="Aha"
art="/files/aha.jpg" rating="3.5" isFavorite="false" />
</mx:Array>
</mx:ArrayCollection>
</mx:dataProvider>
</mx:DataGrid>
</mx:Panel>
</mx:Application>
Drop-in Item Renderer
The first item renderer I am going to show is the drop-in. This is the simplest in form and execution, but is the most limited in functionality. We use this type for the check box to show if the song is a favorite of ours. The drop-in item renderers will take the data corresponding to the datafield and use it to define a property of the item renderer. Most of the time these are properties that defined the display of the component being used for the item renderer. Also there are only a certain number of components that can be used in this way, for a full list of these and the properties that can be used check out this livedocs page.
To get one of the drop-in renderers working all we need to do is simply set a property on the DataGridColumn named itemRenderer
. In the third column we are going to set this property equal to mx.controls.CheckBox
, which is the name of the component we want to use for the item renderer. Now we have a nice pretty checkbox instead of "true" or "false". But if you remember I mentioned they were limited in functionality, well this is very true because beyond what we have done we can't really do much else. This includes not being able to disable the checkbox, change the alignment, or anything else. The following code is our new DataGridColumn for the favorite property.
itemRenderer="mx.controls.CheckBox"/>
Inline Item Renderer
The next item renderer I am going to jump into is the inline item renderer, which gives us a whole lot more functionality than the drop-in and isn't much harder to implement. These are written as small components added directly into the itemRenderer
property of the DataGridColumn. These let us use the current row data to display the information however we would like using all the components of Flex. We can also create reusable inline item renderers, but we will not be building one in this tutorial because they are so similar to normal inline. The code below is the new first DataGridColumn - I will explain all this in just a moment.
<mx:itemRenderer>
<mx:Component>
<mx:VBox verticalGap="2">
<mx:Image source="{data.art}" height="50" width="50" />
<mx:Label text="{data.artist + ' ~ ' + data.title}" />
</mx:VBox>
</mx:Component>
</mx:itemRenderer>
</mx:DataGridColumn>
For this column we are going to define the item renderer explicitly and then create a component for the rendering inline. You always start by defining it as an <mx:Component>
and then adding the actually rendering code to this. I added a VBox to align my other components and narrowed the vertical gap for aesthetics. Inside of the VBox I have added two components, the first of which is an image. We get the source for this image from data
which is how we refer to the current data row and we can simply grab properties from it. The second component is a Label in which we display the artist name and song title concatenated together. There you have it, your first simple inline item renderer.
Now I mentioned reusable inline renderers. This is done very similar to the technique we used above. The only main difference is where we put our component code - to make it reusable we will move it up to directly below the opening application tag and we need to make sure we give it a unique id (which would be used later to refer to it). That will take care of making it reusable and then to use it just set the itemRenderer
property equal to the id of the renderer component.
Custom Component Renderers
These item renderers are the most complicated to implement but are also the most useful. Hopefully everyone has had some small experience with creating custom components, the minimal amount of experience is needed. To create my rating item renderer I actually created two components, one for the star and one which puts all the stars together (actual item renderer). I will run through the star component first. This component, named "RatingStar.mxml", is fairly simple - it is based off of a Canvas and holds an Image component. The component has three states, one for a no star rating, one for a half star rating, and one for a full star rating. Each state sets the source to the appropriate embedded image. If you don't understand the concept of state for a component, don't worry - I'm planning for a tutorial on this later on. So a new file named "RatingStar.mxml" contains the following code. Note - the embedded images are just some I created.
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml" width="20" height="20"
currentState="noStar">
<mx:Script>
<![CDATA[
[Embed("assets/nostar.png")]
private var noStar:Class;
[Embed("assets/halfstar.png")]
private var halfStar:Class;
[Embed("assets/fullstar.png")]
private var fullStar:Class;
]]>
</mx:Script>
<mx:Image id="imgStar" height="20" width="20" />
<mx:states>
<mx:State name="noStar">
<mx:SetProperty target="{imgStar}" name="source" value="{noStar}" />
</mx:State>
<mx:State name="halfStar">
<mx:SetProperty target="{imgStar}" name="source" value="{halfStar}" />
</mx:State>
<mx:State name="fullStar">
<mx:SetProperty target="{imgStar}" name="source" value="{fullStar}" />
</mx:State>
</mx:states>
</mx:Canvas>
On to the interesting part, creating the renderer component. This is a custom component created in a file named "RatingRenderer.mxml". I based this component off of the HBox component. From here we could take two different routes to defining the component - we could have just built the component using mxml and used the data
property to access the current row data just like the inline item renderer. But I choose to go another route, most container components have the data
property and you can override the set function of this property. So basically when the component's data is set - when the datagrid is passing the current row data to the component - we can then use this data to modify the look of the component. This is all done in Actionscript. Here is the code for that:
<mx:HBox xmlns:mx="http://www.adobe.com/2006/mxml" horizontalGap="2"
width="100%" height="100%" horizontalAlign="center" verticalAlign="middle">
<mx:Script>
<![CDATA[
private var _data:Object;
override public function set data(value:Object):void
{
_data = value;
var rating:Number = Number(value.rating);
removeAllChildren();
var ratingStar:RatingStar;
for(var i:uint = 0; i < 5; i++)
{
ratingStar = new RatingStar();
if(rating > 1)
{
ratingStar.currentState = "fullStar";
rating -= 1;
}
else if(rating > .5)
{
ratingStar.currentState = "halfStar";
rating -= .5;
}
else
ratingStar.currentState = "noStar";
addChild(ratingStar);
}
}
override public function get data():Object
{
return _data;
}
]]>
</mx:Script>
</mx:HBox>
Looking at the basic structure we have our HBox which has a couple properties set, nothing out of the ordinary. Then we have some actionscript and this where the magic happens: we override two public functions set data(value:Object)
and get data()
and setup a private var. Inside the set data(value:Object)
function is where most of the work happens. We start by setting our private var to the value passed in by the datagrid and then figure out the rating by getting it from the data passed in and converting it to a number. The next line is very important, we start by removing all the children currently added to the HBox so we start with a clean slate. Following this we loop through 0 to 4 (max rating - 1) and create stars to add to our HBox. For each star we set the state based on the rating of the song. Lastly we add the star to the HBox (again very important - as nothing will show up without it). And there we have it a fully customized item renderer.
Wait, how do we use this complex thing now? Well that is the simplest part - all we need to do is set the itemRenderer
property of our DataGridColumn to the name of the component to do the item rendering. So in this case, we set it equal to RatingRenderer
. The code for our new DataGridColumn is below.
Well that pretty much does it. If you want more info on the custom component item renderers you can check out the livedocs here. I hope this tutorial gives you some insight into the working of item renderers. Only thing left is to show you the entire source of the main mxml file:
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"
width="530" height="405">
<mx:Panel x="0" y="0" width="530" height="405" layout="absolute"
title="Item Renderers">
<mx:DataGrid x="0" y="0" width="510" height="365">
<mx:columns>
<mx:DataGridColumn headerText="Song">
<mx:itemRenderer>
<mx:Component>
<mx:VBox verticalGap="2" paddingLeft="5">
<mx:Image source="{data.art}" height="50" width="50" />
<mx:Label text="{data.artist + ' ~ ' + data.title}" />
</mx:VBox>
</mx:Component>
</mx:itemRenderer>
</mx:DataGridColumn>
<mx:DataGridColumn headerText="Rating" width="125"
itemRenderer="RatingRenderer"/>
<mx:DataGridColumn headerText="Favorite" dataField="isFavorite"
width="75" itemRenderer="mx.controls.CheckBox"/>
</mx:columns>
<mx:dataProvider>
<mx:ArrayCollection>
<mx:Array>
<mx:Object title="Stairway to Heaven" artist="Led Zepplin"
art="/files/led_zep.jpg" rating="4.5" isFavorite="true" />
<mx:Object title="How to Save a Life" artist="Fray"
art="/files/fray.jpg" rating="4" isFavorite="false" />
<mx:Object title="Sinnerman" artist="Nina Simone"
art="/files/nina_sim.jpg" rating="5" isFavorite="true" />
<mx:Object title="Take On Me" artist="Aha"
art="/files/aha.jpg" rating="3.5" isFavorite="false" />
</mx:Array>
</mx:ArrayCollection>
</mx:dataProvider>
</mx:DataGrid>
</mx:Panel>
</mx:Application>
If you have any questions or comments drop a line below. Also to make things a little easier here is a .zip file with all the source code for this tutorial.
Edit (12/10/2007):
There have been a couple questions on how to center the checkbox in this example/tutorial. So I have modified the example above to have the checkbox centered and the View Source option is available if you right click the example.
But basically all that needs to be done is switch:
itemRenderer="mx.controls.CheckBox"/>
To the following:
<mx:itemRenderer>
<mx:Component>
<mx:Box width="100%" height="100%" horizontalAlign="center"
verticalAlign="middle">
<mx:CheckBox selected="{data.isFavorite}" />
</mx:Box>
</mx:Component>
</mx:itemRenderer>
</mx:DataGridColumn>