In a previous post I discussed about all sort of lists available to developers when using the BlackBerry Tablet OS SDK for AIR. In this article I will focus on the remaining UI controls. I have an idea for a PlayBook application and before writing a single line of code I wanted to make sure I understand all the UI components that are available as part of the BlackBerry SDK (by the way, in case you didn’t know, RIM offers a free PlayBook device to any developer who gets his application on BlackBerry App World). And I thought it is worth sharing with you my little research.

I won’t talk about how to skin these components. This is a topic for another blog post. In the mean time you can read Ryan’s posts on skinning ( buttons/text fields and lists) and Renaun’s post.
Buttons
There are seven buttons in the package qnx.ui.buttons. When you want to do something when a buttons was clicked you have to register an event listener for the MouseEvent.CLICK, Event.CHANGE (only for SegmentedControl), or Event.SELECT (for ToggleSwitch):
myButton.addEventListener(MouseEvent.CLICK, doSomethingOnClick);
private function doSomethingOnClick(e:MouseEvent):void {
//do something
}
LabelButton and IconButton, as the names sugest, enables you to create buttons with either text labels or icons. Both these buttons extend Button class (if you use the Button class you will get a button without labe or icon). You can enable toggle by setting the the property name with the same name to true. This will give you a button that once clicked stays selected and then you can click again to unselect. In the image below you can see these two buttons with toggle on in selected and unselected state.

You can use a flash.Text.TextFormat object to change the label settings for a LabelButton. You set the image to be used by the IconButton by calling setIcon() method and passing the path to image to be used or a Bitmap/BitmapData instance. Here is the code used for rendering the example above:
var format:TextFormat = new TextFormat();
format.font = "BBAlpha Sans";
format.size = 22;
format.color = 0x990000;
format.align = TextFormatAlign.CENTER;
var btn:LabelButton = new LabelButton();
btn.size = 50;
btn.sizeMode = SizeUnit.PIXELS;
btn.label = "Label Button";
btn.toggle = true;
btn.setTextFormatForState(format,SkinStates.DISABLED);
btn.setTextFormatForState(format,SkinStates.UP);
btn.setTextFormatForState(format,SkinStates.DOWN);
btn.setTextFormatForState(format,SkinStates.SELECTED);
btn.setTextFormatForState(format,SkinStates.DISABLED_SELECTED);
container.addChild(btn);
var btn2:IconButton = new IconButton();
btn2.size = 100;
btn2.sizeMode = SizeUnit.PIXELS;
btn2.setIcon("assets/icon.png");
btn2.toggle = true;
container.addChild(btn2);
BackButton extends the LabelButton and you can set a label if you want (the default label is Back). Also has a arrow icon to the left of the button. Like in the case of LabelButton you can use a TextFormat object and played with the toggle property.

var btn3:BackButton = new BackButton();
btn3.label = "Go Back";
container.addChild(btn3);
CheckBox button extends the LabelButton too and has the toggle property set to true (you can’t change this one). However, you can control where to display the text relative to the check sign by using labelPlacement property. Using labelPadding property you can set the space between the label and icon. And of course TextFormat can be used to customize the label look. You have to pay attention to the width property if you don’t want to have the label cropped.

var cb:CheckBox = new CheckBox();
cb.label ="Select Me";
cb.width = 150;
cb.labelPlacement = LabelPlacement.RIGHT;
cb.labelPadding = 30;
container.addChild(cb);
The last component that extends the LabelButton is RadioButton. You use this component to force the user make a single selection from a group of at least two radio buttons. You can use TextFormat to custom the look of the label. You assign a radio button to a group by setting the same string to the groupname property. If you want to have one option preselected, you set the selected property to true.

var rb1:RadioButton = new RadioButton();
rb1.size = 30;
rb1.sizeUnit = SizeUnit.PIXELS;
rb1.groupname = "group1";
rb1.label = "Yes";
rb1.selected = true;
container.addChild(rb1);
var rb2:RadioButton = new RadioButton();
rb2.size = 30;
rb2.sizeUnit = SizeUnit.PIXELS;
rb2.groupname = "group1";
rb2.label = "No";
container.addChild(rb2);
SegmentedControl displays a series of connected radio buttons and the user can select only one of them. Like in the case of the lists you use a DataProvider to add the labels you want. There is an alternative to add a label one by one using the addItem() or addItemAt() methods. Each label is set using an instance of Object with one property named label. You can preselect an option using the selectedIndex property and you listen for user selection by registering an event listener for the flash.events.Event.CHANGE event. You can skin this component by setting up a new skin for the background and buttons.

var segData:Array= [
{label:"< 10"},
{label:"10-19"},
{label:"> 20"}
];
var seg:SegmentedControl = new SegmentedControl();
seg.dataProvider = new DataProvider(segData);
seg.selectedIndex = 1;
container.addChild(seg);
The last button is the ToggleSwitch – this component extends the Slider component. This ones enables you to create a button with two labels and slider that always hides one of this two labels. To set the label from the left you use selectedLabel property, and for the one on the right you use defaultLabel property. You can control the default selection by setting selected property to true/false and you react to component changes by listening for Event.SELECT events. You can see in the image below the component using the default skin in the two states:

var tog:ToggleSwitch = new ToggleSwitch();
tog.selectedLabel = "On";
tog.defaultLabel = "Off";
tog.selected = true;
container.addChild(tog);
Text Controls
In the package qnx.ui.text you’ll find two UI components: Label and TextInput. You can use the Label component for displaying text (can’t receive focus). There are a number of properties you can use in order to fine control how the text will be displayed:
- By default a label has 100 pixels width. If you don’t set the autoSize property or adjust the width, then it is quite possible to not see the whole text. The easiest way is to use the autoSize property and set how the label should grow so the text will be fitted: label.autoSize = TextFieldAutoSize.CENTER;
- If you want to set the font family name/size/type-face/color you use an instance of TextFormat to set these things and then you assign it to the label’s format property
- You set the text you want to be displayed using either the text property or htmlText property. After playing with the htmlText property it seems that the main benefit is that you can throw some HTML formatted text and it will be displayed just like text – this means you want see the tags in the label.
- When you set the multiline property to true, and display a HTML text like this <p>one</p><br/><p>two</p> then you will see the text on two paragraphs and with one extra line between them
- selectable property when set to true allows user to select the text
- You can use the wordWrap property to allow wrapping the words so the text can be displayed without increasing the width of the label
Here is a code example and its rendering:
var format:TextFormat = new TextFormat();
format.font = "BBAlpha Sans";
format.bold = true;
format.size = 19;
format.color = 0x000099;
var lbl:Label = new Label();
lbl.format = format;
lbl.autoSize = TextFieldAutoSize.CENTER;
lbl.wordWrap = true;
lbl.multiline = true;
lbl.selectable = true;
lbl.htmlText =
"<p><strong>This is a label!!!</strong></p><br/><p>Second Paragraph</p>";
container.addChild(lbl);

If you want to let the user introduce some text or edit it, then you’ll use the TextInput component. This is component is pretty powerful because it lets you add custom icons inside of it (on the left/right sides) and control when to be displayed. You can also control what type of the keyboard should be displayed when the text field gets the focus (the keyboard is pulled out automatically when a TextInput is selected). Just set the keyboardType property to one of the following values: KeyboardType.DEFAULT, KeyboardType.EMAIL, KeyboardType.URL, KeyboardType.PIN, or KeyboardType.PHONE. You can customize the label displayed on the Enter key by using the returnKeyType property: txt.returnKeyType = ReturnKeyType.GO. If you want to use the text field for passwords you can set the displaysAsPassword property to true.

When you want to format the text you use a TextFormat object exactly like in the case of the Label. By default you can have a Clear icon inside of the text field you can use for deleting the whole text. You can control when to be displayed using the clearIconMode property: txt.clearIconMode = TextInputIconMode.ALWAYS. If you want to add custom icon to the left or right side, one way to do it is to create a transparent PNG file with the icon you want and then embed the PNG file and set the class to the leftIcon or rightIcon properties. From my tests it seems that is better to have the icon rather bigger than smaller compared to what will be actually rendered. And you can control when to be displayed using leftIconMode or rightIconMode properties. For example:
[Embed(source="assets/arrow48x48.png")]
private var ArrowIcon:Class;
...
txt.leftIcon = new ArrowIcon();
txt.leftIconMode = TextInputIconMode.UNLESS_EDITING;

Here is the code used to create the example displayed in the pictures:
var format:TextFormat = new TextFormat();
format.font = "BBAlpha Sans";
format.bold = true;
format.size = 19;
format.color = 0x000099;
var txt:TextInput = new TextInput();
txt.format = format;
txt.width = 400;
txt.keyboardType = KeyboardType.PHONE;
txt.returnKeyType = ReturnKeyType.GO;
txt.clearIconMode = TextInputIconMode.ALWAYS;
txt.leftIcon = new ArrowIcon();
txt.leftIconMode = TextInputIconMode.UNLESS_EDITING;
If you want to do something when the user introduces or edits the text, then you have to register an event listener for Event.CHANGE event:
txt.addEventListener(Event.CHANGE, onChange);
Here are the shortcomings I found so far for the TextInput (as of writing this post the SDK is in beta):
- You can’t test it outside of the Emulator without a hack – see Renaun’s post on this topic. This is happening because this component requires a class that is part of the RIM’s AIR runtime.
- When setting the autoSize property to TextFieldAutoSize.CENTER you can’t select the field and type in.
- It doesn’t support multilines. Some people found a way to get around this, but I think we need official support from RIM in a form of a different component or an addition to the TextInput.
Sliders
In the package qnx.ui.slider you’ll find a Slider and VolumeSlider components. VolumeSlider extends the Slider and provides a default skin suited for media applications. You can set the minimum and maximum values using the properties with the same name. You can’t set the step when moving the thumb around. You can retrieve the current value using the value property and keep in mind it is a Number and not Integer (so you can get 6.7 for example). If you want to have the slider’s thumb position to something else than the left side by default, you set the value property to what you need.
var slider:Slider = new Slider();
slider.width = 300;
slider.height = 40;
slider.minimum = 1;
slider.maximum = 10;
slider.value = 5.5;
slider.addEventListener(SliderEvent.START, onDragStart);
slider.addEventListener(SliderEvent.END, onDragEnd);
slider.addEventListener(SliderEvent.MOVE, onMove);
var volume:VolumeSlider = new VolumeSlider();
volume.minimum = 0;
volume.maximum = 1;
While you can set the width of the component, setting the height doesn’t change anything. You need to create skins for thumb and track if you need more customization and use setThumbSkin() and setTrackSin() methods to apply them.

There are three events you can listen for: SliderEvent.START (when the thumb starts to move), SliderEvent.END (when you finished the dragging), and SliderEvent.MOVE (while the thumb is moving). It seems that both these sliders can be only horizontal.
Progress Bars
When you want to display progress to the user you can use components available in the qnx.ui.progress package. The ActivityIndicator component lets you display a visual feedback when a time consuming operation is executing. You start the animation by calling the animate(true) method and stop it with animate(false). You can check if the animation is running by calling isAnimating() method. Should be possible to skin by extending ActivityIndicatorSkin.

var activity:ActivityIndicator = new ActivityIndicator();
activity.setSize(30, 30);
activity.animate(true);
Using the ProgressBar and PercentageBar you can give a sense of progress, of how much of the work has been completed and how much is remaining. You change this by setting the progress property to a number between 0 and 1 where 1 represents 100% of the job done. PercentageBar gives you a label as well, so the user knows exactly where he is – you can hide it by setting showPercent property to false.

var prog:ProgressBar = new ProgressBar();
prog.width = 200;
prog.progress = 0.3;
var per:PercentageBar = new PercentageBar();
per.width = 200;
per.progress = 0.9;
Extending the skins from the qnx.ui.skins.progress package is the way to customize the look and feel.
Image Controls
In the qnx.ui.display you’ll find two components: Image and TilingBackground. You use Image class when you want to display an image. By calling the setImage() method you set a local path to an image, an URL, or a Bimap/BitmapData object. There are two events you can listen for:
- Event.COMPLETE – this event is triggered once the image was loaded and ready to use
- IOErrorEvent.IO_ERROR – dispatched when the image can’t be loaded
If you plan to scale the image (up or down) you might want to set the smoothing property to true. You can cache the images by creating a single instance of ImageCache and assign it to Image.cache property. You can force the cache to reload an image if you need.
Here is an example that loads an image from the application folder:
private static var imageCache:ImageCache = new ImageCache();
private static const IMAGE_PATH:String = "assets/picture.jpg";
...
var img:Image = new Image();
img.smoothing = true;
img.cache = imageCache;
img.addEventListener(Event.COMPLETE, onComplete);
img.setImage(IMAGE_PATH);
container.addChild(img);
...
private function onComplete(e:Event):void {
container.layout();
}

Please note that in the above example I didn’t explicitly set the width and height of the image. These were set automatically to the values taken from the loaded image. If you want to set them to something different you have to do it when the image was loaded and the Event.COMPLETE event was thrown. Otherwise they will be overwritten.
When you want to create a tile from images, you use the TilingBackground component. You don’t have much control on how image will be tiled (it will be tiled on both X and Y depending on how much space it is available and how large the image to be tiled is). What was occurred to me was the way you set the image. I was expecting to have the same options available like for Image.setImage(). You use bitmapData property and set to a BitmapData object. In the following example I’m using the ImageCache.getImage() API to get the BitmapData object loaded by the Image object and set it to the bitmapData property of the TilingBackground:
tile = new TilingBackground();
tile.containment = Containment.BACKGROUND;
container.addChild(tile);
//inside the complete event handler of the Image
tile.bitmapData = imageCache.getImage(IMAGE_PATH, false);

Dialog
QNX UI library provides a number of classes for creating a floating window on top of your application. I think that it is a nice feature, because with the real estate available on the PlayBook tablet you may want to use one of these dialogs instead of “pushing” an entirely new screen (screen metaphor approach).
There are five dialog boxes to choose from, and you have to remember that the appearance is controlled by the BlackBerry Tablet OS. You can control the position where the window will be placed and the size as well the type of the window: system modal (blocks the entire tablet until the window is closed – Dialog.open() ) or application modal (blocks the interaction only with the application – Dialog.open(IowWindow.getAirWindow().group) ). Using the DialogAlign and the align property of the dialog you can specify where on the vertical should be placed (on horizontal it is always centered). You can add buttons and set properties to them like this:
var myDialog:AlertDialog = new AlertDialog();
myDialog.addButton("Yes");
myDialog.addButton("No");
myDialog.addButton("Cancel");
myDialog.setButtonPropertyAt("enabled", false, 2);
Once the user clicks on one of the buttons you add to the window, if you registered an event listener on the dialog for the Event.SELECT event you can check example on what button he pressed:
myDialog.addEventListener(Event.SELECT, alertButtonClicked);
...
private function alertButtonClicked(e:Event):void {
trace((e.target as AlertDialog).selectedIndex)
}
The live cycle for a dialog window is:
- You create a new instance of the dialog you want
- You configure the content (messages, buttons, labels…) and the positioning/sizing of the dialog
- You register an event listener for the Event.SELECT event
- You call the show() method on the dialog to display it
AlertDialog is the simplest window. You use this window in order to display some text to the user and allow him to click on one of the buttons you add to it. You set the title and message using the properties with the same names.

And here is the code:
var myDialog:AlertDialog = new AlertDialog();
myDialog.title = "AlertDialog";
myDialog.message = "This is an alert dialog. Do you like it?";
myDialog.addButton("Yes");
myDialog.addButton("No");
myDialog.addButton("Cancel");
myDialog.setButtonPropertyAt("enabled", false, 2);
myDialog.dialogSize = DialogSize.SIZE_MEDIUM;
myDialog.align = DialogAlign.TOP;
myDialog.addEventListener(Event.SELECT, alertButtonClicked1);
myDialog.show(IowWindow.getAirWindow().group);
...
private function alertButtonClicked1(e:Event):void {
trace((e.target as AlertDialog).selectedIndex)
}
LoginDialog presents the user an interface for providing the information necessary for a login operation: user name, password, remember me. It extends the AlertDialog class so everything you can do with it applies to LoginDialog too. Using the rememberMe, password, and username properties you can retrieve the information the user introduced. You can set the texts used for username/password/remember me using these properties: usernameLabel, passwordLabel, passwordPrompt, and rememberMeLabel.

var login:LoginDialog = new LoginDialog();
login.title = "Login";
login.message = "Please enter your username and password:";
login.addButton("OK");
login.addButton("Cancel");
login.passwordPrompt = "password";
login.rememberMeLabel = "Remember me";
login.rememberMe = true;
login.dialogSize = DialogSize.SIZE_SMALL;
login.addEventListener(Event.SELECT, alertButtonClicked2);
login.show(IowWindow.getAirWindow().group);
...
private function alertButtonClicked2(e:Event):void {
var login:LoginDialog = e.target as LoginDialog;
trace(login.password); //retrieve the password
trace(login.username); //retrieve the username
//retrieve whather the remember me option was selected
trace(login.rememberMe);
trace(login.selectedIndex); //retrieve the button was clicked
}
PasswordChangeDialog, as the name suggests, is handy when you want to let the user change his password. It extends the LoginDialog. As with its superclass, you can customize the labels of the predefined UI components.

Here is the code to create the dialog from above:
var password:PasswordChangeDialog = new PasswordChangeDialog();
password.title = "Password Change";
password.message = "Please choose your new password:";
password.addButton("OK");
password.addButton("Cancel");
password.dialogSize= DialogSize.SIZE_MEDIUM;
password.addEventListener(Event.SELECT, alertButtonClicked3);
password.show(IowWindow.getAirWindow().group);
And here is how you retrieve the user input:
private function alertButtonClicked3(e:Event):void {
var login:PasswordChangeDialog = e.target as PasswordChangeDialog;
trace(login.password); //retrieve the old password
trace(login.username); //retrieve the username
trace(login.newPassword); //retrieve the new password
trace(login.confirmation); //retrieve the confirmation for the new password
trace(login.selectedIndex); //retrieve the button was clicked
}
The PromptDialog extends AlertDialog and provides a way to capture user text input. You can set the hint to be used by the TextField using the prompt property. And you can retrieve the text entered using the text property.

Here is the code:
var prompt:PromptDialog = new PromptDialog();
prompt.title = "Choose your username!";
prompt.message = "Please enter an username between 6-10 chars:";
prompt.prompt = "username";
prompt.addButton("OK");
prompt.addButton("Cancel");
prompt.dialogSize= DialogSize.SIZE_SMALL;
prompt.addEventListener(Event.SELECT, alertButtonClicked4);
prompt.show(IowWindow.getAirWindow().group);
...
private function alertButtonClicked4(e:Event):void {
var d:PromptDialog = e.target as PromptDialog;
//retrieve the text introduced by the user
trace(d.text);
}
The last dialog window is the PopupListcomponent. This dialog creates a window that presents a scrollable list and the user can select either a single entry or multiple entries. You set the items that will be rendered in the list by assigning an Array of strings to the items property. If you want to allow multiple selection then you set multiSelect property to true. If you want to preselect an item or a number of items, just set the selectedIndices property to an array of indices of the items you want selected.

Here is the code:
var popUp:PopupList = new PopupList();
popUp.title = "Chose your title:";
popUp.items = ["Mr.", "Ms.", "Miss", "Dr.", "Phd"];
//allow multiple selection
popUp.multiSelect = true;
//preselect the second item
popUp.selectedIndices = [1];
popUp.addButton("OK");
popUp.addButton("Cancel");
popUp.dialogSize= DialogSize.SIZE_SMALL;
popUp.addEventListener(Event.SELECT, alertButtonClicked5);
popUp.show(IowWindow.getAirWindow().group);
...
private function alertButtonClicked5(e:Event):void {
var d:PopupList = e.target as PopupList;
//retrieve the selected index/indices
trace(d.selectedIndex);
trace(d.selectedIndices.toString());
}
One last note on dialog components: it doesn’t seem you can skin them or extend. It would have been nice to be able to create custom dialog with the components you want in them. On the other hand they really cover most use cases you might think of.
Download
If you want to have all the code used for this article, just download this ActionScript mobile project for Flash Builder 4.5 (you’ll need the PlayBook simulator and the BlackBerry plug-in for Flash Builder installed in order to run and compile the code).
Conclusions
These are not all the components available in the QNX libraries. Though I feel that with this post and the previous one on lists I covered the vast majority. Check Ryan‘s and Renaun‘s blogs for articles on Containers and playing multimedia.
I have to say that I really think that RIM did an amazing job with this QNX UI library. I thing there is pretty much all you need to create great applications. Especially the list components seemed to be almost complete and the Dialog components are a nice touch especially for tablet devices (though I’m not sure how well they will perform on smartphone screens).
In general everything seems to be well engineered and design. The biggest caveat of all this is the fact we don’t have the library source code. Any Flex developer will tell you how important is the fact that the Flex framework is open source and you can inspect all the code you wish. It’s great when learning the framework and it is definitely great when debugging. Right now even a process that shouldn’t be that complicated (skinning) is a try and error process.
What do you think?
