Posts tagged "Flex"

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" />
 

“Failure in Loader: VerifyError: Error #1014″ within the Composite Application Framework (Mosaic)

The error, Failure in Loader: VerifyError: Error #1014″, within the Composite Application Framework (Mosaic) can be caused by the presence of a non-interface class within the interface library that defines a service within Mosaic. Service interfaces should not use non-interface classes as arguments or results. For example, if a service that is being implemented returns an ArrayCollection, the result should be specified in the interface as IList. Creating an interface with an ArrayCollection in its definition will cause other tiles to throw this exception.

See also:
Adobe Forums post about this error
Create a composite application: create a service

How to respond to close and open events for tiles within the Composite Application Framework (Mosaic)

Within the Composite Application Framework, responding to the closing of tiles can be problematic. A tile can detect its own closing. Problems arise if the tile attempts to notify other parts of the program that it is closing through calls to shared services or changing global values. Before the tile can dispatch Mosaic events to other tiles it is closed.

Underlying Mosaic is the Client Component Framework (Gravity). Gravity is an OSGi-inspired framework that allows for plugins or modules to be added and removed to Flash applications. When a tile closes, the Gravity BundleEvent.BUNDLE_UNINSTALLED event is dispatched.

Here is an example snippet passed to me that shows how to listen for Gravity-oriented events:

BundleContext.currentContext.addEventListener(BundleEvent.BUNDLE_INSTALLED, bundleChange);
BundleContext.currentContext.addEventListener(BundleEvent.BUNDLE_RESOLVED, bundleChange)
BundleContext.currentContext.addEventListener(BundleEvent.BUNDLE_RESOLVING, bundleChange);
BundleContext.currentContext.addEventListener(BundleEvent.BUNDLE_STARTED, bundleChange);
BundleContext.currentContext.addEventListener(BundleEvent.BUNDLE_STARTING, bundleChange);
BundleContext.currentContext.addEventListener(BundleEvent.BUNDLE_STOPPED, bundleChange);
BundleContext.currentContext.addEventListener(BundleEvent.BUNDLE_STOPPING, bundleChange);
BundleContext.currentContext.addEventListener(BundleEvent.BUNDLE_UNINSTALLED, bundleChange);
BundleContext.currentContext.addEventListener(BundleEvent.BUNDLE_UNINSTALLING, bundleChange);
BundleContext.currentContext.addEventListener(BundleEvent.BUNDLE_UPDATED, bundleChange);
BundleContext.currentContext.addEventListener(BundleEvent.BUNDLE_UPDATING, bundleChange);

BundleContext.currentContext.addEventListener(ServiceEvent.SERVICE_MODIFIED, bundleChange);
BundleContext.currentContext.addEventListener(ServiceEvent.SERVICE_REGISTERED, bundleChange);
BundleContext.currentContext.addEventListener(ServiceEvent.SERVICE_UNREGISTERING, bundleChange);

 

Deconstructing Experience Services: Client Component Framework (Gravity)

The ADEP Client Component Framework (codename Gravity) is an ActionScript library that takes the concepts of OSGi and applies them to ActionScript. It provides a module and/or plugin architecture in which individual pieces can be added and removed seamlessly. The Gravity SDK comes with the es-sdk package for Experience Services. The parts become much more independent of each other allowing easier integration of content from different teams or repurposing of existing content. It makes it easier to have a library of parts that are put together as needed for applications.

While Gravity can be used by itself, it is one of the foundations of the Client Application Framework (Mosaic).

An excellent sample application using Gravity has been posted along with supporting video tutorials at ADEP Client Component Framework blog.