php[architect] logo

Want to check out an issue? Sign up to receive a special offer.

Mobile Dashboards Made Easy – Part 2

Posted by on September 8, 2011

In the first installment of this article, I showed how a Flex-based front end can easily connect to a PHP backend; this is particularly true if the backend was designed for this kind of operation in the first place, but Cal Evans is going to show you how you can build an API around any existing system in Part 3.

For now, let’s focus on the user experience that our dashboard will provide. The first thing to keep in mind that is that we’re targeting platforms that are quite different: Flash can support iOS (which is itself broken down into iPads and iPhones), Android and QNX, which powers RIM’s Playbook.

As it so happens, we use all three categories of devices at Blue Parabola—which means that instead of endlessly fighting over which one to support, we wrote a simple app that works on every platform.

There are a couple of considerations to make here. The first is that these devices have very diverse sizes and aspect ratios. Therefore, our app needs to adapt to each configuration—possibly with as little intervention on our part as possible. The second is that mobile users often expect the layout of their apps to adapt dynamically to the orientation of their device.

In our case, the needs of our mobile dashboard are quite simple; there are, after all, only so many ways in which one can display sales information. One thing that is worth noticing, however, is the fact that this information also lends itself to being displayed visually—for example, through a nice pie chart.

Thus, our application will display a list of sales figures when in landscape mode and a pie chart that illustrates the various categories of sales.

Getting Started

The first thing that we need to do is, obviously, create the project. This is actually fairly simple, as the latest version of Flash Builder (4.5.1 as of this writing) features a mobile project template that can simultaneously be compiled against all three supported platforms.

Given that we have the ability to display results according to their time period (day, week and month), our best choice here is a “Tabbed Application,” which allows us to navigate through multiple tabs.

The new project wizard generates a new TabbedViewNavigatorApplication container, complete with the three tabs:

<s:ViewNavigator label="Today"
                 width="100%" height="100%"
                 firstView="views.TodayView"
                 firstViewData="{todayData}" />
<s:ViewNavigator label="Week"
                 width="100%" height="100%"
                 firstView="views.TodayView"
                 firstViewData="{weekData}"/>
<s:ViewNavigator label="Month"
                 width="100%" height="100%"
                 firstView="views.TodayView"
                 firstViewData="{monthData}"/>

There are a couple of things to note here. The first is that Flash Builder will normally create individual views for each of the tabs on the expectation that each tab contains different information. In our case, the information displayed is always the same (the only thing that changes is the time period), so we simply recycle the same view three times.

Additionally, the ViewNavigator will create its own instance of our initial view, which means that whatever data this uses must either be generated internally or passed by using the firstViewData parameter. In our case, it makes more sense to do the latter, which means that we need to generate the proper date periods that signify “today,” “this week,” and “this month.”

There are a number of ways to do so; our backend automatically sets the time portion of the start date to midnight and that of the end date to 23:59:59. Therefore, we can generate these date periods with a little bit of ActionScript code:

protected function handleCreationComplete(event:FlexEvent):void {
   var startDate:Date;
   var endDate:Date;

   startDate = new Date(todayData.startDate);
   startDate.date -= startDate.day;

   endDate = new Date(startDate);
   endDate.time += 86400000 * 7 - 1;

   weekData = {
      startDate: startDate,
      endDate: endDate
   };

   startDate = new Date(todayData.startDate);
   startDate.date -= startDate.date - 1;

   endDate = new Date(startDate);
   endDate.month += 1;
   endDate.time -= 1;

   monthData = {
      startDate: startDate,
      endDate: endDate
   };
}

Nothing special here. Note, however, that ActionScript’s date functionality is quite different from that of PHP. On one hand, it doesn’t quite have the flexibility of functions like strtotime(); on the other, the fact that dates are expressed as an object does make it easy to manipulate their components by adding to or subtracting from them, which in this case, is quite handy.

Handling Device Orientations

Onto the view itself. Since we want to display different content based on the orientation of the screen, we need to figure out when the user rotates their devices. That turns out to be quite easy; when the view is added to the stage, we simply add an event handler to Stage that gets notified whenever the orientation changes:

isLandscape = (stage.orientation == StageOrientation.ROTATED_LEFT ||
               stage.orientation == StageOrientation.ROTATED_RIGHT);

stage.addEventListener(StageOrientationEvent.ORIENTATION_CHANGE,
   function(e:StageOrientationEvent):void {
      var stage:Stage = e.target as Stage;

      isLandscape = (stage.orientation == StageOrientation.ROTATED_LEFT ||
                     stage.orientation == StageOrientation.ROTATED_RIGHT);
});

As you can imagine, isLandscape is a protected property of the view; as a result, all it takes to show or hide a particular element based on orientation is to use its visible and includeInLayout properties to check if isLandscape is true or false.

Segmenting the Results

Since our results are segmented by product, category and country, we might as well have our interface reflect the same breakdown. We can do this, for example, by displaying a segmented control (called ButtonBar in Flex) that allows the user to switch between the available screens:

<fx:Declarations>
   <s:ArrayCollection id="buttons">
      <fx:String>SKU</fx:String>
      <fx:String>Category</fx:String>
      <fx:String>Country</fx:String>
   </s:ArrayCollection>
</fx:Declarations>

<s:ButtonBar width="100%" selectedIndex="0"
          id="buttonBar"
          requireSelection="true"
          dataProvider="{buttons}"/>

Here, the ArrayCollection serves as a data provider to the ButtonBar, and that’s where we store the names of the various buttons. From this point on, we can track the selectedIndex property of the ButtonBar to determine which result set must be displayed.

Note also that, here, I have set the requiredSelection property to true to ensure that the user has at least one selected button at all times, since leaving all the buttons unselected would cause the application to be in an inconsistent state.

Displaying the Results

The results returned by a call to our backend can be displayed very easily using a tabular approach—which is exactly what the List component is designed to do. Each row in the table is drawn by an item renderer—essentially just an object that the list component recycles throughout its life to display each row.

Flash comes with a number of different item renderers that have been optimized for mobile display, and the one we’re going to use is called IconItemRenderer. Despite its name, we’re not going to use any icons—but we are going to take advantage of its ability to display both a primary and a secondary label: we’ll use the former to display the name of the line item, and the latter to display revenue and sales:

<s:List
    width="100%" height="100%"
    visible="{buttonBar.selectedIndex == 0 &amp;&amp; isLandscape}"
    includeInLayout="{buttonBar.selectedIndex == 0 &amp;&amp; isLandscape}"
    dataProvider="{salesData.resultsBySKU}">

    <s:itemRenderer>
        <fx:Component>
            <s:IconItemRenderer
                labelField="name" fontSize="14"
                messageField="description" messageStyleName="message" />
        </fx:Component>
    </s:itemRenderer>
</s:List>

It would be entirely possible to create our own item renderer—after all, it’s basically the same as building most other MXML components, although Adobe recommends that mobile item renderers be built in pure ActionScript for performance reasons. However, the built-in renderers, which are themselves quite similar to the ones that most mobile platforms provide, work just fine for most uses, particularly if you consider that you have nearly-complete freedom to alter their styling.

Visualizing the Data

What about the charts? These are usually not easy to generate and are even less easy to generate in a cross-platform way. We could resort to some server-side generation component, but the good news is that Flex actually comes with a rather rich set of charting components built in, making the generation of a chart as easy as this:

<mx:PieChart
    id="CountryChart"
    visible="{buttonBar.selectedIndex == 2 &amp;&amp; !isLandscape}"
    includeInLayout="{buttonBar.selectedIndex == 2 &amp;&amp; !isLandscape}"
    dataProvider="{salesData.resultsByCountry}"
    width="100%" height="100%">
    <mx:series>
        <mx:PieSeries
            displayName="Revenue"
            nameField="name"
            field="total"/>
    </mx:series>
</mx:PieChart>
<mx:Legend
    visible="{buttonBar.selectedIndex == 2 &amp;&amp; !isLandscape}"
    includeInLayout="{buttonBar.selectedIndex == 2 &amp;&amp; !isLandscape}"
    dataProvider="{CountryChart}"/>

As you can see, the visible and includeInLayout properties can be used to determine when either a chart or a table is visible. Here, I took advantage of the ability to write ActionScript expressions directly into the MXML code to decide whether to display a particular object depending on orientation and the user’s current selection.

Where to Go From Here?

There are a few things that I really like about this solution. Given our constraints (quick turnaround and cross-platform support) this turned out to be a good balance between features and requirements, particularly considering that the application is for our internal use. Considering the alternatives – either writing individual versions for each platform or using a web-based solution, which we didn’t want to do for a variety of reasons – the end result is quite good.

Naturally, I ended up having to write comparatively little actual code—Flash’s component set is rich enough in features that this is true most of the time. Since the underlying framework is open source, it’s normally also easy to extend the built-in components to add whatever functionality you need (despite Adobe’s deplorable insistence on using private methods and properties which makes extension sometimes difficult).

Alas, this solution is still tied to our backend, which was designed from the get go for the kind of uncoupled operation that makes Flash a very advantageous choice for front ends where the audience is captive and portability is essential. In the next two installments, my colleague Cal Evans will take over and show you how the same functionality can be built (and debugged) onto an existing system that uses Zend Framework.


Marco is the keeper of keys and Chief Garbage Collector at Blue Parabola. He can be found on Twitter as @mtabini.

 

Responses and Pingbacks

[…] Mobile Dashboards Made Easy – Part 2 […]

Leave a comment

Use the form below to leave a comment: