This post is also available in: Russian
As AIR applications can run on multiple platforms and devices, AIR based developers have to take into account a vast diversity of screens, their resolutions and DPIs. Despite a broad diversity of supported platforms, in this post we will focus on multiscreen development for mobile AIR applications based on the Flex framework.
Let’s now agree upon the basic terminology that we are going to use here. For convenience, in parentheses we will indicate the property containing a numerical value of a given parameter.
- Screen resolution means the number of pixels on the screen in horizontal and vertical dimensions (
Capabilities.screenResolutionX
andCapabilities.screenResolutionY
) - Display space resolution means both horizontal and vertical pixels of the display space. It may differ much from screen resolution as it does not include the status bar at the top, the button bar at the bottom, and also due to auto-scaling (
systemManager.screen.width
andsystemManager.screen.height
). - Screen DPI means the physical number of dots per inch on the device screen running the application (
Capabilities.screenDPI
). - Runtime DPI returns Screen DPI rounded up to the value of one of the constants defined in the class called
DPIClassification
(runtimeDPI
property) in the main class of the Flex application. The value of this property can be retrieved usingFlexGlobals.topLevelApplication.runtimeDPI
). This property can only take three values:160, 240, 320
. These are the basic values to which DPI screen is rounded. You have to use fixed values to have a limited set of graphics for three different DPIs. Using theruntimeDPIProvider
property, you can specify, for the main application, a class inherited fromRuntimeDPIProvider
. In this class, you can override theruntimeDPI
method and set your own logic to round up Screen DPI to the desired value of DPI runtime. This might be useful, for example, in the event in the addition to screen DPI you need to define its resolution or any other options. - Application DPI means a numeric DPI value for which the application is developed (property –
applicationDPI
) in the main class of the Flex application. The value of this property can be accessed viaFlexGlobals.topLevelApplication.applicationDPI
). This is the key property used during automatic scaling performed by the Flex framework. You can set this value can using MXML in the main application class, and can change it at runtime. IfapplicationDPI
is not specified, automatic scaling will not be performed by the Flex framework. This property can only take three values:160, 240, 320
.
Automatic Scaling
Auto-scaling using the Flex framework can be enabled by setting Application DPI. If the developer has specified applicationDPI
, then Flex begins to apply a scaling factor to the main application by changing scaleX
and scaleY
for systemManager
. This facilitates development of multi-platform and multi-screen applications.
When developing applications with auto-scaling enabled, it is essential to define the size of display space. To do this, use systemManager.screen.width
and systemManager.screen.height
only, rather than stage.width
and stage.height
.
For instance, let’s take a device and an application run by it with the following parameters:
- runtimeDPI of the device is set to 320;
- applicationDPI is set to 240;
- screen resolution is 1280 x 720;
Having launched the application on a device, we can see the following values of properties:
stage.stageHeight: 1230
stage.fullScreenWidth: 720
stage.fullScreenHeight: 1280
Capabilities.screenDPI: 320
Capabilities.screenResolutionX: 720
Capabilities.screenResolutionY: 1280
systemManager.screen.width: 540
systemManager.screen.height: 922.5
systemManager.scaleX: 1.333333333333333
systemManager.scaleY: 1.333333333333333
You can see that the display space size is smaller than the stage size, because automatic scaling results in change of scaleX
and scaleY
in the application’s systemManager
. So, for instance, when centering pop-ups and other functional elements that have to know exact display space sizes, you can find the sizes in systemManager.screen.width
and systemManager.screen.height
.
Automatic scaling can result in content distortion. To avoid this, please adhere to the following rules:
- Use vector images. In case of automatic scaling, Flex will display them similarly on all types of screens. Fonts also present no problem, as they are also scalable without distortion.
- For bitmaps, icons or skins you need to have images for three different DPIs. Flex has a number of in-built tools to use a needed image for a particular Runtime DPI.
Customization of Bitmap Resources for Different DPIs
To ensure that bitmaps have no distortions and artifacts associated with automatic scaling of the application, you should define in Flex, which image should be used for a given DPI. To do this, when setting an image source, use an instance of MultiDPIBitmapSource
, whose properties contain image resources for three different DPI.
Let’s consider several examples of bitmap customization.
Here is the class of component displaying a bell icon in a normal and selected state, respectively:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | package ru.denivip.europa.views.schedule { import spark.components.Group; import spark.primitives.BitmapImage; import spark.utils.MultiDPIBitmapSource; public class BellSwitcher extends Group { private var bellIcon:BitmapImage = new BitmapImage(); // Source for the icon in a normal state private var multiSource:MultiDPIBitmapSource; // Source for the icon in a selected state private var multiSourceSelected:MultiDPIBitmapSource; public function BellSwitcher() { super(); // For each of sources, set bitmap images // for three different DPIs multiSource = new MultiDPIBitmapSource(); multiSource.source160dpi = "/assets/icon_bell@160.png"; multiSource.source240dpi = "/assets/icon_bell@240.png"; multiSource.source320dpi = "/assets/icon_bell@320.png"; multiSourceSelected = new MultiDPIBitmapSource(); multiSourceSelected.source160dpi = "/assets/icon_bell_selected@160.png"; multiSourceSelected.source240dpi = "/assets/icon_bell_selected@240.png"; multiSourceSelected.source320dpi = "/assets/icon_bell_selected@320.png"; addElement(bellIcon); commitSelected(); } private var _selected:Boolean = false; public function set selected(value:Boolean):void { _selected = value; commitSelected(); } protected function commitSelected():void { // The icon source is determined based on the element state bellIcon.source = _selected ? multiSourceSelected : multiSource; } } } |
An example of using MultiDPIBitmapSource
for bitmaps embedded in the application at compile time. Skin class for a CheckBox component:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | <!--?xml version="1.0" encoding="utf-8"?--> <s:SparkSkin xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:fb="http://ns.adobe.com/flashbuilder/2009" alpha.disabledStates="0.5"> <fx:Metadata> /** * @copy spark.skins.spark.ApplicationSkin#hostComponent */ [HostComponent("spark.components.CheckBox")] </fx:Metadata> <s:states> <s:State name="up" /> <s:State name="over" stateGroups="overStates" /> <s:State name="down" stateGroups="downStates" /> <s:State name="disabled" stateGroups="disabledStates" /> <s:State name="upAndSelected" stateGroups="selectedStates" /> <s:State name="overAndSelected" stateGroups="overStates, selectedStates" /> <s:State name="downAndSelected" stateGroups="downStates, selectedStates" /> <s:State name="disabledAndSelected" stateGroups="disabledStates, selectedStates" /> </s:states> <s:BitmapImage excludeFrom="selectedStates"> <s:source> <s:MultiDPIBitmapSource source160dpi="@Embed(source='assets/images/ui/check_box@160.png')" source240dpi="@Embed(source='assets/images/ui/check_box@240.png')" source320dpi="@Embed(source='assets/images/ui/check_box@320.png')"/> </s:source> </s:BitmapImage> <s:BitmapImage includeIn="selectedStates"> <s:source> <s:MultiDPIBitmapSource source160dpi="@Embed(source='assets/images/ui/check_box_checked@160.png')" source240dpi="@Embed(source='assets/images/ui/check_box_checked@240.png')" source320dpi="@Embed(source='assets/images/ui/check_box_checked@320.png')"/> </s:source> </s:BitmapImage> </s:SparkSkin> |
Use of Style Sheets Linked to DPI
Flex also lets you customize style sheets depending on the runtime DPI and operating system type. If you need, for instance, to set font size for a particular DPI or operating system, you can explicitly specify it in the style sheet, using the @media
rule. This rule can have two properties: os-platform
specifying the type of the Operating System and application-dpi
allowing you to specify styles for the needed DPI.
The os-platform
property can have the following values:
Android
iOS
Macintosh
Linux
QNX
Windows
The syntax of @media
can be quite complex and list different features and their combinations separated by commas.
Here are several examples of @media
rules and its properties os-platform
and application-dpi
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | /* Any operating system and 160 DPI */ @media (application-dpi: 160) { s|Button { fontSize: 10; } } /* Only iOS and 240 DPI */ @media (application-dpi: 240) and (os-platform: "IOS") { s|Button { fontSize: 11; } } /* iOS at 160 DPI or Android at 160 DPI */ @media (os-platform: "IOS") and (application-dpi: 160), (os-platform: "ANDROID") and (application-dpi: 160) { s | Button { fontSize: 13; } } / * Any OS except Android at 240 DPI */ @media not all and (application-dpi: 240) and (os-platform: "Android") { s|Button { fontSize: 12; } } /* Any OS except iOS at any DPI */ @media not all and (os-platform: "IOS") { s|Button { fontSize: 14; } } |
Automatic Scaling
To disable auto-scaling (or rather not to enable it), please do not specify Application DPI in property applicationDPI
in the main class.
When Automatic Scaling is disabled in Flex, you have to re-size interface and design elements yourself. For images, you can use customization of bitmap resources that we have already mentioned above.
Using style sheets for each DPI value, you can customize font size, icon types and other parameters depending on the device screen. If auto-scaling is disabled, applicationDPI
will be set to runtimeDPI
. Therefore, when using styles with auto-scaling disabled, Runtime DPI will be linked.
Flex has a fairly extensive toolkit to develop multi-platform and multi-screen applications, almost eliminating development routine. Auto-scaling offers great benefits and allows you to develop applications faster and be sure that they will look perfect on various devices.