ItemRendering pt.1

ItemRenderer is the “new” name for what was known as a cellRenderer in ActionScript 2.0. However unlike cell rendering in AS2.0 creating and implementing these custom views is pretty painless to do, but there are a few gotchas and processes to get down pat to make developing them easier. There is a slight diversification between what the Help documents say and what you can actually implement…

According to the documentation all Item Renderers must implement IFactory (as they are factory pattern based classes). For sake of clarity we have a MXML and an ActionScript example below that illustrate how to do this.

First up the ActionScript version

package com.flashgen.renderers
{
	import mx.containers.HBox;
	import mx.core.IFactory;
 
	public class ExampleRenderer extends HBox implements IFactory
	{    	
    	public function newInstance():*
    	{
    		return new ExampleRenderer();
    	}
	}
}

And here is the equivalent in MXML

<?xml version="1.0" encoding="utf-8"?>
<mx:HBox xmlns:mx="http://www.adobe.com/2006/mxml" width="400" height="300" implements="mx.core.IFactory">
	<mx:Script>
		<![CDATA[
			public function newInstance():*
			{
                // this is the name of our MXML component
				return new ExampleMXMLRenderer();
			}
		]]>
	</mx:Script>
</mx:HBox>

As you can see the format is pretty much the same. You’ll notice that both implement IFactory and by doing so have to honour the Interfaces’ contract to implement a public method called newInstance():* (The wild card as return type is required as we don’t want to limit the types of objects that can be processed and returned by the method. Now here is the odd bit. All of this only needs to happen if you wish to interact with this object via a stored instance that is instantiated via a new instance of ClassFactory().

Hold on! What’s ClassFactory()? As Item Renderers are factory objects they need to be imbued with certain characteristics. To achieve this (and the reason that you should implement IFactory) is that when you instantiate your Item Renderer you do it via a new instance of ClassFactory like this:

var myObject :ClassFactory = new ClassFactory(myItemRenderer);

You can then set properties within the renderer like so:

myObject.properties = {myProp1: myValue1, myProp2: myValue2, ...etc}

A common use for this is to pass a reference to the renderers parent, or other external items into the renderer to make communication easier or viable.

However if you don’t need to pass anything through to your renderer beyond the properties and values provided by the data provider for the component it inhabits then you can completely ignore implementing IFactory in your renderer and just build a component (with states, transitions and events etc etc) and as long as you implement it inline as the components item renderer you won’t hear a squeak out of the compiler in regards to not implementing IFactory…

For example I have a super simple item renderer below (in fact it is only a Canvas component with a few labels in it). Now I should implement IFactory and include the method newInstance().

<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml" xmlns="*" width="500">
	<mx:CheckBox x="10" selected="{data.status}"/>
    <mx:Label text="{data.title}" id="title_txt" width="100" x="40"/>
    <mx:Label text="{data.description}" id="description_txt" width="250" x="150"/>
</mx:Canvas>

However I’m going to ignore all of that and take my component and just reference it via it’s fully qualified path as you can see in the code below.

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" viewSourceURL="sourcecode/flex2_examplerenderer/index.html">
	<mx:Script>
		<![CDATA[
			import mx.collections.ArrayCollection;
 
			[Bindable] private var _dp				:ArrayCollection = new ArrayCollection
		    ([ 	
		    	{status:false, title:"Mike Jones", description:"Flex, Flash, Apollo, Flash Remoting, FlashLite"},
		        {status:true, title:"Dave Williamson", description:"FlashLite, Flash, Flex, Apollo, Flash Remoting"},
			{status:true, title:"Sean McSharry", description:"Flash, FlashLite, FMS, Flash Remoting"}
		    ]);
		]]>
	</mx:Script>
 
	 <mx:List id="exampleList_hardcode"
	 	dataProvider="{_dp}"
	 	width="450"
	 	height="80"
	 	variableRowHeight="true"
	 	itemRenderer="com.flashgen.renderers.ExampleRendererMXML"
	 	selectionColor="0xCCCCCC" x="10" y="98"/>
 
</mx:Application>

I personally wouldn’t advocate this approach as it makes your code start to look messy and you may get a few unexpected surprises later on via this procedure. Best stick with the documented approach and reduce the headache and risk should you ever need to change the renderer to one that does implement IFactory.

For completeness here is the ActionScript class that implements IFactory (it doesn’t have to be ActionScript – you could do it in MXML if you wanted to). And directly below it I have the List class implementing it as an item renderer as we did with the non Ifactory version.

package com.flashgen.renderers
{
	import mx.core.IFactory;
	import mx.controls.CheckBox;
	import mx.controls.Label;
	import mx.containers.Canvas;
 
	public class ExampleRenderer extends Canvas implements IFactory
	{
 
		private var _cb 			:CheckBox;
		private var _title 			:Label;
		private var _description	:Label;
 
 
		public function ExampleRenderer()
		{
			super();
		}
 
		public function newInstance():*
		{
			return new ExampleRenderer();
		}
 
		override protected function createChildren():void
		{
			super.createChildren();		
 
			_cb = new CheckBox();
			_title = new Label();
			_description = new Label();
 
			_cb.x = 10;
 
			_title.x = 40;
			_title.width = 100;
			_title.id = "title_txt";
 
			_description.x = (_title.width + _title.x) + 10;
			_description.width = 250;
			_description.id = "description_txt";
 
			this.addChild(_cb);
			this.addChild(_title);
			this.addChild(_description)
		} 		
 
		override protected function commitProperties():void
		{
			super.commitProperties();
			_cb.selected = data.status;
			_title.text = data.title;
			_description.text = data.description;
		}
	}
}

As promised here it is applied to a List…

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" initialize="init()" viewSourceURL="sourcecode/flex2_examplerenderer/index.html">
	<mx:Script>
		<![CDATA[
			import mx.collections.ArrayCollection;
			import com.flashgen.renderers.ExampleRenderer;
 
			[Bindable] private var _listRenderer	:ClassFactory;
			[Bindable] private var _dp				:ArrayCollection = new ArrayCollection
		    ([ 	
		    	{status:false, title:"Mike Jones", description:"Flex, Flash, Apollo, Flash Remoting, FlashLite"},
		        {status:true, title:"Dave Williamson", description:"FlashLite, Flash, Flex, Apollo, Flash Remoting"},
				{status:true, title:"Sean McSharry", description:"Flash, FlashLite, FMS, Flash Remoting"}
		    ]);
 
		    private function init():void
			{
				_listRenderer = new ClassFactory(ExampleRenderer);
			}
		]]>
	</mx:Script>
 
	 <mx:List id="exampleList"
	 	dataProvider="{_dp}"
	 	width="450"
	 	height="80"
	 	variableRowHeight="true"
	 	itemRenderer="{_listRenderer}"
	 	selectionColor="0xCCCCCC" x="10" y="10"/>
 
</mx:Application>

Just so you can compare the IFactory option and the “shortcut” version side by side I have uploaded a SWF with both in it (right click for source). The top List component is the one using the IFactory / ClassFactory instantiation method. While the one below it is the one that is implemented by referencing the class directly via its fully qualified name.

[kml_flashembed movie=”http://blog.flashgen.com/swfs/flex2_examplerenderer.swf” width=”470″ height=”190″ /]

As you have probably noticed – no difference. This is true, but as I mentioned earlier when you want to actually interact with the renderer then you’ll come unstuck quickly if you don’t implement IFactory.

That’ll do for this part – in the next part we’ll look at using States and Transitions in our itemRenderers and some of the bugs that they uncover when used in List based components…

One Comment

Leave a Reply

Your email address will not be published. Required fields are marked *