Archive for April, 2008

Adobe Media Player

,

Adobe’s just released a new product- the Adobe Media Player. This application is built upon AIR using the Flex 3 SDK. The AMP is a new type of video player.

As I write this I’m watching an episode of Star Trek: The Original Series from January 1968. It looks terrific and streams smoothly. There are a dozens of shows to choose from.

I personally think this is paving the way for the future of home entertainment. Why be at the mercy of the cable or satellite companies when the content you want to watch is available when you want to watch it. Right now you can download movies from iTunes and Amazon Unbox. NBC and FOX have the Hulu joint venture with movies and TV shows. I haven’t rented a movie at a movie rental store in who-knows-how-long.

For me, the problem is making the interface to these services as easy as bringing up the guide on my TiVo and as simple to connect all the boxes together.

Here’s an excerpt from the announcement:

It [AMP] provides high quality video playback of streamed, downloaded, or locally stored Internet TV shows and video podcasts. Users can subscribe to Internet television shows and other online video content, have them download automatically in the background, and later view them on demand. AMP’s user interface optimizes the user experience, allowing users to easily enjoy finding and viewing their favorite shows.

AMP represents a tool that is not only important to consumers, but publishers who want to monetize advertisements.  The last couple of months we have been working very hard to get the top broadcasters signed to provide content through AMP. We have successfully signed CBS, MTV and Food Network as just a few that will be part of our launch.

Download Adobe Media Player today!

itemRenderers: Part 5: Efficiency

,,,

If you are displaying a large number of itemRenderers – either in the DataGrid or AdvancedDataGrid – your application’s performance may be adversely affected if you do not code these itemRenderers effeciently. Here are some tips that might help:

  • Limit the number of columns using itemRenderers. Do you really need to have every column be a custom itemRenderer? Sometimes you do, but is all that glitz overwhelming the user?
  • Try not to change the style of the elements in your itemRenderer too frequenty. If you need to switch styles (eg, green for positive values, red for negative values), consider having 2 controls preset with those styles and making one visible. Changing styles is one of the more time-consuming tasks in Flex.
  • Do not use Containers as the basis for your itemRenderers. Containers have a lot of overhead. They are fine for limited use, but it would be more efficient to write your itemRenderers based on UIComponent.

Switching Styles

Here’s an itemRenderer which switches components depending on the value of the data field.

<mx:Canvas>
<mx:Script><![CDATA
private function lessThanZero() : Boolean {
return data.price < 0;
}
]]></mx:Script>
<mx:Label text="{data.price}" color="#FF0000" visible="{lessThanZero()}" />
<mx:Label text="{data.price}" color="#00FF00" visible="{!lessThanZero()}" />
</mx:Canvas>

This will be faster than setting the style. Some other things to keep in mind:

  • Avoid data binding to styles. Not only is changing styles slower than most operations, adding data binding code on top of it just makes it worse.
  • Use a Canvas or extend ListItemRenderer or as the root of the itemRenderer. This allows you to place controls on top of each other.

Extending UIComponent

By far the most efficient way to write an itemRenderer is to extend UIComponent using an ActionScript class. You’ll have complete control of the code and the renderer will be as efficient as possible.

Let’s start with the example above, switching styles, and write a simple itemRenderer extending UIComponent.

package renderers
{
import mx.controls.listClasses.IListItemRenderer;
import mx.core.UIComponent;

public class PriceItemRenderer extends UIComponent implements IListItemRenderer
{
public function PriceItemRenderer()
{
super();
}

}
}

You’ll notice that not only did I write the class to extend UIComponent, I also have it implementing the IListItemRenderer interface. It is necessary to do this because a list control will expect any renderer to implement this interface and if you do not, you’ll get a runtime error as the list attempts to cast the renderer to this interface.

If you read the documentation on IListItemRenderer you’ll see that is an amalgamation of many other interfaces, most of which UIComponent implements for you. But there is one interface extended by IListItemRenderer that UIComponent does not implement: IDataRenderer. This requires you to add the code to give the itemRenderer class the data property you’ve been using all along.

If you attempt to use this class without implementing IDataRenderer you’ll get these errors when you compile the code:

1044: Interface method get data in namespace mx.core:IDataRenderer not implemented by class renderers:PriceItemRenderer.
1044: Interface method set data in namespace mx.core:IDataRenderer not implemented by class renderers:PriceItemRenderer.

Edit this class and change it to the following:

package renderers
{
import mx.controls.listClasses.IListItemRenderer;
import mx.core.UIComponent;
import mx.events.FlexEvent;


public class PriceItemRenderer extends UIComponent implements IListItemRenderer
{
public function PriceItemRenderer()
{
super();
}

// Internal variable for the property value.
private var _data:Object;

// Make the data property bindable.
[Bindable("dataChange")]

// Define the getter method.
public function get data():Object {
return _data;
}

// Define the setter method, and dispatch an event when the property
// changes to support data binding.
public function set data(value:Object):void {
_data = value;

dispatchEvent(new FlexEvent(FlexEvent.DATA_CHANGE));
}

}
}

I took the code directly from the Flex documentation for IDataRenderer, so you don’t even have to type it yourself.

With that out of the way we can add in the two labels.

  1. Add variables to hold the two labels.
    		private var posLabel:Label;
    private var negLabel:Label;
  2. Modify the set data function to call invalidateProperties(). This is important because the change of the data has to make the text in the labels change AND to change their visibility.
    	    public function set data(value:Object):void {
    _data = value;
    
    invalidateProperties();
    dispatchEvent(new FlexEvent(FlexEvent.DATA_CHANGE));
    }

    Calling invalidateProperties() tells the Flex framework to call the commitProperties() function at the apppriate time.

  3. Override createChildren() and create the labels, adding them to the display list of the component.
    Notice that in addition to creating the labels, their styles and visible are also set.

    		override protected function createChildren() : void
    {
    super.createChildren();
    
    posLabel = new Label();
    posLabel.visible = false;
    posLabel.setStyle("color", 0x00FF00);
    addChild(posLabel);
    
    negLabel = new Label();
    negLabel.visible = false;
    negLabel.setStyle("color", 0xFF0000);
    addChild(negLabel);
    }
  4. Override commitProperties() to set the labels’ text and visibility.
    In the past you’ve been overriding set data to make this type of change, and you can do that in this class, too, if you prefer.

    		override protected function commitProperties():void
    {
    super.commitProperties();
    posLabel.text = data.price;
    negLabel.text = data.price;
    
    posLabel.visible = Number(data.price) > 0;
    negLabel.visible = Number(data.price) < 0;
    }
  5. Override updateDisplayList() to size and position the labels.
    You must size the labels because their default size is 0x0. This is another thing a Container class will do for you. Since this is a pretty simple itemRenderer you can just set the labels’ size to match the size of the itemRenderer.

    		override protected function updateDisplayList( unscaledWidth:Number, unscaledHeight:Number ) : void
    {
    super.updateDisplayList(unscaledWidth, unscaledHeight);
    
    posLabel.move(0,0);
    posLabel.setActualSize(unscaledWidth,unscaledHeight);
    
    negLabel.move(0,0);
    negLabel.setActualSize(unscaledWidth, unscaledHeight);
    }

All this probably seems a bit complicated just to do this, but keep in mind that using a container will add a lot more code than this.

UIComponent Notes

The UIComponent class is the basis for all visual Flex components – controls and containers. Here are some tips about using UIComponent as your itemRenderer.

  • UIComponent imposes no layout restrictions on its children (unlike a Container). You have to position and size the children yourself.
  • It is also possible to draw graphics and position children beyond the size specified in updateDisplayList().
  • If you plan on using variableRowHeight in your list, you should also override the measure() function to give the list an idea of how big the itemRenderer is.
  • To use UIComponent as an itemRenderer you must implement IDataRenderer.
  • To use the listData property you must implement IDropInListItemRenderer; that was covered in a previous article of this series.