Flex 4: Understanding DataGroups and VirtualLayouts

On August 2, 2010, in Flex, by Anuj Gakhar

As I have recently started working with Flex4, I have started to use some of the new components from the Flex 4 SDK. One of them is the DataGroup class. A brief explanation of the class is below (from the docs) :-

The DataGroup class is the base container class for data items. The DataGroup class converts data items to visual elements for display. While this container can hold visual elements, it is often used only to hold data items as children. The DataGroup class takes as children data items or visual elements that implement the IVisualElement interface and are DisplayObjects. Data items can be simple data items such String and Number objects, and more complicated data items such as Object and XMLNode objects. While these containers can hold visual elements, they are often used only to hold data items as children.

DataGroups allow you to do something called Virtualization. What that means is, if you have am ArrayCollection with lets say 50 items in it and your DataGroup is only showing 10 items at a time, then Flash Player will only render 10 visual elements on the screen and as you keep scrolling up or down, those renderers will be recycled within each other. In other words, there will only ever be a few visual renderers created based on the size of your visible component.

Lets take an example to get a better understanding of this concept :-

First, we create a simple page with a DataGroup on it an give it some data to play with.

[as3]
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
initialize="init()"
xmlns:local="*">
<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;

[Bindable]
public var ds:ArrayCollection = new ArrayCollection();

private function init():void
{
for (var i:int = 0;i < 15; i++)
{
var obj:Object = {label:’Label’ + i, data:i, selected:false};
ds.addItem( obj );
}
}
]]>
</fx:Script>

<s:layout>
<s:BasicLayout />
</s:layout>

<s:Scroller top="50" left="50">
<local:DataGroupTest
width="300"
height="150"
dataProvider="{ds}" />
</s:Scroller>
</s:Application>
[/as3]

All we are doing here is, creating an ArrayCollection and passing it on to the DataGroupTest.mxml. DataGroupTest is basically a DataGroup with a Custom ItemRenderer and a Vertical Layout so that items can be placed below each other.
Here is how the DataGroupTest looks like :-

[as3]
<?xml version="1.0" encoding="utf-8"?>
<s:DataGroup xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
itemRenderer="renderers.CheckBoxItemRenderer"
height="150">
<s:layout>
<s:VerticalLayout/>
</s:layout>

</s:DataGroup>
[/as3]

You can see I have given it an ItemRenderer (CheckBoxItemRenderer). This simply displays a Checkbox for all the items in the ArrayCollection. Here is how the ItemRenderer looks like :-

[as3]
<?xml version="1.0" encoding="utf-8"?>
<s:CheckBox xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
implements="mx.core.IDataRenderer">
<fx:Script>
<![CDATA[
import mx.core.IDataRenderer;

private var _data:Object;

[Bindable("dataChanged")]
public function get data():Object
{
return _data;
}

public function set data(value:Object):void
{
if( value != null)
{
_data = value;
label = _data.label;
selected = value.selected;
addEventListener(MouseEvent.CLICK, checkBoxclickHandler);
trace( label + ‘ created’ );
dispatchEvent( new Event("dataChanged") );
}
}

private function checkBoxclickHandler(event:MouseEvent):void
{
_data.selected = !_data.selected;
}

]]>
</fx:Script>
</s:CheckBox>

[/as3]

All pretty simple till now. I have added trace() statements in the ItemRenderer so we can track how many items are being created by the Flash Player. When I run this code, I expect to see some trace messages in my Console window. Here is how the application looks like when I run this code :-

And here is how the Console window looks like :-

That is actually showing me all the Labels being created at once. Which is not what I was expecting. Visually, I can only see about 6 Checkboxes but internally, Flash Player has created all 15 of them. Thats because we haven’t yet told the DataGroup to use the Virtual layout. Lets go back to our DataGroup and change it to look like this.

[as3]
<s:DataGroup xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
itemRenderer="renderers.CheckBoxItemRenderer"
height="150">
<s:layout>
<s:VerticalLayout useVirtualLayout="true"/>
</s:layout>

</s:DataGroup>
[/as3]

Notice the useVirtualLayout=”true”. This is where the magic happens.

Now, if you debug the code again and look at your console window, this is what you get.

This makes much more sense and exactly what we wanted to see. So this is pretty cool. Now, the Flash Player will recycle these ItemRenderers to display the items (in this case, Checkboxes) as we scroll up and down. It wont create any new ItemRenderers for this dataset. This is a much better performance specially if you are working with large datasets.

Here is some more info on Virtual Layouts from the documentation :-

In most cases a virtualized DataGroup should behave exactly like one where ItemRenderers are created eagerly. There are a few exceptions:

  • A virtualized DataGroup will “recycle” item renderers when that’s possible (more below).
  • Virtualized layouts do not respect layout elements’ major axis percent size property. That’s percentHeight for VerticalLayout and percentWidth for HorizontalLayout.
  • In the current implementation, virtualized layouts do not respect layout element’s includeInLayout property and do not support null ILayoutElements.
  • Virtual layouts with small numbers of items whose sizes vary dramatically will respond poorly to interactive thumb scrolling. Responsiveness will improve as the variation in size decreases and/or the length of the list increases.

Components like DataGrid and List have the VirtualLayout enabled by default. So if you are using a DataGrid or a List , then you are already making use of this feature. However, a Repeater does not have this functionality which is why it makes much more sense to use a DataGroup with Virtual Layout instead of a Repeater.

Tagged with:  

10 Responses to Flex 4: Understanding DataGroups and VirtualLayouts

  1. Dmitry says:

    Got article. Short, concise explanations. Gives the whole idea w/o reading tons of Adobe html docs. Thanks, man.

  2. Anuj Gakhar says:

    Glad you liked it, Dmitry 🙂 Thanks for dropping by.

  3. JabbyPanda says:

    One important correction to your sample.

    If we use checkbox UI control in recycled item Renderers, we should
    not forget to explicitly set its “selected” property within “data”
    setter,

    e.g.

    public function set data(value:Object):void
    {

    this.selected = _data.selected;
    }

    otherwise checkbox will display erroneous selected state.

  4. Anuj Gakhar says:

    @JabbyPanda, Thanks for pointing that out. I have updated the code with required changes. Didn’t really bother with this before as this was not the main point of this post.

  5. hsTed says:

    good example … I learned a lot from this.

  6. Sitebase says:

    Do you know how to detect changes in the dataprovider of the datagroup? Collection change event doesn’t seem to work for me.

  7. Anuj Gakhar says:

    If you have your dataprovider bound to a arraycollection then you shouldn’t have to worry about detecting changes to it…

  8. Santhosh says:

    Very Good example. I understand DataGroups and virtual layout.

  9. Madhav Manchi says:

    Good Article, Very concise and more informative

  10. smanikandan says:

    very nice example ..super

Leave a Reply to Anuj Gakhar Cancel reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

© 2011 Anuj Gakhar
%d bloggers like this: