Recycling, Virtualization and Buffering

Parent Previous Next

To understand itemRenderers, you have to understand why they are what they are and what our intentions were when we designed them. Suppose you have 1,000 records you want to show. If you think the list control creates 1,000 itemRenderers, you are incorrect. If the list is showing only 10 rows, the list creates about 12 itemRenderers—enough to show every visible row, plus a couple for buffering and performance reasons. The list initially shows rows 1–10. When the user scrolls the list, it may now be showing rows 3–12. But those same 12 itemRenderers are still there: no new itemRenderers are created, even after the list scrolls.


Here's what the grid does. When the list is scrolled, those itemRenderers that will still be showing the same data (rows 3–10) are moved upward. Aside from being in a new location, they haven't changed. The itemRenderers that were showing the data for rows 1 and 2 are now moved below the itemRenderer for row 10. Then those itemRenderers are given the data for rows 11 and 12. In other words, unless you resize the list, those same itemRenderers are recycled to a new location and are now showing new data.


This behavior complicates the situation for certain programming scenarios. For instance, if you want to change the background color of the cell in the fourth column of the fifth row, be aware that the itemRenderer for that cell may now be showing the contents of the twenty-first row if the user has scrolled the list.


So how do you make changes like this?


The itemRenderers must change themselves based on the data they are given to show. If the itemRenderer for the list is supposed to change its color based on a value of the data, then it must look at the data it is given and change itself. The way you do this, is expose a setData method. In the next section, we will take a look at a simple item renderer.


One thing many people try to do is access an itemRenderer from outside of the list. For example, you might want to make the cell in the fourth column of the fifth row in a DataGrid turn green because you've just received new data from the server. Getting that itemRenderer instance and modifying it externally would be a huge breach of the framework. Instead, you should perform the logic associated in the setData method. You cannot assume the renderer has been freshly created. When it is time to update the renderer, the components might be holding old values from the item it was rendering before. You cannot assume that there are only as many renderers created as you see on-screen. You cannot assume that the first renderer created is for the first visible row. The most common error when using renderers is in not resetting values. For example, if you have code that makes the text red if the value is below zero, you might write:


if (data.price < 0)

  this.setStyle("color", "0xFF0000");


This is incorrect. It will work the first time the renderer is created since the default color is black and will work if values are greater than 0, and if the value is less than zero. But once the renderer displays a value less than zero and turns the text red, if it is later asked to renderer a different value that is greater than zero, there is no code to turn the color back to black again. What will happen is that the data will look fine until you start scrolling or receiving updates, then the red will start to appear in funny places. The correct way is to write code like this:


if (data.price < 0)

  this.setStyle("color", "0xFF0000");

else

  this.setStyle("color", "0x000000");


A word about performance:


Since we draw just the visible area, the memory/performance will depend more on the height and width of the grid and the number of columns you pack into the visible area. A couple of pointers to improve performance:


1)      If you have a LOT of columns, set horizontalScrollPolicy to auto or on, which will use horizontal renderer recycling, improving performance.


2)      If you have custom item renderers, be careful with them, if you nest too many heavy weight containers, you will run into performance issues. The renderers should be as light weight as possible, however, we have not yet seen any major issues with even complex item renderers. The key to keep in mind is that the setData method should do as little works as possible, since it is the one that gets called as you scroll.


3)      If you have a very large amount of data (say hundreds of thousands of records), you should consider server side paging. Please review the filterPageSortMode topic for more informaion.


In the next section, we will take a look at how custom item renderers are written.