Example: customizing the SpinnerList in mobile AIR

Question:

Can a SpinnerList on Android or iOS AIR be changed so that certain items in the list have a different background? For example, in a list of states, make Alaska have a black background even when it is not selected.

Answer:

That can be done with a custom item renderer for the SpinnerList. It would extend the existing item renderer and would switch how it is rendered based on data in the data source.

As with so many things in Flex, there is more than one way to do this. This example is just a suggestion. Each project is different and this example may not be the best way for each one.

Item Renderer’s Data

Item renderers should rely solely on the data property for information on how to draw themselves. Item renderers can be reused, so when the data property changes the items renderer needs to change along with it or old information may be displayed. I suggest whatever data source is used for the SpinnerList should include a flag to indicate whether the state name needs to be pre-hilited. Giving the item renderer the logic to figure out if it needs to have a different background color based on the state name makes it so you will have to change your program whenever the list of states that need to be pre-hilited changes.

A very simple data source for the SpinnerList that does this would look like this:

var rawData : Array = [
{"value":"al","hilite":false,"name":"Alabama"},
{"value":"ak","hilite":true,"name":"Alaska"},
{"value":"az","hilite":false,"name":"Arizona"},
{"value":"ar","hilite":false,"name":"Arkansas"}
];
var myDataSource : ArrayCollection = new ArrayCollection(rawData);
 

How to Change the Color of the Item Renderer Background

The code for the default item renderer should be opened up and examined to see how it is rendered. The default item renderer for the SpinnerList can be found at:

[Flex sdk]\frameworks\projects\mobilecomponents\src\spark\components\SpinnerListItemRenderer.as

Item renderers tend not to use skins in order to keep them relatively lightweight. Instead of relying on skins, they usually contain the drawing logic. This particular skin, SpinnerListItemRenderer, draws a transparent box behind itself so it can capture click events. We can override this and draw a colored box instead.

Create a new item renderer that extends the SpinnerListItemRenderer. I created a class named BackgroundColorItemRenderer within my package com.adobe.example.spinner. In this new class override the method, drawBackground, that draws this transparent box and change it so it makes the background a color. Here is my logic: if the data property does not contain a value of hilite that is equal to true then pass the method to the ancestor class for rendering. Otherwise, give the background a color.

override protected function drawBackground(unscaledWidth:Number, unscaledHeight:Number):void
{
    if (! data.hilite)
    {
        // if no hilite, use the existing logic to draw the background
        super.drawBackground(unscaledWidth, unscaledHeight);
    }
    else
    {
        // if data.hilite == true, draw a black background
        graphics.beginFill(0x000000, 1.0);
        graphics.lineStyle();
        graphics.drawRect(0, 0, unscaledWidth, unscaledHeight);
        graphics.endFill();
    }
 }
 

In this method the color for the background is hard-coded. Create a style that sets a value for this for the component to move this value from the component to the style sheet. The name for the style property can be relatively arbitrary as long as it is not the same name as any other style for the component. I added styles, spinnerBackgroundColor and spinnerBackgroundAlpha, for the item renderer. Since, when using a black background, the text color is no longer visible, I changed it to white.

@namespace spinner "com.adobe.example.spinner.*";
spinner|BackgroundColorItemRenderer {
 spinnerBackgroundColor : #000000;
 spinnerBackgroundAlpha : 1.0;
 color : #FFFFFF;
}
 

I change the drawBackground method use the value from the style sheet:

override protected function drawBackground(unscaledWidth:Number, unscaledHeight:Number):void
{
    if (! data.hilite)
    {
        // if no hilite, use the existing logic to draw the background
        super.drawBackground(unscaledWidth, unscaledHeight);
    }
    else
    {
        // if data.hilite == true, draw a black background
        graphics.beginFill(
                getStyle(“spinnerBackgroundColo”), 
                getStyle(“spinnerBackgroundAlpha”));
        graphics.lineStyle();
        graphics.drawRect(0, 0, unscaledWidth, unscaledHeight);
        graphics.endFill();
    }
 }
 

Now, all that is left to do is to assign my data source and my item renderer to the SpinnerList:

<fx:Script>
<![CDATA[
    import mx.collections.ArrayCollection;
    import mx.events.FlexEvent;

    private static const SPINNER_DATA : Array = 
        [
            {"value":"al","hilite":false,"name":"Alabama"},
            {"value":"ak","hilite":true,"name":"Alaska"},
            {"value":"az","hilite":false,"name":"Arizona"},
            {"value":"ar","hilite":false,"name":"Arkansas"}
        ];

    [Bindable]
    public var spinnerData : ArrayCollection = new ArrayCollection(SPINNER_DATA);

 ]]>
 </fx:Script>
<s:SpinnerList 
            dataProvider="{spinnerData}" 
            labelField="name" 
            width="400" 
            itemRenderer="com.adobe.example.spinner.BackgroundColorItemRenderer" />
 

Comments are closed.