Google analytics code

Wednesday, November 18, 2009

Flex: TileList cell switching/redraw problem using an itemRenderer

While working with the TileList component I ran into a very odd issue. Images would move around while scrolling up and down. Also when I updated the dataProvider the TileList didn't display the new information. Here is an example so you can see what is going on. You can view the source the source by right-clicking and selecting "View Source".

After a lot of research, and a post on StackOverflow, I figured out what was going on. My broken example relies on a MXML completion method (init, creationComplete, etc..)  to set the source of the image in the itemRenderer.

index.mxml
<mx:Component>
    <itemRenderers:ImageTile_bad />
</mx:Component>

ImageTile_bad.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:Image xmlns:mx="http://www.adobe.com/2006/mxml" initialize="init()">
    <mx:Script>
        <![CDATA[
            /**
            * This is an example of how not to use an item render in a TileList  
            * */
            private function init() : void {
                this.source = data;
                this.name = data.toString().split("/").pop().split(".").shift();
            }
        ]]>
    </mx:Script>
</mx:Image>

TileList reuses cells inside the component. I'm not sure why, but can't keep track of where things should render and starts switching images around. I tried overridding the data method and it works, but I wanted to find a solution that doesn't require overriding a private method.

The answer is to use a setter inside the itemRenderer to assign the value to the component. Here is a snip-it of code to show you what I mean.

index.mxml
<mx:Component>
    <itemRenderers:ImageTile img="{data}"/>
</mx:Component>

ImageTile.mxml
<mx:Image xmlns:mx="http://www.adobe.com/2006/mxml">
    <mx:Script>
        <![CDATA[
            /**
            * This is an example of how to use an itemRenderer in a TileList
            * Create a setter that will always update the when the TileList redraws
            * */
            public function set img(value:String) : void {
                //make sure there is something to work with. avoid error# 1010
                if(!value) {
                    return;
                }
                this.source = value;
                this.name = value.toString().split("/").pop().split(".").shift();
            }
        ]]>
    </mx:Script>
</mx:Image>

When TileList redraws the cell it will pass data into the img setter. This will make sure that the cell receives the correct information when it redraws. Also switching the dataProvider on the TileList works properly now. Here is a link to the working example that has available source code. Feel free to contact me if you have any questions.

6 comments:

  1. Priceless!

    I spent a few hours trying to figure out why my tilelist wasn't updating properly. By using your technique my problem is solved. I was using a Drop-In itemRender would was inconsistently updating. Wouldn't this be considered a bug in the SDK?

    Thanks.

    ReplyDelete
  2. It sure feels like a bug. I spent way to much time trying to figure this out(2wk+). You really don't want to see my interim solutions. I'm glad it helped you.

    In another project I was adding a canvas to the tile which was created as a canvas. When I updated the tile I had to use removeAll() on the tile before adding anything else to it. Not sure if it's a bug, or if the tileList is supposed to be used for really simple things and it falls apart when trying advanced things. It might be a side affect of object recycling to keep memory low.

    ReplyDelete
  3. I am using your method for the same issue. In my app my TileList loads custom components (a VBox with an Image and a Label) for about 30 images. My Image's source is set to a "no image" graphic file initially.

    When my data loads from the database (via CFC), I load objects into an ArrayCollection that is the dataProvider for my TileList. Then, following your method, the "data" value is passed into my custom component's setter function (as a typed object). If there is an Image path in that object the component's Image source is updated, otherwise it stays the default.

    When the app loads I see the 30 images, 25 generic "no image" images and 5 custom. However if I scroll, the custom images appear to duplicate. They aren't really duplicating, rather the custom image path gets applied to a 2nd and then a 3rd image. So in the end I have the real custom image and then 2 others that appear the same but actually shouldn't have an image at all.

    what causes this and how should I work around it??

    ReplyDelete
  4. I ran into a similar issue when doing something a bit more complex. You might need to use this.removeAllChildren() in the custom component setter to make sure there is nothing in the cell. If that doesn't fix it then please let me know.

    ReplyDelete
  5. Hi

    I had a problem similar to the one above, and also when using embedded images: scrolling made the images disappear. Fixed it in the itemRenderer, tileImage in your case, by changing the following:

    Original_Bad:


    Working_Good:






    Found the soluiton at:
    http://blog.flexexamples.com/2007/08/03/duplicating-images-using-the-bitmap-and-bitmapdata-classes/

    Brian

    ReplyDelete
  6. Fixed my problem... Thanks a million.

    ReplyDelete

If you found this page useful, or you have any feedback, please leave a comment.