Поддержка различных размеров экрана в мобильных AIR приложениях

This post is also available in: Английский

Мультиплатформенность AIR и огромное количество устройств на которых возможна работа AIR-приложений обязывают разработчиков учитывать все разнообразие экранов, их разрешений и DPI. Несмотря на все разнообразие платформ, в данной статье мы рассмотрим разработку для различных экранов только применительно к разработке мобильных AIR-приложений с использованием Flex фреймворка.

Давайте подробно остановимся на основной терминологии, которую мы будем в дальнейшем использовать. Для удобства мы в скобках сразу будем указывать, в каком свойстве можно найти цифровое значение того или иного параметра.

  1. Разрешение экрана — количество точек на экране по горизонтали и вертикали (Capabilities.screenResolutionX и Capabilities.screenResolutionY)
  2. Разрешение рабочей области — количество точек рабочей области по горизонтали и вертикали. Может очень сильно отличаться от Разрешения экрана не только потому, что рабочая область может быть меньше из-за строки статуса сверху, панели кнопок снизу, но и в случае включенного автоматического масштабирования (systemManager.screen.width и systemManager.screen.height.
  3. DPI экрана — физическое количество точек на дюйм экрана устройства, на котором запущено приложение (Capabilities.screenDPI).
  4. DPI среды выполнения — возвращает DPI экрана, округленное до значения одной из констант, определенных в классе DPIClassification (свойство runtimeDPI в главном классе Flex-приложения. Значение данного свойства можно получить через FlexGlobals.topLevelApplication.runtimeDPI). Оно может принимать только три значения: 160, 240, 320. Это основные значения, до которых округляется DPI экрана. Использование фиксированных значений нужно для того, чтобы можно было иметь ограниченный набор графики только для трех различных DPI. Используя свойство runtimeDPIProvider основного приложения можно указать класс, отнаследованный от RuntimeDPIProvider, в котором, переопределив метод runtimeDPI, можно задать свою собственную логику округления DPI экрана до нужного значения DPI среды выполнения. Это может пригодиться, например, в том случае, если предполагается учитывать не только DPI экрана, но и его разрешение или какие либо другие параметры.
  5. DPI приложения — это числовое значение DPI, для которого разрабатывается приложение (свойство applicationDPI в главном классе Flex-приложения. Значение данного свойства можно получить через FlexGlobals.topLevelApplication.applicationDPI). Данное свойство является ключевым при использовании автоматического масштабирования посредством Flex-фреймворка. Это значение можно указать только через MXML в главном классе приложения и невозможно изменить во время выполнения. Если applicationDPI не задано, то автоматическое масштабирование Flex-фреймворком выполняться не будет. Значение данного свойства может принимать только три значения: 160, 240, 320.

Автоматическое масштабирование

Автоматическое масштабирование посредством Flex-фреймворка можно включить, задав DPI приложения. Если разработчик указал значение applicationDPI, то Flex к основному приложению начинает применять коэффициент масштабирования, меняя scaleX и scaleY для systemManager. Это позволяет облегчить  разработку мультиплатформенных и мультиэкранных приложений.

При разработке приложений со включенным автоматическим масштабированием важно правильно определять размер рабочей области. Делать это нужно только через systemManager.screen.width и systemManager.screen.height, но никак не через stage.width и stage.height.

Например, возьмем устройство и запущенное на нем приложение со следующими параметрами:

  • runtimeDPI устройства определяется как 320;
  • applicationDPI задано как 240;
  • разрешение экрана 1280 х 720;

Запустив приложение на таком устройстве мы можем увидеть следующие значения некоторых свойств:

stage.stageWidth: 720
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

Видно, что размер рабочей области меньше, чем размер stage, поскольку при включенном автоматическом масштабировании у systemManager приложения меняется scaleX и scaleY. Поэтому, например, при центрировании всплывающих окон и разработке другого функционала, где нужно точно знать правильные размеры рабочей области, их нужно искать в systemManager.screen.width и systemManager.screen.height.

Автоматическое масштабирование может приводить к отображению контента с различными искажениями. Для того, чтобы избежать этого, необходимо придерживаться следующих правил:

  1. Использовать векторные изображения. При автоматическом масштабировании векторных изображений Flex отобразит их одинаково для всех типов экранов. Со шрифтами тоже проблем не возникает, они также масштабируются без искажений.
  2. Для растровой графики, иконок или скинов приложения необходимо иметь их изображение для трех различных DPI. Flex имеет ряд инструментов, позволяющих использовать нужное изображение для конкретной DPI среды выполнения.

Кастомизация растровых ресурсов для разных DPI

Для того чтобы у растровых изображений не было никаких искажений и артефактов, связанных с автоматическим масштабированием приложения, необходимо указать Flex-фреймворку, какое изображение использовать для того или иного DPI. Для этого при указании источника изображения используется экземпляр класса MultiDPIBitmapSource, в свойствах которого заданы ресурсы изображений для трех различных 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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
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();

    // Источник для иконки в обычном состоянии
    private var multiSource:MultiDPIBitmapSource;

    // Источник для иконки в выбранном состоянии
    private var multiSourceSelected:MultiDPIBitmapSource;

    public function BellSwitcher()
    {
        super();

        // Для каждого из источников задаем растровые изображения
        // для трех различных DPI
        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();
    }
    public function get selected():Boolean
    {
        return _selected;
    }
    protected function commitSelected():void
    {
        // В зависимости от состояния элемента отображаем нужную иконку,
        // задавая соответствующий источник
        bellIcon.source = _selected ? multiSourceSelected : multiSource;
    }
}
}

В данном примере изображения иконок будут загружаться на этапе выполнения по мере необходимости.

Приведем пример использования MultiDPIBitmapSource для растровых изображений, внедряемых в приложение на этапе компиляции. Класс скина для компонента CheckBox:

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
<!--?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>
    <![CDATA[
       /**
        * @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>

Использование таблиц стилей с привязкой к DPI

Flex также предоставляет возможности настройки таблицы стилей в зависимости от DPI среды выполнения и типа операционной системы. Если необходимо, например, задать размер шрифта для определенного DPI или операционной системы, то можно явно указать это в таблице стилей, используя правило @media. Данное правило может иметь два свойства: os-platform, определяющее тип операционной системы, — и application-dpi, позволяющее задать стили для нужного DPI.

Свойство os-platform может принимать следующие значения:

  • Android
  • iOS
  • Macintosh
  • Linux
  • QNX
  • Windows

Синтаксис использования @media может быть достаточно сложным и позволяет совмещать различные свойства и перечислять варианты сочетаний свойств через запятую.

Приведем несколько примеров использования правила @media и его свойств os-platform и 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
/* Любая операционная система и 160 DPI */
@media (application-dpi: 160) {
    s|Button {
        fontSize: 10;
    }
}

/* Только iOS и 240 DPI */
@media (application-dpi: 240) and (os-platform: "IOS") {
    s|Button {
        fontSize: 11;
    }
}

/* iOS при 160 DPI или Android при 160 DPI */
@media (os-platform: "IOS") and (application-dpi:160), (os-platform: "ANDROID") and (application-dpi: 160) {
    s|Button {
        fontSize: 13;        
    }
}

/* Любая операционная система, за исключением Android, при 240 DPI */
@media not all and (application-dpi: 240) and (os-platform: "Android") {
    s|Button {
        fontSize: 12;
    }
}    

/* Любая операционная система, за исключением iOS, при любом DPI */
@media not all and (os-platform: "IOS") {
    s|Button {
        fontSize: 14;        
    }
}

Отключение автоматического масштабирования

Для того, чтобы отключить автоматическое масштабирование (а точнее, чтобы не включать его), не нужно задавать DPI приложения в свойстве applicationDPI в главном классе.

При отключенном автоматическом масштабировании во Flex, операции по изменению размеров элементов интерфейса и оформления придется делать самостоятельно. Для изображений можно будет использовать кастомизацию растровых ресурсов, о которой мы уже говорили выше.

Используя таблицы стилей для каждого значения DPI, можно кастомизировать размер шрифта, типы иконок и прочее в зависмости от свойств экрана устройства. В случае, если автоматическое масштабирование выключено, значение свойства applicationDPI будет принимать значение runtimeDPI. Поэтому, если использовать стили при выключенном автоматическом масштабировании, привязка по DPI будет осуществляться к DPI среды выполнения.

Flex имеет достаточно богатый арсенал для разработки мультиплатформенных и мультиэкранных приложений, во многом освобождая разработчика от рутинных действий по их написанию. Использование автоматического масштабирования дает большие преимущества, позволяет разрабатывать приложения быстрее и быть уверенным в том, что они будут хорошо выглядеть на различных устройствах.