Give us a call 203-379-0773
Check out our Angular Book Series.

Building a Mobile ItemRenderer in Flex

Whenever I talk to people about renderers, and building renderers for Flex, I get stuck on what exactly to talk about. A renderer , is just a component. Building a renderer is no different than building any other Flex component. Yet, still people seem to get confused.

I think part of the confusion comes from the fact that people aren't used to using the Flex Component LifeCycle to build components. They do a lot in MXML and don't become intimate with what is going on under the hood. MXML masks a lot of details.

When building renderers for mobile, Adobe recommends that we build in ActionScript. I think the difference between building components in ActionScript and MXML is what gives people the "deer in headlight" look. It's become like a second hat at Flextras, because all our components are all ActionScript. This post will give you the steps to create a mobile optimized renderer. We're going to extend the LabelItemRendererclass and add a radio button to it.

Create the Class!

I outlined the steps to create a custom renderer in my previous blog post on the topic, but to reiterate:

  • In createChildren() create your renderer's children.
  • In measure() use your children to determine the measuredWidth and measuredHeight of the component
  • In layoutContents(), position and size your children.
  • Add an event listener for the dataChange event. In that event handler, make the changes needed to the component, such as updating the labelDisplay.

For the purposes, of this post, I'm going to take a step back even further. Start by creating the class:

package com.flextras.renderers.SimpleRadioButtonRenderer{
 import spark.components.LabelItemRenderer;
    
 public class SimpleRadioButtonRenderer extends LabelItemRenderer{
  public function SimpleRadioButtonRenderer(){
   super();
  }
 }
}

Since this class extends the LabelItemRenderer it already has a label to display text. But, we'll need a variable for the RadioButton:

protected var radioButton : RadioButton;

The class is created, now let's look at the createChildren() method.

Create the RadioButton

The createChildren() method is a Flex Component LifeCycle method that runs during the component's creation. It is used to create the component's children. In this case, we'll use it to create the new radio button:

override protected function createChildren():void{
 super.createChildren();
 radioButton = new RadioButton();
 addChild(radioButton);
}
Yes, it is really that simple. Since we're calling super, we don't have to worry about creating the labelDisplay component, as the parent handles that.

Position and Size the Children

The next step in our component creation is to position and size the children. For the purposes of this demo, the code will just place the label first, and the radio button all the way to the right. The radio button will size to fill up all the space it needs; and the label will take up the remaining space. All this code will be implemented in the layoutContents() method. Here is a blank method signature:

override protected function layoutContents(unscaledWidth:Number, unscaledHeight:Number):void{
}

First, let's calculate the size of the radio Button. We want to size the radio button to give it as much space as it needs, so we can use the getPreferredHeight() and getPreferredWidth() methods. These are helper methods implemented in the LabelItemRenderer.

var radioButtonWidth : Number = getElementPreferredWidth(radioButton);
var radioButtonHeight : Number = getElementPreferredHeight(radioButton);

Of course, now that we have the size, we want to set the size. To do that, we use the setElementSize() method, another helper method implemented in LabelItemRenderer. The concept is borrowed from the MobileSkin.

setElementSize(radioButton, radioButtonWidth, radioButtonHeight);

Next, we calculate the size of the label. For the width, we can just use the unscaledWidth minus the space for the radio button:

var labelWidth:Number = unscaledWidth-radioButtonWidth;

For the label's height, we can make use of the getElementPreferredHeight() method:

var labelHeight:Number = getElementPreferredHeight(labelDisplay);;

Finally, set the actual size:

setElementSize(labelDisplay, labelWidth, labelHeight);

The last piece of this is to position the two elements in relation to each other. The label is easy we can just position that at 0,0:

setElementPosition(labelDisplay,0,0);

The X position of the radio button is a bit more difficult. It is not a known value, and we have to calculate it based on the label. Since we want to position he radio button to the right of the label, we want to use the label's width to offset the x position of the radio button. Here is the code:

setElementPosition(radioButton, labelDisplay.x + labelDisplay.width ,0);

Although it wouldn't matter in this example, I like to use the label's X position in the calculation. If we change the X position of the label the radioButton will automatically reposition itself. It's a trick I used a lot when creating MXML Skins for a recent consulting client.

Things can get a lot more complicated, especially if you want to accommodate for different styles for padding and gaps in the layout, and alignment of items. Look at the layoutContents method in LabelItemRenderer for a sample of this.

Set the Selected State of the RadioButton

There is one last thing we'll need to do for this renderer. We need to update the selected state of the radio button based on the selection. Renderers are reused as you scroll through a list; and the elements of the renderer always need to be aware of the data they display. To do this, we'll listen to the dataChange event. I'll add the listener in the constructor:

public function SimpleRadioButtonRenderer(){
 super();
 this.addEventListener(FlexEvent.DATA_CHANGE, onDataChange);
}

The LabelITemRenderer class has a selected property, which automagically keeps track of whether or not the renderer is selected or not. We can use that property to keep the selected state of the radio button in sync:

protected function onDataChange(event:FlexEvent):void{
 this.radioButton.selected = this.selected
}

Conclusions

I hope this post gives you a better understanding of how to build ActionScript renderers. It's not hard. Just create your new children in createChildren(), size and position them in layoutContents(), and use a dataChange event listener to change your children based on the actual data. Here is the code, and the sample:

For a production quality sample, I'd definitely devote some more time to tweaking the layout, and accommodating for padding styles. But, I hope this helps you understand the concepts.

Comments (Comment Moderation is enabled. Your comment will not appear until approved.)
john's Gravatar hello,
I am trying to truncateToFit() the labelDisplay after I have set the text and the embedded font,color,font size. It seems the truncation is never successfull...Which is the correct way to do it, I mean when exactly in the component life cycle the truncateToFit must be placed?? Does the setTextFormat() must be called before or after?

thanks a lot for your help,
John
# Posted By john | 9/11/11 2:03 PM
Jeffry Houser's Gravatar John,

This isn't something I've tried personally. But, usually I would use updateDisplayList() to change the visual elements. So, after you have the size; and then call setTextFormat(); you could perform a text calculation based on the font to see how much space you have / need and truncate the text accordingly.

Conceptually, anyway.

The LabelItemRenderer, which this component extends uses a StyleableTextField as the labelDisplay and I do not see a truncate method on it.
# Posted By Jeffry Houser | 9/11/11 2:32 PM
john's Gravatar hey,
there is the method truncateToFit():
https://www.flextras.com/MobileComponents/Docs/asd...()

I will try the updateDisplayList for my renderer and see...

thanks for your answer
John
# Posted By john | 9/11/11 8:47 PM
Jeffry Houser's Gravatar John,

My bad; I looked up the Adobe documentation which must have been filtering out Flex specific data again. I have no idea why they do that.

Best of luck!
# Posted By Jeffry Houser | 9/11/11 11:22 PM
shyamshyre's Gravatar This tutorial is really cool.
Can you post a tutorial where we use measure()
in the Applicaiton .
# Posted By shyamshyre | 9/26/11 6:50 AM
Colin's Gravatar Cool! And I wonder Can I change the fontsize or color of labelDisplay within the custom IconItemRenderer file? Like the default color s black, and i want it to be blue. thanks a lot for your help, Colin.
# Posted By Colin | 5/18/12 7:59 AM
Jeffry Houser's Gravatar Colin,

I see no reason why you wouldn't be able to.
# Posted By Jeffry Houser | 5/18/12 9:16 AM
Angelo's Gravatar Hi, thanks for the great post.
I'm creating a custom as item renderer composed from an image background, the label and a text item.
i've created everything you told us with your post but i'm a bit confused about the data changeEvent. i used the data function (instead the one you wrote onDataChange) that Flex creates automatically when you start create a new custom item renderer, but, the question is: How i can manage the new image source, the textInput text and the label text that are arriving? Thank you very much!
# Posted By Angelo | 4/9/13 11:18 AM
Jeffry Houser's Gravatar A dataChange event handler is one way to change the elements of your renderer whenever the data element changes.

You can also do this in set data() function, which I think it is what you're saying you're doing. Either one works.

My preference is to use a dataChange event handler, but there is no logical reason for using one vs the other.

Whichever method you prefer; just change the properties of your renderer children (such as image.source or label.text) based on the new data element.
# Posted By Jeffry Houser | 4/9/13 12:36 PM