Using MXML with QNX UI Components for the PlayBook
One of the less known facts about the Flex SDK and all its various parts is that MXML != Flex. MXML is a markup language that gets turned into ActionScript by mxmlc. With a little bit of knowledge of some metadata and willingness to build a bit of plumbing into a container class you can use MXML and Binding without Flex.
So what I am trying to do here?
I am trying to take a ActionScript 3 UI library and use MXML to describe its layout. More specifically I wanted to use the PlayBook’s native OS UI component library from the BlackBerry Tablet OS SDK, called the QNX UI components. Lets take a look at some code to show you what I am talking about.
Here is a MXML based app using a new class called QApplication
that extends QNX Container
, which then contains a QNX UI Button
:
<r:QApplication xmlns:r="http://ns.renaun.com/mxml/2010"
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:buttons="qnx.ui.buttons.*">
<buttons:Button />
</r:QApplication>
Doing this in ActionScript would look like this:
{
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import qnx.ui.buttons.Button;
import qnx.ui.core.Container;
public class DisplayButtonAS3 extends Sprite
{
public function DisplayButtonAS3()
{
stage.scaleMode = StageScaleMode.NO_SCALE;
stage.align = StageAlign.TOP_LEFT;
var container:Container = new Container();
var but:Button = new Button();
container.addChild(but);
addChild(container);
container.setSize(stage.stageWidth, stage.stageHeight);
}
}
}
All to create the same application seen here:
What does this mean?
This means you can create a PlayBook application all in MXML with no Flex. This also means you can use Flex 4 (including Flex Hero the latest SDK with mobile support) and mix in some QNX UI components. All you need is to add the QMXML.swc (compiled against Tablet OS SDK 0.9.2) and namespace xmlns:r="http://ns.renaun.com/mxml/2010"
to your project/application.
Now that sounds like the bomb! Well its still another UI library. So its not like the QNX UI Component library all of the sudden works just like the Flex Spark architecture. No, the QNX UI lib walks its own path and you will have to remember that when using it. Doesn’t mean its bad just be aware its not the same as Flex.
If you want to continue and play with the examples make sure to get the BlackBerry Tablet OS SDK from the BlackBerry developer site:
http://us.blackberry.com/developers/tablet/devresources.jsp
NOTE: I am using BlackBerry Tablet OS SDK 0.9.1 as of writing this.
Ok I get it, let me see some code!
First! All the code is available on github, go get it. The repository has a few Flash Builder projects in it. The main library project is called QMXML
. All you need to create your own project is the QMXML.swc (compiled against Tablet OS SDK 0.9.2) found in the bin folder or you can download it from here.
The other projects are sample projects called: NonFlexMXMLUsingQNX
and FlexAndQNX
. The NonFlexMXMLUsingQNX
project contains the DisplayButton
applications shown above, as well as another project showing a few more QNX UI classes nested in containers using QNX flow/containment properties.
The FlexAndQNX
application is a standard Flex 4 spark (non-mobile) application that uses a QNX Button
and List
along side a Flex LineSeries
data chart. Which made some sense as QNX UI library lacks charting at this point. Here is a view of the application running in the simulator:
Here is the source code for the FlexAndQNX
application which allows you mix QNX UI components directly in Flex.
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:r="http://ns.renaun.com/mxml/2010"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:buttons="qnx.ui.buttons.*" xmlns:listClasses="qnx.ui.listClasses.*"
width="1024" height="600"
backgroundColor="0xcccccc"
>
<s:layout>
<s:HorizontalLayout />
</s:layout>
<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import qnx.ui.core.ContainerFlow;
import qnx.ui.core.SizeUnit;
import qnx.ui.data.DataProvider;
import qnx.ui.events.ListEvent;
private var data:Array = [];
[Bindable]
private var chartData:ArrayCollection;
private function createData():void
{
data = [];
for (var i:int = 0; i < 50; i++)
data.push({label: "Point " + i, data: i+(Math.random()*30),
data2: i+60+(Math.random()*60),
data3: i+80+(Math.random()*120)});
lstData.dataProvider = new DataProvider(data);
chartData = new ArrayCollection(data);
}
protected function lstData_listItemClickedHandler(event:ListEvent):void
{
trace(event.data.data + " - " + event.row);
}
]]>
</fx:Script>
<fx:Declarations>
<!-- Define custom colors for use as fills in the AreaChart control. -->
<mx:SolidColor id="sc1" color="0x336699" alpha=".3"/>
<mx:SolidColor id="sc2" color="0x993333" alpha=".3"/>
<mx:SolidColor id="sc3" color="0x339933" alpha=".3"/>
<!-- Define custom Strokes. -->
<mx:SolidColorStroke id = "s1" color="0x336699" weight="2"/>
<mx:SolidColorStroke id = "s2" color="0x993333" weight="2"/>
<mx:SolidColorStroke id = "s3" color="0x339933" weight="2"/>
<mx:SeriesInterpolate id="seriesInterpolate" duration="500" />
</fx:Declarations>
<s:Group height="100%" width="20%">
<r:QContainer id="c" align="near">
<buttons:LabelButton label="Create Data" click="createData()" />
<listClasses:List id="lstData" size="100" sizeUnit="percent"
listItemClicked="lstData_listItemClickedHandler(event)" />
</r:QContainer>
</s:Group>
<s:HGroup width="80%" height="100%">
<mx:AreaChart id="Areachart" height="100%" width="80%"
paddingLeft="5" paddingRight="5"
showDataTips="true" dataProvider="{chartData}">
<mx:horizontalAxis>
<mx:CategoryAxis categoryField="label"/>
</mx:horizontalAxis>
<mx:series>
<mx:AreaSeries yField="data" form="curve" displayName="Plot 1" showDataEffect="{seriesInterpolate}"
areaStroke="{s1}" areaFill="{sc1}"/>
<mx:AreaSeries yField="data2" form="curve" displayName="Plot 2" showDataEffect="{seriesInterpolate}"
areaStroke="{s2}" areaFill="{sc2}"/>
<mx:AreaSeries yField="data3" form="curve" displayName="Plot 3" showDataEffect="{seriesInterpolate}"
areaStroke="{s3}" areaFill="{sc3}"/>
</mx:series>
</mx:AreaChart>
<mx:Legend dataProvider="{Areachart}"/>
</s:HGroup>
</s:Application>
There is probably more integration on the QContainer
class that I can do to make it work with Flex better but for now follow these loose rules. When mixing QNX UI components in a Flex app always start with a QContainer
, think of that as your little mini QNX app space. You will want to wrap this in a Flex Spark Group
or SkinnableContainer
with the notion of the QContainer
filling up all of its parents width and height. The QContainer
listens for its parents RESIZE
event and then invalidates its layout. Then based on the new width
and height
of its parent it remeasures itself and its children. There is some leaway for setting different QNX properties like size
and sizeUnit
, but for the safest bet start with:
<r:QContainer />
</s:Group>
Any changes to the dimensions of the Group will automatically be detected by the QContainer
and it will fill up the whole space of its parent.
NOTE: BlackBerry Tablet OS SDK does not provide a manifest file and namespace for their classes, which means your apps will have a lot of namespaces that look like qnx.ui.buttons.*
. Also some QNX classes might cause problems with Flex’s SystemManager control the stage, let me know if you come across something.
Project Types, SDKs, and Plugins
There are various projects types across Flash Builder 4 and Flash Builder Burrito. Also the BlackBerry Tablet OS SDK installs plugins for both Flash Builder versions. Typically all you will need is to create a AIR project and modify your application descriptor file for mobile devices (meaning typically non-WindowedApplication and set visible=true). The QMXML
classes will work in Flex 4 spark desktop apps as well as the newer Flex Hero mobile app and theme. This assumes your projects are created and point to the BlackBerry Tablet OS SDK 0.9.1.
Be aware the classes have had limited testing and would love you to take them for a ride. Have fun!
Geek tidbits from my journey
With MXML you get Binding. Which is one of the great benefits of MXML. But this also means I had to take a UI framework that didn’t take that in to consideration and tweak it to work. Some of the QNX UI framework choices made this not so straightforward. I had to work in a bunch of hooks to properties that might change the layout and create my own layout invalidation/refresh. Luckily the Container class is the only Container in QNX UI library and it only had a half dozen layout related properties. That made it easy to make this a viable solution.
There is still probably some issues with using width/height instead of the QNX’s size, sizeUnit, and sizeMode for flow layouts. But if you are using explicit width/height you probably are not invalidating the layout a bunch.
Also there where a few properties that didn’t implement getter/setters so I had to watch for changes on them differently to invalidate the layout on changes. Overall it wasn’t too complicated but could be improved. Having said all that I have to put a disclaimer that BlackBerry Tablet OS SDK is in beta and will improve, as well as my understanding how it works will change over time.
Pingback: @renaun posts: Using BlackBerry PaymentService with Adobe AIR for the PlayBook