Welcome!

Welcome to the official BlackBerry Support Community Forums.

This is your resource to discuss support topics with your peers, and learn from each other.

inside custom component

Adobe AIR Development

Reply
Contributor
ChillyBin
Posts: 22
Registered: ‎01-28-2011
My Device: Not Specified

Lists and CellRenderers and "label"

[ Edited ]

I'm noticing some strange behaviour with a List that uses a CellRenderer. Items I am adding to the list do no show up and/or it messes up the existing items on the list. In my previous post I was not using AddItem() but I am using it now. I got the problem to go away by adding a field with the name "label" in it. This seems odd and I want to make sure there is not some other underlying problem and if anyone else has encountered this.

First of all, is "label" a special reserved string in lists?
In the below code (not using CellRenderer). The object being inserted in the array must be named "label" otherwise the item will not show up on the list.

            anArr = new Array();
            anArr.push({ label:"one" });
            anArr.push({ label:"two" });
            taskTimerList.dataProvider = new DataProvider(anArr);


When using a CellRenderer, it appears that "label" is not required and works. But when I add additional items to the List, the item either doesn't show up and/or I see duplicates in the existing items on the list and some of the existing items gets shifted around as I scroll up and down.

I am feeding an Array of objects into the DataProvider. That object has a bunch of "fields"


            taskList.dataProvider = new DataProvider(getArrayClone());

                          //Array of Tasks                              
            ...
               
            taskList.addItem(task);
            ....
            
            
            Class Task
                Has fields and getter/setters for fields such as "desc". I

                needed to add a field called "label" with empty string and

                problem went away.
                
                
I also noticed that even when I was using a CellRenderer. If I renamed one of my existing fields to "label" that field will appear twice on the row, once as set by the CellRenderer and the other I assume automatically since "label" has pecial meaning.

In my CellRenderer, I override the drawLabel to load images and text:

        override protected function drawLabel():void{
            super.drawLabel();
            if(data){
                var theURL:smileyfrustrated:tring = data.imageURL;
                var imageRequest:URLRequest = new

                           URLRequest(theURL);
                imageLoader.load(imageRequest);            
                taskDesc.text = data.description;        
                timerPicker.minutes = data.duration;
            }
        }  //I didn't even need to  reference the "label" field here


Appreciate any input. It's working now but I'm thinking there may be another underlying issue that may bite me later.

 

EDIT: Did some more debugging and am finding that some of my object instances on the list fluctuates and one object will become the same instance of another object as I scroll up and down.

 

I need to understand better how the CellRenderer instantiates the objects on the list. Anyone shed some insight on that?

Please use plain text.
Regular Contributor
renaun
Posts: 86
Registered: ‎10-25-2010
My Device: Not Specified

Re: Lists and CellRenderers and "label"

drawLabel is more for drawing cellrenderer and will be called differently then setting the actual data values.

 

Move the setting of the data values in the data setter:

 

 

override public function set data(value:Object):void
{
super.data = value.data;
  if(data)
{
        var theURL:String = data.imageURL;
        var imageRequest:URLRequest = new URLRequest(theURL);
        imageLoader.load(imageRequest);            
        taskDesc.text = data.description;        
        timerPicker.minutes = data.duration;
    } 
}

 

 

blog: http://renaun.com
twitter: @renaun
Please use plain text.
Contributor
ChillyBin
Posts: 22
Registered: ‎01-28-2011
My Device: Not Specified

Re: Lists and CellRenderers and "label"

[ Edited ]

Thanks renaun. Changing the code to what you suggested removes the need to have the "label" property.

 

However, I'm still getting the problem with the instances of the objects being changed as I scroll up and down. The timerPicker object in my code would become duplicated at times and appear in, for example, the top row and the bottom row. I did some Trace debugging and noticed that they are the exact same instances (e.g. timerPicker.name shows "instance76" in both rows).

 

Upon further, debugging I have noticed that the CellRenderer constructor is called to create the objects on each row as the rows come into view on the list. This is followed by the data setter and drawLabel methods which allow us to bind the data with those created objects.

 

What I have noticed is that sometimes, the constructor is not called but the data setter and drawLabel is only called, causing the displayed row to (randomly?) use another row's instance objects.

 

I have 3 theories about this but not sure which is the case:

 

1) This is normal behaviour and the object instances need to be set explicitly in the data setter for each item. In the case of simpler objects such as buttons and labels, it doesn't matter what instance of the object is being used since they as long as they are set to display the proper information from the data property. But with more complex objects, the instances will matter. For example, I have an timer object that extends the Label and it handles displaying the remaining minutes on a timer. This timer label moves from one row to another as I scroll from top to bottom.

 

2) This is a SDK bug and the specific instances should always be associated to a  specific row and  each row should have unique instances created by the constructor.

 

3) Something is wrong with my code.

 

 

Any idea which of the above or another may be the case?

 

Thanks.

Please use plain text.
Developer
peter9477
Posts: 6,473
Registered: ‎12-08-2010
My Device: PlayBook, Z10
My Carrier: none

Re: Lists and CellRenderers and "label"

I've only just started looking at Lists and custom renderers, and found this thread while researching the topic.  As no one else has answered yet, I will.

 

The problem is that only enough CellRenderers are created for the number of items that can be visible in a list, rather than the total number of items in the list.  (Actually a few more can be created, but that's an unimportant detail.)  As you scroll down through the list, renderers are "pulled off" the top and moved to the bottom, after changing their data to that of the next item to be scrolled into view.  If you scroll up, they're pulled off the bottom and moved to the top.

 

In each case, the "set data()" routine is called with the data object (e.g. {label: 'foo'}) from the DataProvider for that item.

 

To make a renderer work properly, you have to ensure that it properly updates all of its children (e.g. checkboxes, images, etc) in this data accessor method.

 

I'm a bit shocked that this doesn't seem to be more widely known, not to mention documented.  It's also pretty discouraging to see things like the example code in the docs for AlternatingCellRenderer, where this wee fact isn't taken into account.  That example is fundamentally broken because it ignores this fact.  If you change that example so the number of list items is, say, twice as many as the visible number, then "check" the top item's checkbox, and scroll the list up, you'll see one of the checkboxes for the later items "mysteriously" appear to be checked already as it scrolls into view.


Peter Hansen -- (BB10 and dev-related blog posts at http://peterhansen.ca.)
Author of White Noise and Battery Guru for BB10 and for PlayBook | Get more from your battery!
Please use plain text.
Developer
gerardkcohen
Posts: 48
Registered: ‎10-06-2009
My Device: Not Specified

Re: Lists and CellRenderers and "label"

As mentioned, this has to do with the way items are reused for efficiency.

 

In creating my own custom renderers, I have found that setting a 'changed' flag helps.  Doing something like this:

 

override public function set data(value:Object):void {		
			
			if (value != null && value.item) { //make sure data is not empty
				if (super.data != value) { // check to make sure that its a new item by checking previous data for match
					super.data = value;
					
					itemChanged = true;		
					
					invalidateProperties();
				} 
			}
			
		}

 

Then, usually overriding something like commitProperties() I will do something like this:

 

override protected function commitProperties():void {
			if (itemChanged) {
				itemChanged = false;
				
				//perform any updates to items, ie label
				
							
			}
			
			invalidateSize();
			invalidateDisplayList();
			
		}

 

I hope that makes sense and helps.

Please use plain text.