Working with PlayBook QNX List Components

If you plan to create applications for PlayBook using the BlackBerry Tablet OS SDK for Adobe AIR then you’ll be happy to learn that the support for lists is quite extensive. If you look at the components from the qnx.ui.picker and qnx.ui.listClasses you will find nine UI components:

  1. List
  2. RoundList
  3. ScrollPane
  4. SectionList
  5. TileList
  6. SectionTileList
  7. DropDown
  8. PickerList
  9. Picker

In this post I will discuss each one of these components so you can get a sense of how to use them and when. I strongly believe that you need good ‘List’ components if you want to create great mobile applications. Lists represent one of the most important mean you have to deal with the small size of tablet/phone screens.

Data Provider

All these components (except ScrollPane) use a data provider in order to set the data used for displaying the items. For List, RoundList, TileList, DropDown, Picker, and PickerList you will use the qnx.ui.data.DataProvider class. Typically you wrap an Array into a DataProvider. For example:

var arr:Array = new Array();
arr.push("item 1");
arr.push("item 2");
var dp:DataProvider = new DataProvider(arr);
myRoundList.dataProvider = dp;

SectionList and SectionTileList use an instance of qnx.ui.data.SectionDataProvider for setting the data.

Dealing with Item Selection

When you want to do something once an item (or multiple items) is selected, usually you have to register an event listener for flash.events.Event.SELECT or qnx.ui.events.ListEvent.ITEM_CLICKED.

For List, PickerList, SectionList, SectionTileList, TileList, and RoundList you will do something like this:

list.addEventListener(ListEvent.ITEM_CLICKED, onSelected, false, 0, true);

private function onSelected(e:ListEvent):void {
    trace(e.index); //selected item index
    trace(e.section); //selected section for SectionList and SectionTileList
}

For DropDown you will do:

list.addEventListener(Event.SELECT, onDropDownSelection, false, 0, true);

private function onDropDownSelection(e:Event):void {
    //retrieve the selected item index
    trace((e.target as DropDown).selectedIndex);
}

For Picker you want to listen for the Event.CLOSE event. Usually, you use Picker with at least two data providers and if you listen for the Event.CLOSE event, you can check the selectedItems property of the Picker to see what was selected.

List

This is the most basic list you can use. It allows you to set single selection, multiple selection, or no selection. The default skin for all lists looks for the label property for each item you supplied in the dataProvider property. If you don’t have a label property in your data, then you have to create a custom renderer and use the list.setSkin(SkinName) method to set it. Otherwise nothing will appear in your list.

var arr:Array = [
        {label: "List"}
        {label: "RoundList"}
        {label: "ScrollPane"}
        {label: "SectionList"}
    ];
var list:List = new List();
list.dataProvider = new DataProvider(arr);
list.selectionMode = ListSelectionMode.SINGLE;
list.addEventListener(ListEvent.ITEM_CLICKED, onItemSelected, false, 0, true);

RoundList

A RoundList is pretty much a List that continues to be scrollable after you reach the first/last element by displaying the same elements once again. In the image below you can see a RoundList that has 10 items; once you passed the 10th item, you will see the first items (1, 2, 3, 4).

Here is a snippet of code:

var list:RoundList = new RoundList();
list.dataProvider = new DataProvider(arr);
list.rowHeight = 30;
//set width & height; if not nothing will be rendered.
list.width = 100;
list.height = 300;
list.addEventListener(ListEvent.ITEM_CLICKED, onSelected, false, 0, true);

ScrollPane

ScrollPane components let you set a view port over a component. In the code example below I create an image, create a ScrollPane with the width/height smaller than the image, and then set the image as the ScrollPane content. You can control the direction of the scrolling (Horizontal/Vertical/Both).

var img:Image = new Image();
img.setImage("assets/map.png");

var pane:ScrollPane = new ScrollPane();
pane.x = 30;
pane.y = 30;
pane.width = 300;
pane.height =  300;
pane.scrollDirection = ScrollDirection.BOTH;
pane.setScrollContent(img);

As of writing this post, both in PlayBook Simulator and Flash Builder simulator the ScrollPane doesn’t seem to work as advertised. I wasn’t able to scroll the content.

SectionList

You will find SectionList quite useful when dealing with large collections of data that can be grouped by some criteria. For example a contact list can be grouped by the first letter or a play list could be grouped by the artist name. The list will be renderered with sections and each section will have a number of items.

You can control the section header row height and the item row height. You can also set a custom skin for the header or item. The only trick when using section lists is building the data provider because this list uses SectionDataProvider instead of DataProvider. Here is an example:

//create the data provider
var data:ISectionDataProvider = new SectionDataProvider();
//create 3 sections
var s1:Object = new Object();
s1.label = "Section 1";
var s2:Object = new Object();
s2.label = "Section 2";
var s3:Object = new Object();
s3.label = "Section 2";
//add the sections
data.addItem(s1);
data.addItem(s2);
data.addItem(s3);
//add items to each section
for (var i:int = 0; i < 5; i++) {
	data.addChildToItem({label: 1 + i}, s1);
	data.addChildToItem({label: 11 + i}, s2);
	data.addChildToItem({label: 21 + i}, s3);
}

As you see you have two steps: one for adding a section (data.addItem(section)) and one for adding an item to a section (data.addChildToItem(item, section)).

And here is an example of how to create a list and handle item selection:

var list:SectionList = new SectionList();
list.dataProvider = createSectionDataProvider();
list.selectionMode = ListSelectionMode.SINGLE;
list.width = 200;
list.height = 500;
list.headerHeight = 50;
list.rowHeight = 30;
list.setHeaderSkin(CustomHeader); //set a custom header skin
list.addEventListener(ListEvent.ITEM_CLICKED, onItem, false, 0, true);

private function onItem(e:ListEvent):void {
	trace(e.section);
	trace(e.index);
	var d:ISectionDataProvider = (e.target as SectionList).dataProvider as ISectionDataProvider;
	//retrieve the current selected section
	var tmp:Object = d.getItemAt(e.section);
	//retrieve the current selected item
	var item:Object = d.getChildInItemAt(tmp, e.index);
}

TileList

You use TileList to display more than one item on a row. You do so by using the columnCount property. Here is an example:

var list:TileList = new TileList();
//create a data provider with 1 to 10 numbers
list.dataProvider = createDataProvider();
list.width = 300;
list.height = 200;
//set 3 cells on a row
list.columnCount = 3;
list.cellPadding = 1;
list.selectionMode = ListSelectionMode.SINGLE;
list.addEventListener(ListEvent.ITEM_CLICKED, onItem, false, 0, true);

This code will render a list that looks like this:

SectionTileList

SectionTileList is a combination of TileList and SectionList – no big surprise is it? This means you create SectionDataProvider the same way as with SectionList and you use the columnCount property to set the number of items on a row as with TileList.

Here is an example:

var list:SectionTileList = new SectionTileList();
list.dataProvider = createSectionDataProvider();
list.columnCount = 3;
list.cellPadding = 1;
list.selectionMode = ListSelectionMode.SINGLE;
list.width = 300;
list.height = 500;
list.addEventListener(ListEvent.ITEM_CLICKED, onItem, false, 0, true);

private function onItem(e:ListEvent):void {
	trace(e.section);
	trace(e.index);
	var d:ISectionDataProvider = (e.target as SectionList).dataProvider as ISectionDataProvider;
	//retrieve the current selected section
	var tmp:Object = d.getItemAt(e.section);
	//retrieve the current selected item
	var item:Object = d.getChildInItemAt(tmp, e.index);
}

DropDown

DropDown list displays the currently selected item and when clicked, it expands and lets you scroll through the items and make a selection. Here you can see a DropDown in its normal and expanded state.

You use the rowCount property to set how many rows are displayed when the component is expanded. Code example:

var list:DropDown = new DropDown();
list.rowHeight = 45;
list.rowCount=3;
list.width = 250;
list.dataProvider = createDataProvider();
//3 events: Event.OPEN / CLOSE / SELECT
list.addEventListener(Event.SELECT, onDropDownSelection, false, 0, true);

private function onDropDownSelection(e:Event):void {
	trace((e.target as DropDown).selectedIndex);
}

PickerList

PickerList is used by the Picker component (though you can use it too). It offers the same behavior as the RoundList (you can’t reach the end of the list; the elements will be repeated forever). The difference is that by default, it displays the currently selected item, and only when pressed it opens and displays more items. In the expanded state it uses a mask with variable Alpha to give some sense of depth.

Code example:

var list:PickerList = new PickerList();
list.dataProvider = createDataProvider();
list.selectionMode = ListSelectionMode.SINGLE;
list.width = 50;
list.height = 40;
list.opaqueBackground = 0xFFFFFF;
//you can listen for Event.SELECT
list.addEventListener(ListEvent.ITEM_CLICKED, onSelected, false, 0, true);

Picker

A Picker uses a number of PickerList. Picker has two states: a compact one for displaying the current selection and an expanded one for making the selection. Here is an example with the two states:

It is quite useful for situations when you want to present related lists together; like in the picture above where you want to provide a way for setting the date. Or you want to provide a way for selecting the time.

As in the case of the SectionList/SectionTileList the trickiest part is to create the data provider. Basically for each PickerList you want you need a qnx.ui.data.DataProvider object. So, for creating the data provider used by the Picker (it uses three PickerList components) from the image above you’d do something like this:

private function createPickerDataProvider():IDataProvider {
	// Create three arrays to handle days, months, years.
	var day:Array = new Array();
	var month:Array = new Array();
	var year:Array = new Array();

	// Populate the month
	for (var i:int=1; i<32; i++) {
		day.push({label: i.toString()});
	}

	// Populate and month array and create the month
	month.push({label: "January"});
	month.push({label: "February"});
	month.push({label: "March"});
	month.push({label: "April"});
	month.push({label: "May"});
	month.push({label: "June"});
	month.push({label: "July"});
	month.push({label: "August"});
	month.push({label: "September"});
	month.push({label: "October"});
	month.push({label: "November"});
	month.push({label: "December"});

	// Create the year DataProvider
	for (var i:int=1950; i<2012; i++) {
		year.push({label: i.toString()});
	}

	// create the data provider
	var data:Array = new Array();
	data.push(new DataProvider(day));
	data.push(new DataProvider(month));
	data.push(new DataProvider(year));
	return new DataProvider(data);
}

You create a Picker like this:

var list:Picker = new Picker();
list.dataProvider = createPickerDataProvider();
list.width = 400;
list.height = 50;
//you can set the width for each section
list.setListWidth(0, 100);
list.setListWidth(1, 200);
list.setListWidth(2, 100);
list.selectedIndices = [1,1,45];
list.addEventListener(Event.SELECT, onPickerSelection, false, 0, true);
list.addEventListener(Event.OPEN, onPickerSelection, false, 0, true);
list.addEventListener(Event.CLOSE, onPickerSelection, false, 0, true);

private function onPickerSelection(e:Event):void {
       trace(e.type);
       if (e.type == Event.CLOSE) {
           var p:Picker = (e.target as Picker);
           for (var i:int = 0; i < p.selectedItems.length; i++) {
                trace("Picker.selectedItems " + p.selectedItems[i].label);
            }
            trace("Picker.selectedIndices " + p.selectedIndices);
            trace("Picker.data " + p.data);
       }
}

If you want to take an action when the user has finished making a selection, you should register an event listener for the Event.CLOSE event. This is triggered when the user taps outside the component boundaries and makes the component return to the compact state.

Skinning Lists

There aren’t many resources for skinning list components. And so far I haven’t played enough to talk about this. However, there is one thing I want to say about lists. In general they expect to be fed them with a data provider of objects that have a label property.

If you want to use an array of Strings as the source for the data provider you have to create a custom CellRenderer:

package org.corlan.renderers {
	import qnx.ui.listClasses.AlternatingCellRenderer;

	public class CellRenderer extends qnx.ui.listClasses.AlternatingCellRenderer {

		public function CellRenderer() {
			super();
		}

		override public function set data(value:Object):void {
			super.data = value;
			setLabel(value as String);
		}

	}
}

You can use this renderer like this:

var optionsArr:Array = ["item 1", "item 2", "item 3"];

var list:List = new List();
list.setSkin(CellRenderer);
list.dataProvider = new DataProvider(optionsArr);

Download

You can download a Flash Builder project with examples for all these components from here.

21 thoughts on “Working with PlayBook QNX List Components

  1. Pingback: Tweets that mention Working with PlayBook QNX List Components : Mihai Corlan -- Topsy.com

  2. Nice info … it’s just that no matter what I do I can’t get the downloaded project to look like in the screenshots. :)

  3. Pingback: Some Great BlackBerry PlayBook Blog Posts : Ryan Stewart – Mountaineer Coding

  4. Pingback: Some Great BlackBerry PlayBook Blog Posts (Adobe Flash Platform Blog)

  5. Mihai,

    Is there a way to create a Picker List component for FB 4.5 that would work on any Android app?

    Do you have suggestions on how to develop such a component?

  6. Gilbert,

    Of course it is possible :D You just have to created from scratch :). You would create the component like any other custom component. Maybe you can reuse the List component to have a quick start…

    There is no way to use the QNX component on Android as far as i know…

  7. Mihai,

    Yes. I was thinking on reusing the list component, but I am not sure of what I need to do.
    I am envisioning a component were the item that can be selected is in the middle (vertically) and the list ca be scrolled up and down…

    Can you provide some more details of how to do that?

  8. Pingback: Working with PlayBook QNX UI components : Mihai Corlan

  9. Pingback: Working with PlayBook QNX UI components (Adobe Flash Platform Blog)

  10. Hi Mihai,

    for the scrollpane you need to listen when image is ready and then you have to call update() method of scrollpane and everything will work ;)

    cheers dude

  11. Hello,

    This is a great article.
    Can you people help me out with picture scroll field in Playbook,

    I need to have a slideshow of all the images from which i can select one.

  12. Pingback: Extending QNX TileList: Liquid Tile List : Mihai Corlan

  13. Pingback: Extending QNX TileList: Liquid Tile List (Adobe Flash Platform Blog)

  14. Hello Mihai!
    A have a few questions about developing for PlayBook on AS.
    1) Can you recommend where I can read about customization of cells in List? I mean add image to cell, or button etc. What methods I need to override?
    2) How I can render PDF on Playbook? I found one way, used on AIR, but it’s work only if Reader is installed in system.

  15. Pingback: Creating a DropDown List for PlayBook Applications « NTG Blog

  16. Pingback: Creating a DropDown List for PlayBook Applications « NTG Blog

  17. “You can download a Flash Builder project with examples for all these components from here.”

    No you can’t It is ‘containers’.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>