User level
Required products
Flash Builder 4(Download trial)
Adobe AIR
Flex (Download trial)
Sample files (25036 KB)

This article discusses and explains techniques our team has developed through the creation and deployment of an enterprise application across multiple screens. It provides an overview followed by concrete examples and sample code that achieve the most code reuse between screens. The sample code uses the AIR 2.5 runtime for the Android platform which is currently in prerelease.
This application was built prior to the announcement of Flex SDK "Hero" and the sample code is compiled and built with Flex 4.1. However, the techniques explained here can be applied to Flex SDK "Hero". Upon its release, we recommend building your multiscreen applications with it.
Note: Be sure to watch Senior Evangelist Ron Nagy's video presentation of this sample application, which demonstrates how Adobe AIR for Android can benefit enterprise users with a highly dynamic employee directory app that works equally well across desktop and mobile.

Parsley overview

To create this application we used the Parsley framework. We'll go over a quick overview of Parsley, but this is not ment to be a tutorial on Parsley. For more information refer to the documentation.
Parsley is an application framework for Flex/AIR that offers several features including dependancy injection and a robust messaging framework. In order to take advantage of the dependancy injection and messaging features you must construct a Context. The Context contains an instance of all classes that are exposed to the messaging framework and are available to be injected into other classes. This can be seen in the following snippet found in EmpdirDesktop.mxml.
<fx:Declarations> <spicefactory:ContextBuilder> <spicefactory:FlexConfig type="{ApplicationContext}"/> <spicefactory:FlexConfig type="{EmployeeContext}"/> </spicefactory:ContextBuilder> </fx:Declarations>
If you examine the contents of the com.adobe.empdir.ApplicationContext and com.adobe.empdir.EmployeeContext classes, you'll notice that they contain various Command classes, Presentation Models, and other Manager classes. These classes and their functionality will be explained in more detail throughout the article. For now the important part to take away from this is that the Context contains a single instance of each of the classes referenced in either the ApplicationContext or EmployeeContext classes. Any class in the Context can be injected into any other class also found in the Context. An example of this can be seen in the PanelManager class:
[Inject] public var leftPanelPM:LeftPanelManagerPM;
Both the PanelManager class and the LeftPanelManagerPM class are found in ApplicationContext.mxml and can be injected when needed.
The second part, the messaging system, allows classes in the Context to communicate through ManagedEvents. ManagedEvents have three parts to them; the event being dispatched, the ManagedEvents metadata tag on the class dispatching the event, and the MessageHandler metadata tag on the receiving function. All managed events must be bubbled up to the Context which lives in the root of the application. The ManagedEvents metadata tag is placed on the class that is dispatching an event and sets the type that should be passed to the messaging framework. An example of this can be seen in the ApplicationHeaderPM class:
Note: Only key parts of the class are shown.
[ManagedEvents(names="search, changeState, openState, setSearchListFocus")] public class ApplicationHeaderPM extends EventDispatcher { . . . dispatchEvent(new SearchEvent(SearchEvent.SEARCH, pendingTerm)); . . . }
The MessageHandler metadata tag is the receiving end of the previous example. It defines the function to be called based on a selector and event type. An example of this can be found in the SearchResultsPanelPM class.
[MessageHandler(selector="search")] public function onSearch(event:SearchEvent):void{ currentState = SEARCHING_STATE; }
The selector criteria matches the event type that was dispatched in the ApplicationHeaderPM and the event:SearchEvent identifies that it is a SearchEvent type that has to be passed in.
This should cover the concepts of creating a Context, injecting classes, and the messaging framework which are key to understanding this article and sample code. If you are interested in more information about the Parsley framework, please visit the Parsley site.

Package structure

A well laid-out project structure helps you maintain code and keep it organized. We've decided to divide our applications into three types of projects: shell projects, domain projects, and library projects.
  • Shell projects: Shell projects exist on a one per screen basis. They are responsible for initializing the application, housing the corresponding views for that screen, and managing their states.
  • Domain projects: One way to keep your project organized is to group domain specific objects, that is to say objects that are all related to the same subject, together in their own project. Since these are grouped together into their own Flex library projects, it presents the opportunity for them to be used across multiple applications.
  • Library projects: These contain all the shared assets, events, skins, and components that are to be shared across shell and domain projects.
How we implemented it
The following is a quick rundown of our package structure, what is found where, and how it all ties together.
The Shell projects
  • Responsible for managing all views specific to mobile.
  • Contains business logic specific to mobile.
  • Contains some custom mobile specific code (Android-like menu ).
  • Responsible for managing all views specific to desktop.
  • Contains business logic specific to desktop.
  • Contains some specific desktop code—tray icon, dock icon, auto-update code (not in sample code).
The Library projects
  • Contains all the shared assets, skins, events, interfaces.
  • Contains everything required for looking up and displaying employee information. For example, business logic, UI components, and presentation models specific to looking up and displaying employee information for any screens.

Skinning and styling

In the sample application we take two different approaches in reusing skin classes between multiple screens. One method uses a combination of FXG and CSS, whereas the other uses several different images and CSS. Each of these has advantages and disadvantages. The FXG approach allows the vector to be scaled for the various screens. However, FXG component images do incur a small performance hit (mainly on mobile devices). The alternative—using several different images and CSS—does not have these performance issues; however, it becomes harder to manage as the amount of screens you are targeting increases. You are forced to have a custom graphic for each screen due to scaling and resolution issues. You should use discretion when choosing between the two—large amounts of FXG component (especially animations and transformations) will have a noticeable performance impact on mobile devices.
How we implemented it
In our sample application we've implemented both FXG component and the use of images and CSS.
We'll start with covering how to define FXG component. Consider the following:
<icons:MagnifingGlass left="7" top="8" scaleX="{getStyle('searchIconScale')}" scaleY="{getStyle('searchIconScale')}"/>
The code snippet above is found in SearchInputSkin.mxml. The <icons:MagnifingGlass..> tag references the FXG image of the magnifing glass in the search input. Note that the two scaleX and scaleY properties both reference externalized values loaded from the style sheet. On mobile, these values are set to 2 which indicates to scale the vector to twice its X and Y values (basically double in size) whereas on destkop, we set the scale to 1 (unchanged). We mentioned previously the potential performance hit you may incur on mobile devices with FXG component. To help alleviate some of these performance problems, try not to overload your application with them and keep them static (no animations/transitions applied).
An alternative is to use CSS and multiple images for each screen. Consider the following:
<mx:Image id="closeButtonImage" source="{getStyle('closeNormal')}" mouseDown="closeButtonImage.source=getStyle('closePressed')" mouseOut="closeButtonImage.source=getStyle('closeNormal')"/>
and the two CSS files:
LightTheme.css ( Found in the mobile-shell project )
.searchInputSkin{ searchIconScale: 2.0; closeButtonRight: 5; closeButtonTop: 9; textPaddingLeft: 40; fontSize: 26; closeNormal: Embed('assets/icons/IconSearchCloseMobileNormal.png'); closePressed: Embed('assets/icons/IconSearchCloseMobilePressed.png'); }
LightTheme.css ( Found in the desktop-shell project )
.searchInputSkin{ searchIconScale: 1.0; closeButtonRight: 5; closeButtonTop: 6; textPaddingLeft: 26; fontSize: 16; closeNormal: Embed('assets/icons/IconSearchCloseNormal.png'); closePressed: Embed('assets/icons/IconSearchClosePressed.png'); }
The . searchInputSkin style is applied to the SearchInputSkin component. The closeButtonImage component uses the getStyle function to reference the externalized source from the CSS file. You'll notice that the image that is referenced in the LightTheme CSS changes based on the desktop and mobile environments. Externalizing the source values for the image allows the skin class to be reused across desktop and mobile.
It's worth mentioning that we didn't cover the use of 9-slice images in this article; they could be useful for an alternative way of skinning buttons and other components. However, this technique would not apply in all cases (for example, the above example could not have this technique applied to it); it would still require specific resolutions of that image per screen.

Presentation models and screen design

A presentation model is much like any other model—a class that contains variables that the view is bound too. However, the presentation model concept extends the traditional model to take the state and logic out of the view component. This concept lends itself well to multiscreen development as it allows you to have several view components laid out differently, and every component has a single class to handle the logic. These presentation models should be able to be shared most of the time between shell projects.
Personally, we don't recommend the reuse of views. Views and layouts should be tailored for the best experience on that screen. At times, that experience can be the same across all screens, but for the most part you will be building specific views for specific screens.
How we implemented it
Remember our project structure—the empdir-employee is a domain project that contains all of the shared presentation model classes for the employee related functionality. Consider the scenario detailed in Figure 1
The DepartmentPanel views are bound to the same shared DepartmentPanelPM.
Figure 1. The DepartmentPanel views are bound to the same shared DepartmentPanelPM.
Figure 1 illustrates how the separate DepartmentPanel views are bound to the same shared DepartmentPanelPM; the DepartmentPanelPM contains the state information as well as the logic for selecting a person or closing the panel. In both desktop and mobile the same thing needs to happen when an employee is selected from the the department panel. The panel manager needs to be notified to update and the loadEmployee event needs to be dispatched. The only thing that is different is the DepartmentPanel for mobile is laid out differently then the DepartmentPanel desktop. The same concept can be found in the AvailabilityPanel, DirectReportsPanel, and others.
Another thing to keep in mind is that you may want to use different touch-enabled components in the mobile app but not in the desktop app. Creating separate views for desktop and mobile allows you to do this while still reusing logic code (presentation model).

Commands and Events

For the most part, commands and events can be reused between screens. Events act as the communication piece to kick off commands and commands contain all the business logic, interface with external systems, local databases, and the file system. Most of the time, this business logic does not change across screens. However, there may be cases where the logic in the command may need to be altered for a specific screen. You can achieve this by instantiating the command class in the shell project's Context class and setting its properties from there. When the context is created, it will create an instance of the command you want to alter with new values for the public properties. From there, the command uses these values instead of the default values in the command.
How we implemented it
Take a look at the ApplicationContext.mxml files in both the empdir-desktop-shell and the empdir-mobile-shell . You'll notice that these files both instantiate a LoadDepartmentCommand , but on closer investigation you should also notice that the desktop version has the resultSize = -1 whereas the mobile version has resultSize = 20 . This doesn't limit the results returned from an employee seach for the desktop shell, but it does limit the results returned for mobile—despite it being the same Command class under the hood.
One imporant note about where the LoadDepartmentCommand is being added to the Context. Since we externalized some of the properties of this command to allow shell projects to supply specific values, this command has to be added to the Conext class that is built by the shell project. If this command was to be added to the Context by the empdir-employee – EmployeeContext.mxml , it would not be configurable from the individual shell projects.

Screen management

The experience you'll have on a desktop system, a mobile device, or a tablet can be drastically different. A desktop could have several windows, be resizable and display data in a completely different way from a single screen mobile view. To account for this we've taken the approach of designing a screen manager specific for each device. The ScreenManager class essentially controls displaying and updating the view states based on what needs to be shown. It does this by listening for state change events.
How we implemented it
Both desktop and mobile have separate screen manager classes. Each manager listens for the same state change events, however, they are handled differently. For example, when 'direct reports' is selected from the main employee view, a "show direct reports" event is dispatched and picked up from the Screen Manager class. In Desktop, this is handled by opening the "right panel" and showing the direct reports view in the right panel. Where as in mobile, the existing panel is swapped to the direct reports view. The same concept can be seen in availability view and department view.
You'll notice in for the desktop shell that there is some desktop specific logic that is called when an employee is changed. In a desktop environment, you can be looking at an employee's details and their direct reports at the same time. If you were to change the selected employee, the panel manager needs to react to this and update the right panel to either show the direct reports of the newly selected employee or a No Direct Reports screen in place of it if they have none. In a mobile environment its not possible to to view both an employees details as well as their direct reports at the same time. Since updating the right panel is a screen specific function we put it into the panel manager.

Where to go from here

This article outlined the development techniques we've successfully used to deploy an internal enterprise application. We encourage you to try creating a multiscreen application of your own using the samples provided or apply the theory elements in this article to your preferred MVC framework. The Flex SDK "Hero" framework will introduce new best practices that will most likely affect the implementation details, however, the theory behind our techniques should remain similar.