If you are a Flex developer, one of the Flex components you will work on quite a bit is the Flex List control. This entry will hopefully help you when creating custom List renderers the (hopefully) right and much lighter way. Almost all the Flex applications I work on require a List thats skinned a little differently and display more information than that could be represented by a label value.
You can see the not-so-glorious example here and check the source here
There are quite a number of examples on the web on creating the a custom renderer for a List control. Very simply, a custom renderer can be used as a List renderer by calling it as below:
So thats simple enough. However most of the code I have seen that is used to create custom renderers starts with a parent Canvas tag or some other container. This works for the most part and hides some of the complexity of creating custom renderers, however Canvas and other containers are pretty heavy objects to have a lot of in memory, especially when you are using a bunch of lists in the application. We ran into this issue on the Fan for example when we played fullscreen video without destroying our multiple List instances. As you can see in the image below, the Fan uses three Lists and one TileList in the square view. So at any point we could have close to 40 renderers displaying data.
So how do you create lighter renderers? well here’s how:
1) Implement IListItemRenderer:
Lists expect the renderers to implement the IListItemRenderer interface. Conveniently, the UIComponent class implements most of the methods mandated by the interface (I will not go into my ‘God Object’ rant about the UIComponent class here). The only method you *have* to implement is the get/set data functions to make it compliant with the IDataRenderer interface that IListItemRenderer extends.
2) Implement IDropInListItemRenderer:
So now you have a List renderer that works for the most part. You may consider leaving it at this point but you are still missing some magic. For one thing, your renderer isn’t really aware of its position, etc in the List. To get that info, your renderer must implement the IDropInListItemRenderer interface which forces another couple of getters and setters: those for accepting the BaseListData object. When the List creates instances of your custom renderer and when it swaps data in and out of your renderer, it will pass it the BaseListData object. This object will tell your renderer its row/column index and its the owner List object. So now your renderer is much more aware of its position in the world.
3) Create UITextFields:
Most Lists have textual data that they represent. Text and Label components would seem to be the obvious choice for showing that data. However, these are much heavy objects again. UIComponents, unlike containers, have no qualms about what kind of children they can add to their displaylist (Containers only let classes extending UIComponent to be added to them. Sprites etc can only be added to their rawChildren list). So you can very easily add simple native TextField instances and size them appropriately. However styling them is tedious. Instead, consider adding UITextField instances. These play nice with the Flex LayoutManager and can also use the CSS styles in the Flex app by just being assigned the styleName property.
4) Set styles:
Like HTML, its always a good idea to keep style declarations in external CSS files. These can usually be assigned to Flex controls by just setting their styleName property. For example you can create a Flex CSS file and create a unique identifier like this:
To style a list with the styles you declare above, all you need to do is:
[xml] …. [/xml]
What is a less obvious is that you can create arbitrary values in the style declaration (These aren’t compiled to a packed actionscript object the way a class would be but more like a dynamic class would be). So in my earlier style declaration, I could just as easily add:
A List by default would have no idea what to do with the myawesometextstyleName style value and would ignore it. However, the CSSStyleDeclaration object referenced by the List is passed by reference to the renderers as well. So in your updateDisplayList function, you can call getStyle(‘myawesometextstyleName’) and get the value declared there. Now by assigning the value to the UITextField, you can get the formatting you are looking for.
We still have some ways to go. The remaining stuff comes in next entry so stay tuned :).
15 thoughts on “Creating custom List renderers, the complete guide (Part I)”
That’s a great set of tips there, as I know from experience working with itemRenderers in lists is a bit funny in Flex2, thanks for sharing.
Have you had issues with variableRowHeight when using custom itemRenderers in lists? That’s the biggest issue I came across when creating them in Flex2, that and the fact that the lists always scroll to show an entire list item – which stops you from adding something like smooth scrolling.
I’m looking forward to part two.
I haven’t worked as much with variable row height so I won’t comment there. With the List scrolling in fractions of the itemRenderer’s height, thats because the List actually does not ‘scroll’ but rather swaps the data between itemRenderers. So as you scroll up, at some point the list moves the data in itemRenderers to the renderer below the current. Pretty annoying and apparently wont be fixed till Flex 4 :(.
Yeah it is annoying on the scroll, but it makes sense now you’ve described the reasons for it. I’ve always got around it by writing my own ‘List’ class which functions similarly (but probably won’t perform as well for large lists due to the way it handles item renderers).
Another option is to use a Repeater instead of the List. Again, they aren’t optimized to handle large data but at least you dont have to write a list from scratch.
Yo long time no talk, how are you doing? Are you still in the Philadelphia region?
Drop me a line, I think you got my e-mail through this? If not its firstname.lastname@example.org
est il availible en Francais, my English not good
Those tips were spot on, thanks for the info man 😉
If your’re looking for smooth scrolling then check this site:
I haven’t tried it yet, but it looks very nice!
@smith, Thats an interesting example but using a VBox will not use the itemRenderer reuse strategy that Lists use that make them efficient for a lot of data. The simpler way, if smooth scrolling is all you are looking for is by just using a mx:Repeater.
Thanks for this.
Correct me if I am wrong but for those novices like me :o) In the source, db needs to be [Bindable] to avoid “Data binding will not be able to detect assignments to db” warnings.
(I’m just here to learn so apologies if wrong). Appreciate these examples.
Yes making the data source bindable will remove the warnings but it actually will make the application a little heavier since Flex Builder will generate the data binding code. I often dont bind data that only needs to be set once. Doing it in Actionscript wont give you any warnings at all (list.dataProvider = someArray)
Man, thatâ€™s greatâ€¦Thanks for providing such a good infoâ€¦â€¦â€¦
Again, this is a huge help and resource. Using the clarity gained here, I’m 99% functional with a custom, lightweight renderer. I’m hitting a wall and curious if there’s a ‘magic bullet’ or simple answer…
I’m swapping my styleName property based on the type of data object. It works perfectly. I’m also attempting to vary the row’s height based on that same idea. So, for example, if the object is type A, the row height is 100 – and if type B, the height is 300.
I’m able to do all of this sizing to elements *within* the row (eg: the container holding my various UI elements) but the actual row height always remains constant.
Is there a single phase at which I need to invalidate size to achieve that (set, commit props, measure, etc)? I feel like I’m a single line of code away from having a flawless, wicked fast, variable height itemrenderer.
Drop a note if anything comes to mind. If it’s more complex or I haven’t given enough detail, no worries.
Cheers and thanks again for the resource.
It seems UITextField can be styled only with css. A little annoying.