Оптимальное использование пикселей — адаптация к изменениям состояния просмотра

В Windows 8 ваши приложения выполняются на экранах разного размера и в разных состояниях просмотра. Пользователь может работать с приложением, прикрепив его к одной из сторон экрана 25-дюймового настольного монитора или развернув на весь экран 10-дюймового широкоэкранного планшетного ПК. Вы наверняка хотите, чтобы в каждом из приведенных случаев приложение полностью использовало доступное место. В этой статье я покажу, как отслеживать текущий размер и состояние просмотра вашего приложения в коде, и посоветую вам способы написания приложения в Windows 8 Consumer Preview, позволяющие обрабатывать изменения размера экрана и состояния просмотра.

На конференции //build/ мы изложили несколько идей о том, как разрабатывать приложения с расчетом на разные размеры экранов. Например, вы можете ознакомиться с выступлением о XAML или с выступлением о HTML. Недавно в блоге о создании Windows 8 мы также описали результаты проведенных нами исследований и поделились идеями по поводу масштабирования экрана. Часто для адаптации к изменяющемуся размеру экрана достаточно использовать только разметку без написания кода в явном виде. Но в некоторых случаях требуется отслеживать состояние просмотра, в котором находится приложение. То есть требуется определять, находится ли приложение в портретном, полноэкранном, прикрепленном режиме или режиме заполнения, и предусматривать соответствующую реакцию в коде. Например, если вы используете HTML ListView для отображения элементов, то вам может потребоваться использовать GridLayout в полноэкранном режиме и ListLayout в прикрепленном режиме, как показано на рисунке. Для XAML вам может потребоваться аналогичное переключение между элементами управления GridView и ListView. Чтобы выяснить, как это можно реализовать, давайте сначала рассмотрим, как определять изменения размера и состояния просмотра в коде.

В полноэкранном состоянии просмотра приложения метеопрогноза отображаются 3 ряда по 3 плитки, упорядоченные в виде сетки. В прикрепленном представлении справа отображаются те же плитки, упорядоченные в один столбец.

В полноэкранном состоянии просмотра слева используется разметка Grid (HTML) или элемент управления GridView (XAML),
на экране прикрепленного состояния просмотра справа используется разметка List (HTML) или элемент управления ListView (XAML).

Основные сведения об изменениях размера и состояния просмотра

Для приложений Windows 8, написанных на XAML и HTML, используется один базовый шаблон для обработки изменений размера экрана и состояния просмотра: просто прикрепите функции обратного вызова к подходящим событиям, предоставляемым соответствующими платформами, и выполните запрос нужных дополнительных сведений.

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

JavaScript

 function handleResize(eventArgs) {
    var appWidth = eventArgs.view.outerWidth;
    var appHeight = eventArgs.view.outerHeight;
    ...
}

window.addEventListener("resize", handleResize);

Аналогичным образом в XAML можно задать обработчик событий, используя событие SizeChanged для текущего окна:

C#

 private void OnWindowSizeChanged(object sender, Windows.UI.Core.WindowSizeChangedEventArgs e)
{
    double AppWidth = e.Size.Width;
    double AppHeight = e.Size.Height;
}

Window.Current.SizeChanged += OnWindowSizeChanged;

При программировании приложений для Windows 8 вы также можете использовать WinRT, чтобы напрямую запросить текущее состояние просмотра. WinRT предоставляет значение перечисления, сообщающее текущее состояние просмотра приложения.

JavaScript

 var currentViewState = Windows.UI.ViewManagement.ApplicationView.value;

C#

var

  CurrentViewState = Windows.UI.ViewManagement.ApplicationView.Value;

Приложения для Windows 8, написанные на JavaScript, поддерживают также функцию msMatchMedia. Эта функция работает через объект окна модели DOM и удобна при наличии состояния с несколькими медиазапросами, например сочетания полноэкранной альбомной ориентации и ширины экрана в 1600 пикселей:

JavaScript

 var isSnapped = window.msMatchMedia(“(-ms-view-state:fullscreen-landscape) and 
(min-width: 1600px”).matches;

JavaScript

 function handleSnappedMode(mql) {
if (mql.matches) {
        ...
        }
}
window.msMatchMedia(“(-ms-view-state:fullscreen-landscape) and (min-width: 
1600px”).addListener(handleSnappedMode);

Изменение размера и состояние просмотра в Consumer Preview

Теперь, когда мы рассмотрели основные механизмы доступа к сведениям о состоянии просмотра и размере экрана, давайте обсудим, как развернуть эти функциональные возможности для обработки изменения размера экрана. В Windows 8 Consumer Preview порядок событий изменения размера и изменения состояния просмотра детерминирован, так что события состояния просмотра (и сопоставленные функции обратного вызова) всегда предшествуют событиям изменения размера. Поэтому перед доступом к текущему состоянию просмотра всегда следует дожидаться возникновения события изменения размера. Это обеспечивает простой и согласованный подход к обработке изменений размера и состояния просмотра в коде. Ожидание возникновения событий изменения размера (и вызова сопоставленных функций обратного вызова) гарантирует, что возвращаемые сведения для текущей области отображения и состояния просмотра согласованы.

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

Предположим, вы хотите создать приложение, загружающее фоновые изображения разных размеров в зависимости от текущего разрешения экрана. В прикрепленном режиме вам может потребоваться загрузить небольшое изображение на фоновую плитку, в режиме заполнения может потребоваться изображение со стандартными пропорциями 4:3, а в полноэкранном режиме с альбомной ориентацией — изображение с пропорциями 16:9. Кроме того, в зависимости от высоты экрана вам может потребоваться выбрать разные разрешения изображения для каждого варианта пропорций.

Следуя этой рекомендации, мы задаем функцию обратного вызова на основе события изменения размера и используем WinRT API внутри нее для запроса текущего состояния просмотра, как показано ниже:

JavaScript

 function handleResize(eventArgs) {
    var currentViewState = Windows.UI.ViewManagement.ApplicationView.value;
    var appHeight = eventArgs.view.outerHeight;
    var appWidth = eventArgs.view.outerWidth;

    // downloadImage requires accurate view state and app size!
    downloadImage(currentViewState, appHeight, appWidth);
}

window.addEventListener("resize", handleResize);

 

C#

 private void OnWindowSizeChanged(object sender, Windows.UI.Core.WindowSizeChangedEventArgs e)
{
 var CurrentViewState = Windows.UI.ViewManagement.ApplicationView.Value;
 double AppWidth = e.Size.Width;
 double AppHeight = e.Size.Height;

    // DownloadImage requires accurate view state and app size!
    DownloadImage(CurrentViewState, AppHeight, AppWidth);
}

Window.Current.SizeChanged += OnWindowSizeChanged;

Напротив, если бы мы запрашивали размер области отображения приложения в функции обратного вызова, активируемой изменением состояния просмотра, мы бы получили правильные сведения о состоянии просмотра, но при этом размер области отображения приложения не успел бы обновиться. Так, если в указанных условиях наше приложение перешло из прикрепленного режима в полноэкранный режим с альбомной ориентацией, AppWidth может иметь значение 320 пикселей, а состояние просмотра может быть fullscreen-landscape. Если мы передадим эти значения в DownloadImage, то загрузим изображение неправильного размера!

Данная рекомендация по использованию обратных вызовов, активируемых событиями изменения размера, распространяется и на другие сценарии. Например, если бы при программировании на JavaScript вы хотели изменять разметку элемента управления ListView с Grid на List в зависимости от состояния просмотра, то вы могли бы задать выполнение обратного вызова при изменении состояния просмотра. Однако установка разметки ListView до изменения размера экрана привела бы к возникновению двух этапов разметки для элемента управления ListView — одного до изменения размера экрана, и одного после. (Элемент управления WinJS ListView автоматически обрабатывает события изменения размера, но не события состояния просмотра.) В отличие от примера DownloadImage, рассмотренного ранее, пользователь не сможет заметить разницу напрямую, но дополнительное время, затрачиваемое приложением на разметку, может привести к тому, что оно будет работать или реагировать медленнее. Поэтому перед запросом текущего состояния просмотра и заданием соответствующей разметки ListView лучше дождаться обратного вызова, обусловленного изменением размера экрана:

JavaScript

 function handleResize(eventArgs) {
    var isSnapped = (Windows.UI.ViewManagement.ApplicationView.value === 
Windows.UI.ViewManagement.ApplicationViewState.snapped);
    listView.layout = isSnapped ? new WinJS.UI.ListLayout() : new WinJS.UI.GridLayout();
}

window.addEventListener("resize", handleResize);

В XAML для переключения между этими разными состояниями используйте API VisualStateManager (VSM), чтобы определить, какие элементы XAML должны отображаться для отдельных представлений. Если вы используете инструменты Visual Studio 2011 Beta, то можете просмотреть пример этих API, определенных в шаблонах проектов для шаблона приложения таблицы:

XAML

 <VisualStateManager.VisualStateGroups>
    <!-- Visual states reflect the application's view state -->
    <VisualStateGroup>
        <VisualState x:Name="FullScreenLandscape"/>
        <VisualState x:Name="Filled"/>

        <!-- The entire page respects the narrower 100-pixel margin convention for portrait -->
        <VisualState x:Name="FullScreenPortrait">
            <!-- definition of what UI elements should display or change would go here in Storyboard 
elements -->
        </VisualState>

        <!-- The back button and title have different styles when snapped, and the list representation 
is substituted for the grid displayed in all other view states -->
        <VisualState x:Name="Snapped">
            <!-- definition of what UI elements should display or change would go here in Storyboard 
elements -->
        </VisualState>
    </VisualStateGroup>
</VisualStateManager.VisualStateGroups>

Обратите внимание на группы VisualState, определенные для разных состояний просмотра. Ваш код может изменять их при изменении состояния в событии SizeChanged:

C#

 private void OnWindowSizeChanged(object sender, Windows.UI.Core.WindowSizeChangedEventArgs e)
{
 // get the view state after the size has changed
 string CurrentViewState = Windows.UI.ViewManagement.ApplicationView.Value.ToString();
 
 // using VisualStateManager make sure we navigate to the correct state definition
    VisualStateManager.GoToState(this, CurrentViewState, false);
}

Как видно из данного примера, использование VSM в сочетании с API SizeChanged и ApplicationView предоставляет гибкий механизм адаптации к изменениям разметки экрана.

Заключение

В этой статье мы рассмотрели способы обнаружения событий изменения размера экрана и изменений состояния просмотра в коде. При использовании Consumer Preview я рекомендую перед отправкой в WinRT запроса сведений о состоянии просмотра ожидать события изменения размера экрана. Если вы будете следовать этой рекомендации, то при каждом изменении размера приложения ваш код будет получать правильные сведения о текущем размере экрана и состоянии просмотра. Дополнительные примеры можно найти в нашем примере в пакете Windows 8 SDK. Мы также призываем вас поделиться своими мыслями и отзывами по данному вопросу.

-- Крис Джонс (Chris Jones), руководитель программы, Windows

Благодарю Тима Хойера (Tim Heuer) за помощь в работе над статьей.