A List itemRenderer using States and Transitions

Here’s a little demo of how to expand a list item in place. This demo uses CheckBoxes for the itemRenderer, but when you click a box, the list item grows larger revealing more data.

The itemRenderer is actually a Canvas with a CheckBox for a child. The itemRenderer uses states. The initial state contains this CheckBox. The “expandedState” contains a List. A transition is used to hide and show the list by using a Resize effect.

The List must have variableRowHeight set to true, otherwise this won’t work.

Compatable with Flex 2 Beta 2 Public release. Bear in mind that Flex 2 is in beta and changes will occur before the final release.

Main Application

Let’s start with the main application which I call “ExpandoList.mxml”:

<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns="*" layout="absolute">
<mx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
// This collection shows just the title in the list as a CheckBox. When
// one is checked, it expands to review the models array.
[Bindable]
public var dp:ArrayCollection = new ArrayCollection(
[ {title:"Ford", models:["Fusion","Taurus","Mustang"]},
{title:"Volkswagen", models:["Passat","Jetta","Beetle", "Golf", "GTI"]},
{title:"Infiniti",models:["FX35","GX35","Q45","M35"]},
{title:"Audi",models:["A3","A4","A6"]}
]);
]]>
</mx:Script>

<!-- Simple list with 2 important points:
1. variableRowHeight = "true" allows for rows to be of differing height
2. itemRenderer = "DetailItem" specifies the DetailItem.mxml as the renderer
-->
<mx:List dataProvider="{dp}"  width="270" height="315"
horizontalCenter="0"
verticalCenter="0"
columnCount="1"
columnWidth="250"
variableRowHeight="true"
itemRenderer="DetailItem"
selectionColor="0xFFFFFF"/>
</mx:Application>

The Script block just defines the ArrayCollection. Assume this data is the result of a data services call.

The List is defined with this ArrayCollection as its dataProvider and variableRowHeight is set to true. The itemRenderer is set to the class name “DetailItem”.

The itemRenderer

The DetailItem component is shown next. I’ve broken the code into parts for readability. You can combine them in the order given to get working code.

<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml" xmlns="*" width="201" height="36">

<!-- This is the base state and it just defines the simple checkbox
-->
<mx:CheckBox x="10" y="10" label="{data.title}" width="181"
click="changeState(event)"/>

The component extends Canvas. This is a good choice because Canvas does not impose any restrictions on the layout of its children. When you make an itemRenderer, consider how much effort you want to put into positioning the items. For example, if you want all of the components in the renderer on one line, use an HBox. Since this component is going to change size, Canvas is a perfect choice.

A single alternate state (from the default base state) is defined, “expandedState”. You can see that there are two elements. SetProperty changes this component’s (Canvas) height. The second adds a List with the ac member as its dataProvider.

<mx:states>
<mx:State name="expandedState">
<mx:SetProperty name="height" value="123"/>
<mx:AddChild position="lastChild">
<mx:List left="10" y="36" right="10" dataProvider="{ac}" height="77"></mx:List>
</mx:AddChild>
</mx:State>
</mx:states>

The Script block defines the ArrayCollection and an event handler for the CheckBox (see above). When the box is selected (checked), the currentState is changed to “expandedState”, triggering the items in the states above.

<mx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
[Bindable]
public var ac:ArrayCollection;

private function changeState(event:flash.events.Event) : void
{
if( event.target.selected )
currentState = "expandedState";
else
currentState = "";
}

// the setting of the data property is overridden to create the
// ArrayCollection from the Array of models listed in the dataProvider
// this this List.
override public function set data(value:Object) : void
{
super.data = value;
ac = new ArrayCollection(value.models);
}
]]>
</mx:Script>

The transition defines how the state changes take place. Transitions are optional and apply effects to the change. You can really see what this transition does by first running this example WITHOUT the transition. All this transition does is provide a better visual experience.

When you run this example without the transition, clicking the CheckBox just makes the canvas bigger with the List of models.

Now run the example using this transition. When you click the CheckBox the list now opens smoothly, revealing the models. Click the CheckBox again and the item glides back up.

The fromState and toState are both *, wildcards, meaning that this transition applies when going from any state to any other state. The transition is just a Resize effect on the entire component (Canvas). What’s interesting about this is that Flex will figure out what the before and after sizes are for the Resize effect without needing to specify that in the Resize tag.

<mx:transitions>
<mx:Transition fromState="*" toState="*">
<mx:Resize target="{this}" />
</mx:Transition>
</mx:transitions>

Finally, end the component with the matching Canvas tag.

</mx:Canvas>

Quick Summary

You can use states and transitions pretty much anywhere, such as in an itemRenderer. Transitions make the user experience better and often do not take much coding. States and transitions can become quite complex. I suggest that you start off with something simple, such as this example, to get familiar with them. Perhaps add another state to this example and use a different transition.