DLG Software


Application development

  with JavaScript, Flex, and AIR


This post is just a dumping ground for some tips on developing AIR apps for Android, things that might save you some time.

AIR mobile dev tips

This collection of Flex/AIR mobile dev tips emphasizes AIR on Android (most also pertain to iOS but I haven't verified them on an Apple device yet). Most of these tips assume you're familiar with the basics of AIR mobile development. For example, I'll talk about optimizing the use of ViewMenu without describing what ViewMenu is.


Here are links to individual tips:

ActionBar: creating/modifying a global ActionBar

This tip addresses the ActionBar - how to define a global ActionBar and how to create view-specific overrides to that global ActionBar.

An AIR mobile ActionBar is composed of 3 areas:

  • navigationContent: left side of Actionbar; generally for things like a Home button, Back button for iOS, etc.)
  • titleContent: middle of ActionBar; often used for a view title, but you can put other comps here, e.g., a search TextInput field
  • actionContent: right side of ActionBar; where you often put buttons for accessing app options

It can be useful to create global ActionBar content – for example, create a home button for the navigationContent area and have it appear in all your views.


Creating global ActionBar content is simple to do: just define it in your application's root class (i.e., the class based on ViewNavigatorApplication or TabbedViewNavigatorApplication) and it will appear on the ActionBar of all views. BTW, you generally shouldn't define UI in the root view, but ActionBar content is a valid exception to this rule.


Here's an example of creating global actionContent. In this example the binding to navigator.length>1 hides the buttons when the home view is being displayed.


<s:actionContent>    
    <s:Button 
        id="selectCountryButton"    
        includeInLayout="{navigator.length>1}"
        visible="{navigator.length>1}"
        click="selectCountryButton_clickHandler(event)">            
        <s:icon>
            <s:MultiDPIBitmapSource                      
                source160dpi="@Embed('/assets/icons/world_24x24.png')"
                source240dpi="@Embed('/assets/icons/world_32x32.png')"
                source320dpi="@Embed('/assets/icons/world_48x48.png')"/>
            </s:icon>                
    </s:Button>          
    <s:Button id="selectViewerButton"    
              includeInLayout="{navigator.length>1}"
              visible="{navigator.length>1}"
              click="selectViewerButton_clickHandler(event)">            
        <s:icon>                
            <s:MultiDPIBitmapSource                      
                source160dpi="@Embed('/assets/icons/chart_24x24.png')"
                source240dpi="@Embed('/assets/icons/chart_32x32.png')"
                source320dpi="@Embed('/assets/icons/chart_48x48.png')"/>
        </s:icon>    
    </s:Button>        
</s:actionContent>


Sometimes you want to override that global content for an individual view. For example, if your actionContent includes a Help button you wouldn't need to display that Help button when the Help view is being displayed. Here's an example of view code that disables the second button in global actionContent:


protected function creationCompleteHandler(event:Event):void
{
   // Disable Viewer selection button when viewSelector view displayed
   var myArray:Array = navigator.actionContent ; 
   var myButton:Button = myArray[1] as Button ; 
   myButton.enabled = false ;
}


Note that the above is done using creationComplete instead of viewActivate – otherwise you'd actually see the button change from enabled to disabled.


You can also completely clear the global actionContent area in a view simply by adding this to the view:

  <s:actionContent/>


A useful post on this: http://corlan.org/2011/08/29/flex-mobile-development-skinning-the-actionbar-component/



ViewMenu tip #1: no global ViewMenu

While you can have global actionContent (see prev tip) you can't (unfortunately) have a global viewMenu – a ViewMenu defined in your root class will NOT be inherited by your app's views.


While viewMenus must be defined in each individual view, you can avoid replicating code by creating subclasses of ViewMenuItem and then using these in your view-level viewMenu definitions. Here's an example that creates a ViewMenuItem subclass named ViewMenuItemStatus:


<?xml version="1.0" encoding="utf-8"?>
<s:ViewMenuItem xmlns:fx="http://ns.adobe.com/mxml/2009" 
        xmlns:s="library://ns.adobe.com/flex/spark"
        label="Status"
        iconPlacement="top"
        color="#003366" 
        chromeColor="#CCCCCC"
        click="viewmenuitemClickHandler(event)">    
    <s:icon>
        <s:MultiDPIBitmapSource                      
          source160dpi="@Embed('/assets/icons/status_32x32.png')"
          source240dpi="@Embed('/assets/icons/status_48x48.png')"
          source320dpi="@Embed('/assets/icons/status_48x48.png')"/>
    </s:icon>
        
    <fx:Script>
        <![CDATA[            
        import flash.display.DisplayObjectContainer;            
        import mx.core.FlexGlobals;            
        import views.StatusSummary;                        
        protected function viewmenuitemClickHandler(event:MouseEvent):void
        {
            var statusPopup:StatusSummary = new StatusSummary() ; 
            statusPopup.open(FlexGlobals.topLevelApplication as
                     DisplayObjectContainer,true) ;             
        }
        ]]>
    </fx:Script>        
</s:ViewMenuItem>


…and now you can reuse this viewMenuItem in your views just like any other custom class:

<s:viewMenuItems>        
    <phoneViews:ViewMenuItemStatus />        
    <phoneViews:ViewMenuItemDataRefresh />        
    <phoneViews:ViewMenuItemDataFilter />        
</s:viewMenuItems>


ViewMenu tip #2: ViewMenu and popups

Problem: viewMenu is not disabled for modal popups – that is, when you have a modal popup displayed and the user presses the menu key your viewMenu will display. Depending on your viewMenu's action this can cause problems – for example, if your viewMenu button changes the current view (e.g., does a popToFirstView) then that view change will happen behind your popup, which is probably not something you want. You can handle this by having your viewMenu check for popups and not chg state if a popup is active. Here's an example:


protected function viewmenuitemClickHandler(event:MouseEvent):void
{
   var presenter:MainPresenter = AppModel.getInstance().mainPresenter;
   if (!systemManager.numModalWindows==0)
   {
      var tempPopmsg:PopMsg = new PopMsg() ; 
      tempPopmsg.msgText = "Please close popup before navigating to DataFilter view"; 
      tempPopmsg.open(FlexGlobals.topLevelApplication as
            DisplayObjectContainer,true) ;
   }
   else presenter.setState("dataFilter") ;                     
}


Alternatively, you could have your popup's key handler watch for a menu key press and respond appropriately. Below is code from a popup that just calls its close routine when the menu button is pressed:


protected function deviceKeyUpHandler(event:KeyboardEvent):void
{
    var key:uint = event.keyCode;
    if (key == Keyboard.MENU)                    
        closePopUp();                
}



Handling the Android back key

On Android the Back key is mostly handled by the Flex/AIR framework if you've based your app on TabbedViewNavigatorApplication or ViewNavigatorApplication. However, ViewNavigator doesn't handle Back key handling for popups – if you want your popups to close when the Back key is pressed (something Android users expect) then you'll have to write that key handling yourself. And if your mobile app is based on the Application class then you'll need to provide all the Back key handling.


This proved to be a big enough topic that I ended up breaking this out into a separate post: AIR and Android Back key. This post covers:

  • how to close popups on Android on a Back key press
  • how to keep your app from exiting when closing a popup in your home view
  • special handling for Android apps based on Application class instead of ViewNavigatorApplication/ TabbedViewNavigatorApplication

Displaying HTML content on mobile

This summarizes some differences between HTML text handling on mobile versus non-mobile. Note that I'm not talking about rendering a complete web page (though you can do that with AIR's StageWebView, here is a post on that). Rather, I'm talking about a text field displaying a chunk of text with <b> and <p> tags, <br/>'s, bulleted lists, anchor tags, all that good stuff.

Recently I was creating some Flex/AIR components for use in both mobile and non-mobile environments, but when I needed to display some HTML text I soon discovered that the text-handling options available to mobile apps differs significantly from those available to a non-mobile application. Here's a summary of these differences:

  • The Halo (mx:) TextArea class, which has fairly good HTML rendering capabilities when combined with StyleSheet (see sidebar on this below), isn't available on mobile because it's not Spark.
  • Unlike the Halo TextArea class, the Spark TextArea (and TextInput) is available to both mobile and non-mobile apps. However, it provides inconsistent functionality: the textFlow property you normally use to process your HTML text isn't available in the mobile version of Spark TextArea. More on this below...
  • RichEditableText class is available to both mobile and non-mobile apps and it can display HTML. However, once again there are caveats: unfortunately RichEditableText comes with significant performance and memory costs on mobile (Adobe says you can use it but they recommend you don't). Moreover, its HTML support and the way you feed that HTML to the component is vastly different from that of mx:TextArea (and IMHO, vastly inferior).
  • There is a new mobile-only class you can use for displaying HTML text, StyleableTextField. It has many of the same HTML display capabilities as mx:TextArea. However, this class is AIR-only and thus not available to browser-based apps. Also, it can't be instantiated via MXML, only via AS. It's most often used for mobile renderers because it's very lightweight.
  • Another new mobile-only text class (introduced in 4.6) is StageText. It makes use of native text controls' features and functionality. Similar to StyleableTextField, cannot be instantiated in MXML. Has no ability to render HTML. Adobe design doc for StageText is here.
  • It appears that Adobe's recommended component for displaying bits of HTML markup text is StageWebView, which leverages the native browser engine when used on a mobile device. StageWebView isn't really a Flex component, it doesn't even inherit from Sprite let alone UIComponent. On mobile it's essentially a chromeless system browser instance sitting on top of your app within a viewport you provide. The implementation for non-mobile is slightly different — you'll get an instance of HTMLLoader. IMHO using StagewebView to display a bit of HTMLmarkup is overkill, like using a howitzer to kill a mosquito (a howitzer with defects). Here's a useful link if you want to explore using StageWebView.

As you can see, components' text handling capabilites and their availability on mobile varies widely. That shouldn't come as a surprise. However, what might be less obvious is that there are also inconsistencies within individual Spark components. That is, a Spark component may handle text differently when running on mobile versus running on non-mobile. For example, the Spark TextArea's non-mobile skin uses RichEditableText. However, this is too heavyweight for mobile so TextArea's mobile skin uses StyleableTextField. Uh, unless you're using 4.6 — in that case it's based on StyleableStageText.


Another example of a component whose text display capabilities vary between mobile and non-mobile is the Spark Button. Spark Buttons employ a Spark Label to display their text when used in a non-mobile application but on mobile the Button's label is a StyleableTextField.


All of this can yield signficantly different behavior from a component depending on the environment in which you're running it. For an example of this see my tip below, Rotating buttons on mobile: disappearing label text


While it makes sense for mobile skins to use lighter weight components, the inconsistencies that result mean you need to carefuly review the documentation for text-based components you'll use on mobile. To illustrate this here's a snip from the Adobe doc "Use text in a mobile application":

"The Label control uses FTE, which is not as performant as text controls that have been optimized for mobile applications such as TextInput and TextArea. However, the Label control does not use TLF, so it generally performs better than controls such as RichText and RichEditableText, which do implement TLF.
In general, use Spark Label controls in mobile applications sparingly. Do not use the Spark Label control in skins or item renderers. When creating an ActionScript based item renderer, use the StyleableTextField class for rendering text. For an MXML-based component, you can still use Label."


Finally, here's something interesting re Spark TextArea and HTMLText: several Adobe docs give examples of assigning HTML content to a Spark TextArea on mobile, including use of a StyleSheet. This post is an example. However, if you read the comments of this post you find a comment from an Adobe employee stating:

"this was not a supported way to add HTML text to a mobile control. This documentation is in error. The supported way to do this would be to use StageWebView"


And this ASDoc page on StyleableStageText (class on which mobile TextArea and TextInput are based) clearly states that "Links and html markup are not supported".


In 4.5.1 I ran the example code from the post I link to above. It assigns HTML markup (including an anchor) to a Spark TextArea, and it worked fine on both my Droid X and my Xoom. However, I assume this won't work in 4.6 since TextArea and TextInput changed to using StageText (which cannot render HTML). For a relevant post see this from Adobe Flex forum.


I mentioned an Adobe doc above but let me repeat it here, since it's very useful: "Use text in a mobile application"


Adobe TV has a Max 2011 presentation "Performance Tuning Mobile Flex Applications" that has a very useful overview of the various text classes and their differences (about 13:00 minutes into the video).



 
The StyleSheet class provides enhanced CSS features in some text fields (not Spark text fields but this works for mx:TextInput and mx:TextArea and maybe StyleableTextField, which is more Flash than Spark). For example, via Stylesheet you can get anchors to really look and act like link text, set properties like leading, margins, anchor hover color, etc. A good reference on this is Moock's "Essential ActionScript 3.0" Chapter 27.

Assets on Android

AIR for Andoid can include runtime assets in the .apk file it generates. To control which files are included in the apk use Flash Builder menu: Project / Properties / Flex Build Packaging / [ choose your OS target ] / Package Contents.


On Android you mostly access assets normally, thru their directory (e.g. splashScreenImage=

"@Embed(source= 'assets/images/splash1.jpg')". To access local files you can also use File class folder references (e.g. File.applicationDirectory.resolvePath(

"assets/docs/DEOverview.htm")).


However, keep in mind that on Android these files no longer reside in a "real" directory because an .apk is a compressed file (aka zip file). Mostly you won't notice any differences since Flex handles file access for you. Still, there are times when having your assets stored in a compressed file impacts how some AIR functions work and how you access your assets. For example, on Android a File reference's nativePath property always returns a blank value (because your Fileref isn't pointing to a "real" file, just some bytes stored in that compressed apk). For more information on this, especially about StageWebview access of asset files, see my post "AIR, StageWebView, displaying local content".


BTW, if you really want to see what files were packaged up into your apk you can do this easily with any utility that displays the contents of a zip file. Just temporarily rename your .apk to a .zip and then use a tool like WinZip to look at the file contents to see if your asset was really included in the final pkg.


And one more potential gotcha for accessing files on Android: case sensitivity. Remember, Unix-based OS's are case sensitive, so USA.png is not the same as usa.png.



Rotating buttons on mobile: disappearing label text

In "normal" (non-mobile) Flex/AIR apps the Spark button class keeps the button's label text visible as the button rotates (unlike Halo's mx:Button where you had to use an embedded font to keep the label text visible).


However, on mobile when you rotate that button its label text disappears (because the mobile skin for s:Button uses a different component for the button's label). Your first guess might be that you'll need to use an embedded font on mobile to resolve this, but here's a simpler solution: just set the z property for the button to ensure the button and its parts (the label) stays "on top" and visible. Example:


<s:Button id="viewerMgrsButton"  
label="Set layout" width="{widthExpanded}"
width.collapsedState="{NaN}" 
cornerRadius="8"  
click="this.currentState='viewManagementState'" 
rotation.collapsedState="90" z="1"/>


Mobile Skinning

Mobile skins and non-mobile skins are different in several ways. Because of the limitations of mobile devices you'll find that mobile skins are generally not created in MXML (which is the standard for non-mobile skins). Instead they're written in AS for efficiency. And mobile skins include code for handling the three AIR dpi categories: 160, 240, 320. All of this means that mobile skins are more complex than non-mobiles and you can't create mobile skins with Catalyst.


 
You can use MXML skins on mobile (with a performance cost). For a bit more info on this check out slide 9 of Jason San Jose's Developer Week presentation (see below) pdf slides.

This tip on skinning isn't really a tip so much as some links to devnet posts, but these posts are so incredibly useful that if you haven't encountered these JasonJ posts yourself then you owe me a beer for putting you onto them…


Jason San Jose's 3-part series on mobile skinning:

Jason's Developer Week presentation is also excellent:


Flex/AIR certification logo
About

JavaScript

JavaScript appdev primers

General JavaScript

SPA Demo Application

Backbone Demo Application

Recommended sites

Flex/AIR

Flex/AIR primers

Flex demo apps

all require Flash Player!

AIR mobile dev

SAS

SAS Introduction

Varia

About

Archives