Our Blog

If you are starting to do mobile development and are used to create all your ItemRenderers in MXML, you may notice that the small devices like phones and tablets do not perform as well as the desktop does and you need to start looking closely to different ways to optimize you app.

One of the ways to optimize your mobile app is to create your ItemRenderers in ActionScript. Narciso Jaramillo wrote a good article for Devnet with great tips for mobile development and one of the items was exactly that, to keep your ItemRenderers in pure ActionScript.

Narciso mentions the Flex Framework comes with two ItemRenderers one is LabelItemRenderer that extends UIComponent and the other is IconItemRenderer that extends LabelItemRenderer. Those classes are great, but sometimes you have a different use case that needs a different set of classes. So in my examples I will not use those renderers. Instead, I will extend from UIComponent directly or from SpriteVisualElement for an even more lightweight class than UIComponent.

For the purpose of this tutorial, I'm planning a series of posts explaining the basics and moving on to more difficult renderers.

Basic Example

Let's start with the most simple renderer, a TextField that displays some text. To make it simple, we will extend from UIComponent and implement the IDataRenderer interface required from the List.

But before talking about the renderer, let's talk about the other elements.

Application

You need to create a Mobile project. In this example, I use the simplest of all root containers, that is an Application, not the TabbedApplication nor ViewBasedApplication subclasses. I added a List that expands 100% as the only element.

<?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">

   
   <fx:Script>
      <![CDATA[
         import mx.collections.ArrayList;
         [Bindable]
         private var items:ArrayList = new ArrayList( Font.enumerateFonts( true ) );
      ]]>
   </fx:Script>
   
   <fx:Style source="styles/Main.css"/>
   
   <s:List id="list" width="100%"
         height="100%"
         dataProvider="{ items }"
         labelField="fontName"
         itemRenderer="renderers.UILabel"/>

   
</s:Application>

Styles

I added an external StyleSheet where I have all the styles for the ItemRenderer:

@namespace s "library://ns.adobe.com/flex/spark";
@namespace renderers "renderers.*";

renderers|UILabel
{
   fontSize: 20;
   color: #000000;
   font-family: "_sans";
   min-height: 50;
   padding-left: 10;
}

ItemRenderer

I have a simple class extending UIComponet that gives me a few handy methods and the ability to participate in the CSS framework.

The 3 methods from UIComponent that we use are the following:

  • measure where we read the min size of the renderer from the style sheet. That comes in handy because devices with different dpi resolutions need different sizes.
  • createChildren where we create the TextField, set the styles and if there is the data already available, we set the text in the TextField to be displayed.
  • updateDisplayList where we layout the elements and we draw a line separator for each ItemRenderer.

In additions to those methods, we have the data property, which is the implementation of the IDataRenderer interface. That interface is the contract with the List and it is the way that the data is pushed to the ItemRenderer. If the TextField exists at the moment when the data is set, we set the text on the TextField, otherwise, we save it for later.

package renderers
{
   import flash.text.Font;
   import flash.text.TextField;
   import flash.text.TextFormat;
   import mx.core.IDataRenderer;
   import mx.core.UIComponent;
   
   public class UILabel extends UIComponent implements IDataRenderer
   {
      //Protected properties
      protected var labelField:TextField;
      
      // Public Setters and Getters
      protected var _data:Object;
      public function set data( value:Object ):void
      {
         _data = value;
         // if the textfield has been created we set the text
         if( labelField )
         {
            labelField.text = Font( data ).fontName;
         }
      }
      public function get data( ):Object
      {
         return _data;
      }
      
      // Contructor
      public function UILabel()
      {
         percentWidth = 100;
      }
      
      // Override Protected Methods
      override protected function measure():void
      {
         measuredHeight = measuredMinHeight = getStyle( "minHeight" );
      }
      //--------------------------------------------------------------------------
      override protected function createChildren():void
      {
         labelField = new TextField();
         labelField.defaultTextFormat = new TextFormat( getStyle( "fontFamily" ), getStyle( "fontSize" ) );
         labelField.autoSize = "left";
         addChild( labelField );
         // if the data is not null we set the text
         if( data )
            labelField.text = Font( data ).fontName;
      }
      //--------------------------------------------------------------------------
      override protected function updateDisplayList( unscaledWidth:Number, unscaledHeight:Number ):void
      {
         // position the field
         labelField.x = getStyle( "paddingLeft" );
         labelField.y = (unscaledHeight - labelField.textHeight ) / 2;
         // we draw a separator line between each item
         var lineY:int = unscaledHeight -1;
         graphics.clear();
         graphics.lineStyle( 1 );
         graphics.moveTo( 0, lineY );
         graphics.lineTo( unscaledWidth, lineY );
      }
   }
}

As you can see writing an ItemRenderer in ActionScript is not as difficult as it sounds.
I'm planning to show other examples in the future with multiple elements, background, states, text manipulation, multiple columns and more.

Continue reading Part 2 of this series on Item Renderers

The source is available for download.

Nahuel Foronda

Nahuel Foronda

16 Comments

  1. Joan Llenas
    Man, that is optimization!
    Would it make sense to externalize the data setter logic within the commitProperties method?
    In Flex desktop you have to take care of this by yourself as the framework sets data many times in the same frame, which can become a bottleneck as the app grows..

    Cheers!
  2. Thomas Burleson
    Nahuel,
    Great article on mobile optimization. Thanks for the concrete sample. I would highly recommend reading "Metadata-Driven Invalidation" http://bit.ly/f8zGBG...

    The [Invalidate("properties,displaylist")] would simplify your code even more. John and I use it extensively to wonderful advantages.
  3. Nahuel Foronda
    Thomas, I like what you've done, very clean and powerful but..... I wouldn't use that for a mobile renderer because you are using metadata and that is expensive. The Flash player needs to have better introspection to access all the metadata, more natively.
    But for a desktop, I think that your approach is good.
  4. John Yanarella
    Hey Nahuel,

    I am using DescribeTypeCache in InvalidationTracker's introspection logic, so I would expect any performance hit would be limited to the first instantiated renderer of a given Class type.
  5. Nahuel Foronda
    Hi John,
    Yes, caching the DescribeType helps, but I would use it only in certain cases for my mobile renderers. Let me be clear, your solution is pretty neat and I love it.
    But for mobile, I'm using the bare minimum, not even UIComponent. I will show that in my next post.
  6. Thomas Burleson
    Nahuel,
    John and I are considering how to enhance [Invalidate] to work with non-UIComponents; to make immediate [1-frame] calls to specific validation handlers.

    But back to your concersn... I am not trying to be pedantic, but I am striving for clear understandings.

    You do not recommend UIComponents for renderers because of the the validation lifecycle delays?
    What specifically is it that concerns your regarding performance on renderers?
  7. Nahuel Foronda
    Thomas, there is nothing wrong with the UIComponent and its life cycle. I personally don't use them for my renderers when I'm doing mobile, mostly because I want a light weight component but every case is different.
    The bigger the rendered the better is to use the UIComponent. But sometimes it is a simple thing that you can do it with just a SpriteVisualElement. There is no black and white here :)
  8. Vincent

    Vincent

    Hi Nahuel,

    Thank you for this example. Extending the UIComponent provide a leightweight ItemRender but doing this way, how can you access some usefull properties of the ItemRender like itemIndex or how can you be aware of the user interactions with the item ?
  9. Nahuel Foronda
    Hi Vincent, check the example in the part 4 where I'm extending from a base renderer class.
    That base class implemented the IItemRenderer interface that provides the itemIndex and other useful methods.
  10. akin
    thanks for the example,but when I use the part 1 example code on the view base mobile app,I put the list within a view ,the list display only first record not matter how many records in the arraylist as the list's data provider.Is there some point I misunderstand? Tks.
  11. Eric
    Hi, Very nice example. I am trying to port this to a datagrid (for a desktop application) but it does not work. I guess there is a need to implement a different interface. Can you offer guidance as to how this renderer should be midifed to work on a spark datagrid. I made a working one in mxml but I need a very lean renderer because there are tons of them of the screen and this slows down my application. thanks