Документация
ОС Аврора 5.1.5

Использование особенных функций ОС Аврора

При разработке приложений для ОС Аврора существует ряд функциональных возможностей и шаблонных решений, которых следует придерживаться для улучшения качества UI и UX приложения. Модуль Silica предоставляет доступ к этим возможностям для приведения пользовательских интерфейсов в соответствие со стандартами приложений ОС Аврора.

Стек страниц приложения

Каждое приложение ОС Аврора содержит стек страниц, в котором содержатся экраны с содержимым для отображения в окне приложения. Пользователь может переключаться между этими экранами (или, другими словами, страницами); при этом на экране устройства одновременно может показываться только одна страница. Чтобы показать новую страницу, ее следует поместить в верх стека. Чтобы закрыть текущую страницу и вернуться к предыдущей, следует удалить страницу из стека.

Страницы создаются с помощью типа Page, а стек страниц доступен в виде объекта типа PageStack, к которому можно получить доступ через свойство pageStack окна приложения. Первая страница в стеке, отображаемая после запуска приложения, задается через свойство initialPage окна приложения.

Для вызова свойства push или добавления страницы в стек следует вызвать методpush() стека страниц, в который передается URL файла QML либо объект типа Component с корневой страницей Page. Для удаления страницы из вершины стека (т.е. отображаемой в данный момент страницы) следует вызвать метод pop() стека страниц. Для обращения к странице на вершине стека используется свойство currentPage, а для получения количества страниц в стеке используется свойство depth.

Ниже приведён пример стека страниц в действии. Здесь страницы добавляются в стек с методом push() и удаляются методом pop():

import QtQuick 2.2
import Sailfish.Silica 1.0

ApplicationWindow {
    initialPage: pageComponent

    Component {
        id: pageComponent

        Page {
            PageHeader {
                title: "Количество страниц: " + pageStack.depth
            }

            Column {
                width: parent.width
                anchors.centerIn: parent
                spacing: Theme.paddingLarge

                Button {
                    text: "Добавить страницу"
                    anchors.horizontalCenter: parent.horizontalCenter
                    onClicked: {
                        pageStack.push(pageComponent)
                    }
                }

                Button {
                    text: "Удалить страницу"
                    anchors.horizontalCenter: parent.horizontalCenter
                    onClicked: {
                        pageStack.pop()
                    }
                }
            }
        }
    }
}

Примечание. Если провести по экрану слева направо или коснуться индикатора предыдущей страницы в левом верхнем углу экрана, произойдет удаление страницы из стека аналогично вызову метода pop().

Если страница определена в отдельном QML-файле, то для добавления ее в стек предпочтительнее использовать URL этой страницы в вызове метода push() вместо передачи объекта Component:

Button {
    text: "Добавить страницу"
    onClicked: pageStack.push(Qt.resolvedUrl("MyPage.qml"))
}

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

Имеется еще несколько операций, которые можно выполнить с помощью стека страниц PageStack, а именно:

  • При вызове метода push() в него можно передавать набор первоначальных свойств для страницы либо PageStackAction.Immediate в качестве значения параметра operationType для отключения анимации при добавлении новой страницы.
  • При вызове метода pop() в него можно передавать страницу, расположенную ниже в стеке; в этом случае будут удалены все страницы, расположенные выше в стеке.
  • Вместо добавления новой страницы в стек методом push() можно заменять текущую страницу на вершине стека методом replace().
  • Вместо метода push() можно использовать метод pushAttached() для добавления новой страницы в стек без ее отображения на экране, жестом смахивания вперед пользователь сможет отобразить эту страницу.

Дополнительную информацию можно получить в описании типа PageStack.

Использование диалогов для подтверждения ввода

Dialog — это специальный тип страницы, на которой отображается содержимое или запрашивается ввод пользователя, требующие от него подтверждения или отмены. Элемент типа Dialog поддерживает жесты смахивания вперед для подтверждения действия и смахивания назад для отмены действия (так же, как и в обычных страницах Page). Кроме того, диалоги используют тип DialogHeader (вместо PageHeader) для отображения кнопок Принять и Отменить в верхней части страницы.

Ниже приведён пример страницы с кнопкой, которая отображает диалог с набором опций:

import QtQuick 2.2
import Sailfish.Silica 1.0

ApplicationWindow {
    initialPage: firstPage

    Component {
        id: firstPage
        Page {
            PageHeader { id: header }

            Button {
                text: "Show dialog"
                anchors.centerIn: parent
                onClicked: {
                    pageStack.push(dialogComponent)
                }
            }
        }
    }

    Component {
        id: dialogComponent
        Dialog {
            property string selectedOption: options.currentItem.text

            DialogHeader {
                id: header
                title: "Выберите опцию"
            }

            ComboBox {
                id: options
                label: "Options:"
                anchors.top: header.bottom
                width: parent.width

                menu: ContextMenu {
                    MenuItem { text: "Опция 1" }
                    MenuItem { text: "Опция 2" }
                    MenuItem { text: "Опция 3" }
                }
            }
        }
    }
}

В объекте типа Dialog имеются сигналы accepted и rejected , которые испускаются после принятия (нажатие кнопки Принять или выполнение жеста смахивания вперед) или отклонения (нажатие кнопки Отмена или выполнение жеста смахивания назад) диалога, соответственно. Для того, чтобы получить обратную связь после подтверждения выбора опции в диалоге, следует изменить первую страницу так, чтобы на ней отображался результат после испускания сигнала accepted():

Component {
    id: firstPage
    Page {
        PageHeader { id: header }

        Button {
            text: "Show dialog"
            anchors.centerIn: parent
            onClicked: {
                var dialog = pageStack.push(dialogComponent)
                dialog.accepted.connect(function() {
                    header.title = "Last selection: " + dialog.selectedOption
                })
            }
        }
    }
}

Примечание. Метод PageStack::push() возвращает экземпляр страницы, добавленной в стек; в данном случае это экземпляр объекта с идентификатором dialogComponent. Таким образом можно подключиться к сигналу accepted().

Поскольку заголовок страницы title обновляется только в случае принятия диалога, то он не будет обновлён, если пользователь изменит опцию, но не подтвердит ее нажатием кнопки Принять, а вернётся на предыдущую страницу нажатием кнопки Отмена. Например, такой подход можно использовать для сохранения приложением только подтвержденных пользователем данных.

Управление компоновкой и ориентацией пользовательского интерфейса

Использование объекта Theme для создания масштабируемых компоновок

Объект Theme настроен таким образом, что значения его свойств, отвечающих за размеры и отступы, динамически адаптируются в соответствии с пропорциями экрана и его разрешением. Этому объекту следует отдавать предпочтение вместо жёстко заданных размеров и отступов для компонентов приложения. Использование объекта Theme обеспечивает единство стиля оформления с другими приложениями ОС Аврора. Например:

  • Для задания отступов слева и справа между границами элемента Page и его содержимым следует использовать свойство Theme.horizontalPageMargin.
  • В объектах ListItem, состоящих только лишь из значка и текстовой метки, размер значка следует указывать равным Theme.iconSizeMedium, а размер шрифта текстовой метки — равным Theme.fontSizeMedium (стандартный размер шрифта для объектов Label).
  • В объектах ListItem, состоящих из двух строк текста, высота элемента (свойство contentHeight) обычно устанавливается равной Theme.itemSizeMedium.
  • Ширину одиночных кнопок следует устанавливать равной Theme.buttonWidthLarge.

Дополнительную информацию можно получить в описании типа Theme.

Динамическое обновление компоновки при смене ориентации экрана

При повороте устройства и смене ориентации экрана приложения ОС Аврора могут использовать соответствующие свойства для адаптации своего пользовательского интерфейса.

Для динамического обновления компоновки при смене ориентации экрана следует:

  1. Установить свойству allowedOrientations страницы значение для поддержки всех необходимых ориентаций экрана.
  2. С помощью свойств страницы isPortrait, isLandscape или orientation соответствующим образом обновлять пользовательский интерфейс при смене ориентации экрана.

В качестве примера можно рассмотреть приложение, которое показывает изображение и его основные метаданные (имя файла, ширина и высота). В портретной ориентации экрана изображени Image центрируется по горизонтали в верхней части экрана, а в столбце из объектов типа DetailItem показываются метаданные изображения:

import QtQuick 2.2
import Sailfish.Silica 1.0

ApplicationWindow {
    initialPage: Component {
        Page {
            id: page

            PageHeader {
                id: header
                title: "Image details"
            }

            Image {
                id: image
                anchors {
                    top: header.bottom
                    horizontalCenter: parent.horizontalCenter
                }
                sourceSize.width: page.width - (Theme.horizontalPageMargin * 2)
                fillMode: Image.PreserveAspectFit
                source: StandardPaths.pictures + "/img_0001.jpg"
            }

            Column {
                anchors {
                    top: image.bottom
                    topMargin: Theme.paddingLarge

                    left: parent.left
                    leftMargin: Theme.horizontalPageMargin

                    right: parent.right
                    rightMargin: Theme.horizontalPageMargin
                }

                DetailItem { label: "Name of file"; value: image.source }
                DetailItem { label: "Width"; value: image.width }
                DetailItem { label: "Height"; value: image.height }
            }
        }
    }
}

Чтобы страница начала реагировать на изменения ориентации, ее свойству allowedOrientations следует задать значение, соответствующее всем поддерживаемым ориентациям экрана. Значение может быть любой комбинацией из: Orientation.Portrait, Orientation.PortraitInverted, Orientation.Landscape, Orientation.LandscapeInverted. Кроме того, есть специальное значение Orientation.All для поддержки всех возможных ориентаций экрана, которое используется в примере:

Page {
    allowedOrientations: Orientation.All

    // остальной код страницы...
}

Теперь при смене ориентации экрана устройства приложение будет соответствующим образом обновлять значение свойства страницы orientation.

Однако, как в этом можно убедиться после запуска приложения, новая компоновка пользовательского интерфейса работает не самым лучшим образом в портретной ориентации экрана: если изображение имеет большие размеры, то область с метаданными может выйти за пределы экрана. Вместо того, чтобы добавлять контейнер SilicaFlickable для включения прокрутки, можно изменить компоновку пользовательского интерфейса так, чтобы она автоматически адаптировалась под портретную ориентацию экрана: изображение показывать слева, а область с метаданными — справа. Определить текущую ориентацию экрана можно с помощью значения свойства orientation. Также для удобства имеются два булевых свойства isPortrait и isLandscape, которые возвращают истинное значение, если ориентация портретная и альбомная, соответственно.

Следует внести следующие изменения в пример:

  1. Очистить значение свойства изображения horizontalCenter для альбомной ориентации, чтобы в этом случае изображение автоматически прикреплялось к левому краю.
  2. Изменить размеры изображения: в альбомной ориентации оно должно занимать половину ширины страницы, а отступ от правого края больше не требуется.

Ниже приведён пример кода с соответствующими изменениями:

Image {
    id: image

    anchors {
        top: header.bottom
        horizontalCenter: page.isPortrait ? parent.horizontalCenter : undefined
    }

    sourceSize.width: {
        var maxImageWidth = Screen.height/2
        var leftMargin = Theme.horizontalPageMargin
        var rightMargin = page.isPortrait ? Theme.horizontalPageMargin : 0
        return maxImageWidth - leftMargin - rightMargin
    }

    // остальной код изображения...
}

Далее следует адаптировать привязку столбца с метаданными в зависимости от текущей ориентации:

Column {
    anchors {
        // в альбомной ориентации верх столбца привязывается к нижней границе заголовка (вместо нижней границы изображения)
        top: page.isPortrait ? image.bottom : header.bottom
        topMargin: page.isPortrait ? Theme.paddingLarge : 0

        // в альбомной ориентации левая граница столбца привязывается к правой границе изображения
        // с отступом Theme.paddingLarge между ними
        left: page.isPortrait ? parent.left : image.right
        leftMargin: page.isPortrait ? Theme.horizontalPageMargin : Theme.paddingLarge

        right: parent.right
        rightMargin: Theme.horizontalPageMargin
    }

    // остальной код столбца...
}

Дополнительно можно настроить анимации перехода при смене одной ориентации на другую с помощью свойства orientationTransitions. Например, с его помощью можно анимировать изменение положения элементов на странице.

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

Жизненный цикл приложения

Состояния приложения

ОС Аврора является по-настоящему многозадачной операционной системой. Пользователь может при необходимости отправлять приложения в фоновый режим. Для поддержки таких режимов приложения могут обладать двумя состояниями:

Состояние Описание
Active Приложение занимает всё доступное пространство экрана
Background Приложение представляется в виде cover на домашнем экране

Определить состояние приложения можно с помощью свойства Qt.application.state. Значение данного свойства равно Qt.ApplicationActive, когда приложение работает в обычном режиме (активное состояние), и Qt.ApplicationInactive, когда приложение работает в фоновом режиме.

При работе в фоновом режиме приложение должно минимизировать потребление ресурсов. Все анимации по возможности должны быть приостановлены, а само приложение должно освободить неиспользуемые ресурсы:

Label {
    // Вращающаяся текстовая метка
    text: "Hello world!"
    anchors.centerIn: parent
    RotationAnimation on rotation {
        from: 0
        to: 360
        duration: 2000
        loops: Animation.Infinite
        running: Qt.application.state == Qt.ApplicationActive // but only when active
    }
}

Обложки приложения

Обложка приложения — это визуальное представление приложения, которое отображается на домашнем экране, когда приложение работает в фоновом режиме. Создается она с помощью типа Cover, а устанавливается в свойстве cover компонента ApplicationWindow. На обложке можно отобразить важную информацию из работающего приложения или предоставить ограниченный набор команд для взаимодействия с ним.

Ниже приведён пример приложения, которое отображает на экране палитру цветов (тип ColorPicker). После выбора цвета и перевода приложения в фоновый режим выбранный цвет будет отображаться на обложке:

import QtQuick 2.2
import Sailfish.Silica 1.0

ApplicationWindow {
    id: appWindow

    property color selectedColor

    initialPage: Component {
        Page {
            ColorPicker {
                onColorClicked: appWindow.selectedColor = color
            }
        }
    }

    cover: Component {
        Cover {
            Rectangle {
                anchors.fill: parent
                color: appWindow.selectedColor
            }
        }
    }
}

Примечание. Если приложение работает в фоновом режиме, все анимации и ресурсоемкие задачи на обложке следует запускать только тогда, когда значением свойства обложки status является Cover.Active. В примере выше свойство ApplicationWindow::cover задано как элемент типа Component. Более предпочтительным способом описания обложки приложения будет её реализация в виде отдельного QML-файла. URL этого файла передаётся в свойство cover , что в конечном итоге ускоряет запуск приложения за счёт экономии времени на компиляцию QML-компонента обложки.

Действия для обложки

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

Каждое действие для обложки описывается в объекте CoverAction. Все такие действия помещаются в контейнер CoverActionList, который, в свою очередь, помещается в объект Cover. В каждом объекте CoverAction в свойстве iconSource задаётся путь к значку, который отображается на кнопке действия. Само действие описывается в обработчике сигнала onTriggered. В предыдущем примере (с палитрой) кнопкой на обложке приложения можно было бы сбрасывать цвет на белый:

ApplicationWindow {
    cover: Component {
        Cover {
            id: appCover

            CoverActionList {
                CoverAction {
                    iconSource: "icon.png"
                    onTriggered: appWindow.selectedColor = "white"
                }
            }

            // остальной код обложки...
        }
    }
}

Остановка приложения

Пользователь может закрыть приложение в любое время. В этом случае все необходимые действия по освобождению ресурсов можно выполнять в обработчике Component.onDestruction компонента ApplicationWindow.

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

Пожалуйста ознакомьтесь с политикой использования cookies.