Using MXML with QNX UI Components for the PlayBook

Posted on December 5, 2010 | 12 comments

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:

<?xml version="1.0" encoding="utf-8"?>
<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:

package
{
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:

DisplayButton Example

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:

FlexAndQNX in Simulator

Here is the source code for the FlexAndQNX application which allows you mix QNX UI components directly in Flex.

<?xml version="1.0" encoding="utf-8"?>
<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:

<s:Group height="100%" width="20%">
    <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.

  • vijayta

    nice one.
    can you just post code of how to use tree control inQNX?
    it will be helpful.

    • http://www.renaun.com Renaun Erickson

      Using Flex components in QNX is not compatible and is not worth the effort to even attempt. If you want to use a Tree component start with a Flex app and put QNX UI components in it around the Tree.

  • Sven Dens

    Very interesting stuff here Renaun, congratulations!

    It seems to me that your approach would also allow functional & UI unit testing – through FlexUnit and FlexMonkey (http://www.gorillalogic.com/flexmonkey) – while building a QNX app. They both understand MXML and AS3, and since your QContainer encapsulates the QNX components, allowing us to address the QNX components as children, it would seem reasonable to believe this would allow us to unit-test logic and behaviour of those QNX components?

    To take it one step further, I believe this would also allow us to build hybrid applications that can use “native” UI components where available (QNX on PlayBook, Android UI components/widgets on Android), and fall back on Flex/pure AS3 components on standard desktops or Nokia mobiles with Flash but without a component lib, by using an IoC container like spring-actionscript (http://www.springactionscript.org/).
    If someone were to write an “AContainer” or “SContainer” that does the same for Android/Symbian like QContainer does for BBOS, we could use dependency injection to write generic code that will run on all those OS’es (including standard desktop Lin/Win/Mac), yet have a different, natural “look&feel” depending on the platform you’re running the app on.

    Unfortunately I’m not part of the “cool kids” yet and so still have to run all my playground code in emulators, but I guess most mobile/tablet addicts would be delighted to have such apps running on ALL their devices and behaving exactly as they’d expect. What do you think?

    Hell, with Apple finally getting a bit more relaxed on allowing Flash Platform software to run on their iDevices, it seems imaginable we could soon write code that really targets EVERY platform, and does so in a natural, smooth, enjoyable, clean & stable fashion.

    0/ awesomeness++ 0/

    Would love to hear your thoughts.

    Kind regards,
    Sven

    • http://www.renaun.com Renaun Erickson

      Functional test using the Automation framework that has hooks into the Flex framework components, so it will not work with QNX with out QNX UI components implementing a bunch of automation code.

      The reason i called QNX UI components native is because the PlayBook native UI is in AIR. So this doesn’t not work for other platforms, ie: native Android as native Android UI is java not AIR.

  • Sven Dens

    Forgot to ask: can your QContainer capture events dispatched by QNX comps?
    And can it read/write QNX comps properties?
    Or is it just one-way traffic for the moment, from QContainer => QNX?

    • http://www.renaun.com Renaun Erickson

      The QContainer extends QNX’s Container so it can do everything that Container does.

  • http://andymatthews.net andy matthews

    You’re the man Renaun! This might just be the last piece I need to start building my application. Can’t wait to try it out this week!

  • http://flexr.wordpress.com Joseph

    Thanks Renaun.

    I am trying to do the same, with Flex 4.5 (Burrito), and the BlackBerry 0.9.1 SDK.

    Are you debugging in the VMWare or locally? Cause, I can run the App locally, not sure why I would need to use the VMWare (even though its installed – but takes 10x the time to load the app, sometimes even not load).

    So, I am able to run it locally, and I would like to use the qnx AlertDialog, which I don’t seem to have access to any of the qnx.

    Would it be possible to send you the fxp file, it’s really small & basic… I’m just trying to wrap my head around it.

  • http://flexr.wordpress.com Joseph

    Actually, I think I fixed it, I just added the qnx-screen.swc to my Lib Path.

    Though when I debug (locally again) I get in the consol (when the Dialog fires):
    BaseDialog.constructor
    BaseDialog property object Renew
    BaseDialog property object Cancel

    I’m assuming this is fine, though it’s not rendered on the the local environment.

    Even if I test it on the VMware simulator of the Blackberry… The dialog dosen’t fire either…

    Thanks though… I got what I wanted running.

  • Chyarly Peeters

    Is it possible that the design mode still fails ?

    • http://www.renaun.com Renaun Erickson

      Design view still fails, it’s something to do with the BlackBerry Tablet OS swc’s.

  • Louis Lu

    The code is much cleaner for layout with MXML than AS3, I have managed to replicate the Container example at http://www.blackberry.com/developers/docs/airapi/1.0.0/index.html with the code I posted on the thread here:
    http://supportforums.blackberry.com/t5/Tablet-OS-SDK-for-Adobe-AIR/Using-QNX-components-in-MXML/td-p/662409/page/2

  • Pingback: @renaun posts: Using BlackBerry PaymentService with Adobe AIR for the PlayBook