Макет
Разные состояния просмотра приложения
Если в чем-то, касающимся макета приложения для Магазина Windows, и можно быть уверенным, так в том, что режим его отображения, весьма вероятно, будет часто меняться. Во-первых, авто-поворот экрана, особенно, на планшетных, переносных устройствах - делает очень простым и быстрым переключение между альбомной и портретной ориентациями экрана (пользователю не придётся столкнуться с настройкой драйвера дисплея). Во-вторых, устройство может быть подключено к внешнему дисплею, а это означает, что приложение нуждается в самостоятельной настройке на различные разрешения, и, возможно, на различные плотности пикселей. В-третьих, у пользователя есть возможность, в альбомном режиме, "прикреплять" приложение у левой или правой части экрана, когда прикрепленное приложение отображается в области шириной в 320 пикселей, а другое приложение выводится в зоне "заполняющего" просмотра, занимая остаток дисплея. Это можно сделать, используя жесты, мышь, или используя сочетания клавиш Win+. (точка), Win+> (Shift+точка). (Для прикрепленного режима требуется, как минимум, дисплей с разрешением 1366х768, иначе он будет отключен).
Вам, определенно, захочется протестировать своё приложение со всеми этими вариантами: состояния просмотра, размеры дисплеев, плотностью пикселей. Работу в разных состояниях просмотра можно тестировать напрямую, на любом компьютере, а вот для двух последних вариантов нужны специальные инструменты, которые предоставляют имитатор Visual Stuido и закладка Устройство (Device) в Blend ,позволяя вам имитировать различные условия. Нас сейчас интересует вопрос, как приложение будет действовать при различных условиях просмотра.
Состояния просмотра
В "Жизненный путь приложений для Магазина Windows: Характеристики платформы Windows 8" были представлены четыре состояния просмотра, вспомнить о них вы можете, посмотрев на Рис. 1.6. Добавим сейчас следующий уровень точности к их описанию, рассмотрев следующую таблицу. Она включает в себя изображения пространства, которое занимают приложения, описания состояний просмотра и идентификаторов этих состояний и в WinRT (в перечислении Windows.UI.ViewManagement.ApplicationViewState (http://msdn.microsoft.com/library/windows/apps/windows.ui.viewmanagement.applicationviewstate.aspx), и в характеристике -ms-view-state (http://msdn.microsoft.com/library/windows/apps/hh465826.aspx) CSS-медиазапросов.
Пространство, занимаемое приложением (Голубое) | Подробности |
---|---|
Приложение занимает весь экран в альбомном режиме. WinRT: fullScreenLandscape -ms-view-state: fullscreen-landscape | |
Приложение занимает либо левую, либо правую часть экрана в альбомном режиме, на области, ограниченной шириной в 320 пикселей. Это означает, что вам не нужно создавать дизайн для всех возможных размеров среди прикрепленных, заполненных (смотрите ниже) и полноэкранных состояний просмотра. WinRT: snapped -ms-view-state: snapped | |
Приложение занимает область экрана, находящуюся рядом с прикрепленным приложением. Ширина области, доступной приложению, равняется ширине экрана за вычетом 320 пикселей и 22 пикселей для элемента-разделителя. WinRT: filled -ms-view-state: filled | |
Приложение в портретном режиме WinRT: fullScreenPortrait -ms-view-state: fullscreen-portrait |
Снова хочу напомнить, что каждую страницу вашего приложения нужно подготовить для всех четырех режимов просмотра (с некоторыми исключениями, которые описаны во врезке "Предпочтительная ориентация и блокировка ориентации"). Режимы просмотра всегда находятся под контролем пользователя, поэтому любая страница может оказаться в любом режиме просмотра в любое время, даже при старте. Повторяйте этот как мантру, так как многие разработчики и дизайнеры об этом забывают!
Дизайн приложения, таким образом, включает все режимы просмотра для каждой страницы, так же, как мы поступили с описаниями страниц "Here My Am!" в "Быстрый старт" . В то же время, обработка режимов просмотра для каждой страницы не подразумевает четыре разных реализации приложения. Режимы просмотра это не более чем разные визуальные представления одного и того же содержимого страниц, как описано в "Руководстве по прикрепленному и заполненному представлениям" (http://msdn.microsoft.com/library/windows/apps/hh465371.aspx). Таким образом, переключение между режимами просмотра всегда сохраняет состояние приложения и его страниц - оно никогда не изменяет режим работы приложения или не осуществляет навигацию на другую страницу. Единственное исключение из этого правила сущестует, если приложение по веским причинам не может работать в прикрепленном режиме (как, например, игра, которой нужно определенное экранное пространство). В таком случае приложение может вывести сообщение об этом, вместе с инструкцией вроде "Прикоснитесь здесь, чтобы продолжить", что позволяет пользователю выразить таким образом своё намерение. В ответ на команду пользователя, приложение может вызвать Windows.UI.ViewManagement.Application-View.tryUnsnap, (http://msdn.microsoft.com/library/windows/apps/windows.ui.viewmanagement.applicationview.aspx), как показано в примере "Прикрепленный режим", (http://code.msdn.microsoft.com/windowsapps/Snap-Sample-2dc21ee3)2tryUnsnap - это единственный программный API, который может воздействовать на состояния просмотра. Состояния просмотра, другими словами, всегда меняются по инициативе пользователя, и нет API для установки состояний просмотра, и нет способа для того, чтобы задавать их при запуске приложения.. Не используйте, однако, эту возможность для того, чтобы "срезать углы". Попытайтесь сохранить как можно больше возможностей приложения в прикрепленном режиме.
С другой стороны, некоторые приложения вынуждены решать проблему дополнительного пространства по вертикали. Широкоэкранное видео в прикрепленном режиме займёт лишь небольшую частьт этого пространства, оставив место, скажем, для дополнительной информации о видео, рекомендаций, плей-листов и так далее, что обычно не видно, когда видео проигрывается на всём экране. В подобном случае, пользователь найдёт дополнительную информацию, переключившись в прикрепленный режим.
Врезка: Предпочтительная ориентация и блокировка ориентации
Состояния просмотра, с другой стороны, позволяют приложениям запуститься в заданном состоянии или/и заблокировать ориентацию, эффективно игнорируя изменения между альбомным и портретным режимом. Проигрыватель видеофайлов, например, обычно гораздо лучше чувствует себя в альбомном режиме, полагая, что полноэкранный альбомный и полноэкранный портретный режимы абсолютно одинаковы - таким образом, вы можете смотреть видео, лёжа на боку с планшетом, поставленным боком на стул.
Для ясности, приложение всё еще должно работать в трех режимах: полноэкранном альбомном, в режимах заполняющего и прикрепленного просмотра. Предпочтительная ориентация обычно касается выбора между альбомной и портретной, и это влияет на ориентацию экрана-заставки и других страниц приложения. Это, кроме того, позволяет автоматически менять режим отображения, когда вы перелючаетесь между вашими приложениями и другими, у которых нет похожих предустановок.
Для того чтобы сообщить Windows об этих предустановках, установите подходящие флаги в группе параметров Поддерживаемые ориентации (Supported Orientations) на закладке Интерфейс приложения (Application UI) в редакторе манифеста:
Множество подробностей о том, как всё это работает, можно найти в справке по InitialRotationPreference (http://msdn.microsoft.com/library/windows/apps/Hh700342.aspx). Я хочу, кроме того, расссказать о свойствах Windows.Graphics.Display.DisplayProperties.autoRotationPreferences (http://msdn.microsoft.com/library/windows/apps/windows.graphics.display.displayproperties.autorotationpreferences.aspx) и currentOrientation (http://msdn.microsoft.com/library/windows/apps/windows.graphics.display.displayproperties.currentorientation.aspx) дл программного управления ориентацией. Для демонстрации, обратитесь к примеру "Предустановки автоповорота устройства" (http://code.msdn.microsoft.com/windowsapps/Auto-Rotation-Preferences-87ae2902).
Обработка состояний просмотра
Как я уже упоминал, обработка различных состояний просмотра не подразумевает ни изменение режима работы приложения, ни создание новых страниц для разных режимов. В общем случае, вам следует попытаться, чтобы возможности приложения были одинаковыми в разных режимах, но в случае прикрепленного режима просмотра, уменьшенная полезная площадь экрана вынуждает к упрощению содержимого.
Лучше всего думать о состояниях просмотра в терминах видимости элементов, их размеров, взаимного расположения на странице. При таком подходе, основная часть работы может быть выполнена посредством CSS-медиа запросы с использованием возможности -ms-view-state. Мы видели это в приложении "Here My Am!" из "Быстрый старт" . Шаблон проекта Приложение таблицы так же это демонстрирует. Вот как этим медиа-запросы выглядят в CSS:
@media screen and (-ms-view-state: fullscreen-landscape) { /* ... */ } @media screen and (-ms-view-state: filled) { /* ... */ } @media screen and (-ms-view-state: snapped) { /* ... */ } @media screen and (-ms-view-state: fullscreen-portrait) { /* ... */ } /* Синтаксис для комбинирования медиа-запросов (разделены запятыми) */ @media screen and (-ms-view-state: fullscreen-landscape), screen and (-ms-view-state: fullscreen-portrait), screen and (-ms-view-state: filled) { /* ... */ }
Кроме того, весьма разумно добавить другие выражения к этим запросам, такие, как and (min-width: "1600px"), так как вы можете выполнять множество различных настроек, основываясь на разрешении экрана.
Для приложений Магазина Windows применяйте возможности обработки режимов просмотра в медиа-запросах вместо состояний, хранящихся в свойстве CSS orientation (альбомная (landscape) и портретная (portrait)), которые просто получаются путём анализа относительной ширины и высоты дисплея и не отличают состояния наподобие прикрепленного (snapped). Другими словами, состояния просмотра в Windows специфичны для платформы и отражают состояния, описаний которых стандартный CSS не имеет, помогая вашему приложению знать не только о доступном ему пространстве экрана, но и о режиме, в котором оно исполняется3Таким образом, состояния просмотра не сообщаются страницам, загруженным в iframe, в веб-контексте. Подобные страницы могут использовать стандартные CSS-медиа запросы для того, чтобы сделать вывод о состоянии просмотра, или окружающая страница, исполняющаяся в локальном контексте, может передать сведения о состоянии просмотра в iframe с помощью postMessage..
Например, следуя стандартному алгоритму CSS, и полноэкранный портретный (fullscreen portrait), и прикрепленный (snapped) режимы детектируются как orientation: portrait, так как соотношение сторон экрана в таких режимах указывает на его вертикальное расположение. Однако, прикрепленный режим подразумевает иное намерение пользователя, чем полноэкранный портретный. В прикрепленном режиме вы скорее хотите видеть наиболее важные части приложения, нежели копию портретного макета в пространстве шириной 320 пикселей.
Обычный подход заключается в том, чтобы размещать правила, касающиеся полноэкранного альбомного режима просмотра, в верхней части CSS-файла и затем выполнять тонкие настройки внутри конкретных медиа-запросов. Мы сделали это с "Here My Am!" в "Быстрый старт" , где стиль по умолчанию работает для режимов fullscreen-landscape и filled, и нам нужно было задать специфические правила лишь для режимов snapped и fullscreen-portrait.
В некоторых случаях обработка медиа-запросов в декларативном CSS недостататочна. Когда основное содержимое отображается на странице, содержащей ListView с макетом gridLayout, которая прокручивается в горизонтальном направлении, обычно макет переключают на ListLayout при переходе в прикрепленный режим. Вы можете, кроме того, как показано в материале: "Руководство по прикрепленному и заполненному представлениях" (http://msdn.microsoft.com/library/windows/apps/hh465371.aspx), преобразовать список кнопок в единый выпадающий элемент select для того, чтобы предложить пользователю те же функциональные возможности посредством более компактного пользовательского интерфейса. Для реализации подобного вам понадобится JavaScript.
Для подобных целей вы можете задействовать стандартное Media Query Listener API в JavaScript. Данный интерфейс (часть W3C CSSOM View Module, http://dev.w3.org/csswg/cssom-view/) позволяет вам добавлять обработчики к изменениям состояний медиа-запросов. Для того, чтобы прослушивать событие перехода в режим прикрепленного просмотра, вы можете использовать код, подобный этому:
var mql = window.matchMedia("(-ms-view-state: snapped)"); mql.addListener(styleForSnapped); function styleForSnapped() { if (mql.matches) { //... } // Создайте прослушиватели для других состояний просмотра: full-screen, fill, и device-portrait // или обработайте все меди-запросы в едином обработчике, проверяя в нём текущее состояние просмотра.
Вы можете видеть, что строка медиа-запроса, которую передают в window.matchMedia, это та же строка, котрая используется в CSS, и в обработчике вы, конечно, можете сделать всё, что нужно с помощью JavaScript.
Обрабатывая изменения режимов просмотра (или события window.onresize), вы можете получить точные размеры окна приложения посредством свойств window.innerWidth и window.innerHeight. Свойства document.body.clientWidth и document.body.clientHeight позволяют узнать ту же информацию, что и из свойств clientWidth и clientHeight любого элемента (наподобие div), который занимает 100% тела документа. Внутри события resize так же доступны свойства args.view.outerWidth и args.view.outerHeight.
В CSS так же доступны сведения о высоте и ширине окна просмотра (vh и vw, соответственно). Вы можете поставить перед ними префикс в виде процентов, например, 100vh - это 100% высоты окна просмотра, и 3.5vw - это 3.5% ширины окна просмотра. Эти переменные так же могут быть использованы в выражениях CSS calc.
Текущий режим просмотра доступен посредством свойства Windows.UI.ViewManagement.ApplicationView.value. Его значения берутся из перечисления Windows.UI.ViewManagement.ApplicationViewState, как показано в вышеприведенной таблице. В предыдущих лекциях мы видели применение этих механизмов. Например, элементы управления страниц (речь о них шла в "Анатомия приложения и навигация по страницам" ) обычно проверяют состояние просмотра в их методе ready и напрямую получают эти состояния в своём методе updateLayout. На самом деле, каждый метод элемента управления страницы groupedItem в проекте Приложение таблицы чувствителен к изменению состояния просмотра. Взгляните на код, взятый из pages/groupedItems/groupedItems.js:
// Несколько строк и комментариев опущено var appView = Windows.UI.ViewManagement.ApplicationView; var appViewState = Windows.UI.ViewManagement.ApplicationViewState; var nav = WinJS.Navigation; var ui = WinJS.UI; ui.Pages.define("/pages/groupedItems/groupedItems.html", { initializeLayout: function (listView, viewState) { if (viewState === appViewState.snapped) { listView.itemDataSource = Data.groups.dataSource; listView.groupDataSource = null; listView.layout = new ui.ListLayout(); } else { listView.itemDataSource = Data.items.dataSource; listView.groupDataSource = Data.groups.dataSource; listView.layout = new ui.GridLayout({ groupHeaderPosition: "top" }); } }, itemInvoked: function (args) { if (appView.value === appViewState.snapped) { // Если страница в прикрепленном режиме, пользователь вызывает группу. var group = Data.groups.getAt(args.detail.itemIndex); nav.navigate("/pages/groupDetail/groupDetail.html", { groupKey: group.key }); } else { // Если страница не в прикрепленном режиме, пользователь активирует элемент. var item = Data.items.getAt(args.detail.itemIndex); nav.navigate("/pages/itemDetail/itemDetail.html", { item: Data.getItemReference(item) }); } }, ready: function (element, options) { // ... this.initializeLayout(listView, appView.value); // ... }, // Эта функция обновляет макет страницы в ответ на изменения viewState. updateLayout: function (element, viewState, lastViewState) { var listView = element.querySelector(".groupeditemslist").winControl; if (lastViewState !== viewState) { if (lastViewState === appViewState.snapped || viewState === appViewState.snapped) { var handler = function (e) { listView.removeEventListener("contentanimating", handler, false); e.preventDefault(); } listView.addEventListener("contentanimating", handler, false); this.initializeLayout(listView, viewState); } } } } });
В первую очередь, метод initializeLayout, который вызывается и из ready и из updateLayout проверяет текущее состояние просмотра и соответствующим образом настраивает элемент управления ListView. Если вы помните из "Коллекции и элементы управления для вывода коллекций" , во время исполнения программы можно и настраивать ListView и менять его источник данных. Здесь мы используем ListLayout со списком групп в прикрепленном состоянии просмотра и gridLayout со сгруппированными элементами в других режимах. Это показывает, как мы показываем то же самое содержимое, но в более сжатом формате, скрывая отдельные элементы в прикрепленном режиме. Из-за этого itemInvoked так же проверяет режим отображения, так как элементы списка - это группы в прикрепленном режиме и в таком случае навигация осуществляется на страницу сведений о группе вместо перехода на страницу детальной информации об элементе.
Что касается updateLayout, он активируется из обработчика события window.onresize в коде PageControlNavigator (смотрите js/navigator.js в шаблоне проекта Приложение таблицы). Этот обработчик передаёт сведения о новом и предыдущем состоянии просмотра в updateLayout. Если эта функция обнаруживает, что мы переключились в прикрепленный режим или переключились из него, она сбрасывает к исходному состоянию ListView посредством initializeLayout. И, так как мы меняем источник данных ListView, здесь нет нужды в воспроизведении анимации входа или перехода. Небольшая уловка, работающая с событием contentanimating просто подавляет анимацию.
Врезка: Физическая ориентация экрана
Полноэкранный альбомный и полноэкранный портретный режимы просмотра предоставляют некоторую информацию о том, как устройство, на самом деле, ориентировано в пространстве, но подобная информация может быть гораздо точнее получена из свойств объекта Windows.Graphics.Display.DisplayProperties (http://msdn.microsoft.com/library/windows/apps/windows.graphics.display.displayproperties.aspx). В частности, свойство currentOrientation содержит значение из перечисления Windows.Graphics.Display.DisplayOrientations (http://msdn.microsoft.com/library/windows/apps/windows.graphics.display.displayorientations.aspx), что показывает, как устройство повёрнуто по отношению к nativeOrientation (и вызывает, при необходимости, событие orientationchanged). Эти данные позволят вам узнать, например, находится ли устройство экраном вниз, по отношению к небу, что может быть полезным для любого приложения, реализующего дополненную реальность, такую, как звёздная карта.
Похожим образом, API Windows.Devices.Sensors (http://msdn.microsoft.com/library/windows/apps/windows.devices.sensors.aspx), в частности классы, SimpleOrientationSensor и OrientationSensor могут предоставить больше информации от сенсоров. Разговор об этом пойдёт в "Анатомия приложения и навигация по страницам" курса "Пользовательский интерфейс приложений для Windows 8, созданных с использованием HTML, CSS и JavaScript".