Хорошие UI представляют данные пользователям естественными и интуитивно понятными способами независимо от форм-фактора устройства. При создании современных пользовательских сред вывод данных и контента требует обновленных API, элементов управления и средств. В приложениях Windows Store объем необходимого кода и сложность элементов управления зависят от того, какой вид приложения вы создаете — офисное, игровое, финансовое или для социальных сетей. Элементы управления из Windows Library for JavaScript (WinJS) просты в освоении любым разработчиком, создающим приложения Windows Store на JavaScript, и в этой статье я рассмотрю данные элементы управления.
Новые парадигма и элементы управления UI в Windows 8
Приложения Windows Store выглядят и ведут себя во многом иначе, чем программы, выполняемые в предыдущих версиях Windows. ОС Windows основательно переделана, начиная со страницы Start, заполненной активными плитками (live tiles), которые представляют то или иное приложение. Другие очевидные изменения заключаются в том, что приложения Windows Store выполняются в полноэкранном режиме или в прикрепленном представлении (snapped view), размещая контент по центру, а команды и меню остаются вне поля зрения, пока пользователь не затребует их.
UI-элементы Windows, такие как кнопки «Свернуть», «Развернуть» и «Закрыть», которые когда-то были вездесущими, больше не существуют в приложениях Windows Store, поскольку сенсорный интерфейс и движения мышью сделали их бесполезными. Чтобы закрыть приложение, вам нужно сделать жест «смахивания» одним пальцем или провести курсор мыши по экрану сверху вниз. Даже меню больше не являются фундаментальной частью верхней строки каждого экрана. В приложениях Windows Store меню скрыты, пока определенным жестом через сенсорный интерфейс или мышь вы не откроете их внизу в панели приложения (AppBar), как показано на рис. 1, где в качестве примера используется небольшое приложение с таймерами обратного отсчета.
Рис. 1. AppBar внизу приложения
Как видно на рис. 1, меню раскрываются, а командные элементы представляют собой прежде всего графику с некоторым текстом в отличие от традиционных текстовых меню, где иногда попадались графические элементы. Кроме того, этими элементами удобно оперировать пальцами. Если вам нужно больше места для параметров, чем это возможно внизу, вы можете поместить навигационную панель, которая является просто AppBar вверху страницы.
Навигация по традиционным меню в Windows иногда бывает очень неудобной. Мы все морщимся от программы с каскадным меню, где на 13 уровне вложения вы забываете о том, что хотели найти. В приложениях Windows Store навигация вплетена в контент, так как жесты на элементах ListView открывают другие страницы. Жесты сжатия (pinch gestures) и нажатие клавиши Ctrl с одновременной прокруткой колеса мыши активируют контекстное масштабирование (semantic zoom) (bit.ly/16IDtdi), которое является одновременно парадигмой навигации и элементом управления в приложениях Windows Store. SemanticZoom — часть полного списка WinJS-элементов управления (bit.ly/w1jLM5).
Работа с HTML- и WinJS-элементами управления
В приложениях Windows Store, использующих JavaScript, существуют два основных вида элементов управления: стандартные HTML-элементы и WinJS-элементы управления. Последние — это HTML, скомбинированный с заранее скомпилированным JavaScript, изменяющий внешний вид HTML-элементов и расширяющий их поведение. Поскольку это HTML, вы можете присваивать стили WinJS-элементам управления с помощью CSS. На рис. 2 дан пример базового WinJS-элемента управления — WinJS DatePicker, который включает несколько элементов управления DropDown, представляющих день, месяц и год; этот DatePicker показывает вывод по умолчанию из следующего кода:
<span id="eventDate" data-win-control="WinJS.UI.DatePicker" />
Рис. 2. WinJS-элемент управления DatePicker
Конечно, к элементу управления DatePicker на рис. 2 не применено никаких стилей, помимо WinJS-стилей по умолчанию, но вы можете изменить это, переопределив WinJS CSS-селекторы .win-datepicker-date, .win-datepicker-month и .win-datepicker-year. Для применения стиля ко всему элементу управления используйте .win-datepicker.
Причина, по которой DatePicker (или любой другой WinJS-элемент) работает именно так, связана с HTML5-атрибутами data-*, а именно: data-win-control. Атрибут data-win-control обозначает тип элемента управления, рендеринг которого будет выполнять WinJS, поэтому, когда вы присваиваете WinJS.UI.DatePicker значение атрибута datawin-control, этот элемент управления визуализирует раскрывающиеся списки, как на рис. 2. Атрибут data-win-options позволяет задавать дополнительные свойства элементов управления. Например, в DatePicker можно указать data-win-options для отображения даты по умолчанию, а также минимальный и максимальный диапазоны дат. Хотя этот элемент называется DatePicker, его можно изменить так, чтобы он захватывал показатели времени, например часы, минуты и секунды.
Поскольку WinJS создает и визуализирует конечный вывод элемента управления, HTML на этапе разработки и HTML в период выполнения выглядят весьма по-разному. Рис. 3 демонстрирует HTML, который WinJS встраивает в хост-элемент в период выполнения. Это можно увидеть из DOM Explorer (Debug | Windows | DOM Explorer).
Рис. 3. DatePicker визуализирует три раскрывающихся списка «день-месяц-год»
<span class="win-datepicker" id="eventDate" role="group"
lang="en-US" dir="ltr" data-win-control="WinJS.UI.DatePicker">
<select tabindex="0" class="win-datepicker-month win-order0"
aria-label="Select Month">
<option value="January">January</option>
<option value="February">February</option>
<option value="March">March</option>
<option value="April">April</option>
<!-- Другие <options>, показывающие остальные месяцы -->
</select>
<select tabindex="0" class="win-datepicker-date win-order1"
aria-label="Select Day">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<!-- Другие <options>, показывающие остальные номера дней -->
</select>
<select tabindex="0" class="win-datepicker-year win-order2"
aria-label="Select Year">
<option value="1913">1913</option>
<option value="1914">1914</option>
<option value="1915">1915</option>
<option value="1916">1916</option>
<!-- Другие <options>, показывающие года -->
<option value="2112">2112</option>
<option value="2113">2113</option>
</select>
Код, поддерживающий WinJS-элементы управления, такие как DatePicker, находится в файле, расположенном по пути <ProjectRoot>\References\Windows Library for JavaScript 1.0\js\ui.js; там же содержатся некоторые базовые компоненты WinJS. Заметьте, что это та ссылка <script>, которая требуется в элементе <head> на страницах приложения Windows Store:
<script src="//Microsoft.WinJS.1.0/js/ui.js"></script>
Модифицируйте эти файлы на свой страх и риск, так как они состоят из базового WinJS-кода.
Любой WinJS-элемент управления, включая DatePicker, доступен в период выполнения через свойство winControl. В период выполнения WinJS дописывает к свойству winControl дочерние свойства, специфичные для типа WinJS-элемента. Например, ListView содержит свой список элементов, но вы можете запросить элемент WinJS.UI.Ratings рейтинг, выбранный пользователем. К свойству winControl элемента можно обращаться так:
var control = document.getElementById("WinJSElementId").winControl
Buttons,CheckBoxes, RadioButtons, DropDowns, TextBoxes и прочие работают точно так же, как в любой чистой HTML-странице; однако пространство имен WinJS.UI включает массу UI-элементов для многих сложных сценариев, в том числе всегда необходимые элементы управления списками.
Элементы управления списками и сетками
Приложениям многих типов нужно представлять данные в сетке или в списке, поэтому для таких сценариев, разумеется, предусмотрен элемент управления ListView, который можно визуализировать как сетку или список, в том числе с группированием и варьируемым размером элементов. ListView не только обладает высокой гибкостью, но и отлично работает в новой UI-среде Windows, выполняя такие операции, как автоматическое масштабирование под размеры экрана и размещение элементов списка в строках и столбцах, размер которых варьируется в зависимости от разрешения и размера экрана на устройстве.
Хотя большинство других WinJS-элементов управления автономно, ListView работают в тандеме с соответствующим HTML в качестве шаблона. То есть вам нужно подготовить как HTML-шаблон, так и контейнер самого элемента управления, как показано на рис. 4. Заметьте, что атрибуты data-win-control шаблона и data-win-options элемента ListView содержат параметры, которые связывают ListView с его шаблоном.
Рис. 4. HTML, необходимый для создания WinJS ListView
<div id="maincontent">
<div id="listViewTemplate"
data-win-control="WinJS.Binding.Template" >
<div data-win-bind="style.background: color" class="win-item">
<h1 data-win-bind=" innerText: daysToGo"></h1>
<h2 class="subtitle"
data-win-bind="innerText: eventTitle"></h2><br />
<h2 class="subtitle-bottom"
data-win-bind=" innerText: eventDate"></h2>
</div>
</div>
<div id="listView" data-win-control="WinJS.UI.ListView"
class="win-listview"
data-win-options=
"{ itemDataSource: Data.items.dataSource,
itemTemplate: select('#listViewTemplate'),
selectionMode: 'single'}"
>
</div>
</div>
На рис. 4 присутствуют два элемента <div>: один — для шаблона с идентификатором listViewTemplate, а второй — это сам ListView с именем listView. Элемент listViewTemplate содержит дочерние элементы, представляющие различные поля для каждого элемента в списке или сетке, например eventTitle или eventDate. На рис. 4 видно, что свойству itemDataSource присваивается Data.items.dataSource, а значит, Data — это пространство имен, а items — объект WinJS.Binding.List, заполняемый данными. Поскольку JavaScript работает со слабо типизированными данными, вам нужно лишь передать массив объектов в конструктор List, после чего он готов к связыванию с элементами управления ListView — примерно как в следующем коде:
var items = [
{ eventTitle: "Rachel's Birthday",
eventDate: new Date(2014, 1, 13) },
{ eventTitle: "Rachel's BFF's Birthday",
eventDate: new Date(2013, 5, 29) }
];
var list = new WinJS.UI.list(events);
В качестве альтернативы можно использовать метод push для заталкивания элементов в объект List вместо передачи массива конструктору List. Лучший способ управления данными в ListView — предоставлять соответствующие операции (добавление, удаление и т. д.) через элемент управления AppBar.
Панели приложения и команды
«Контент превыше хрома» (content over chrome) — важный принцип дизайна в Microsoft. Панели приложения являются неотъемлемой частью этого принципа дизайна, поскольку они остаются вне поля зрения и выводятся лишь того, когда они нужны вам. В коде AppBar — просто <div>, который содержит один или более элементов <button> (они называются командами панели приложения) с атрибутами data-win-control, установленными в WinJS.UI.AppBarCommand. Индивидуальные команды AppBar различаются атрибутами data-win-options, как вы, вероятно, догадались.
Изучив data-win-options на рис. 5 для каждой команды AppBar, вы увидите параметры id, label, icon и section для них. Вы можете назначить кнопки AppBar глобальному разделу AppBar (который отображается справа внизу на экране приложения) или присвоить параметру section значение «selection» (для вывода внизу слева). Присваивание параметру section команд AppBar значения «selection» делает их контекстно-зависимыми: они появляются, когда пользователь выбирает какой-то элемент в ListView тем или иным жестом.
Рис. 5. Создание AppBar
<!-- HTML -->
<div id="appbar" class="win-appbar"
data-win-control="WinJS.UI.AppBar">
<button data-win-control="WinJS.UI.AppBarCommand"
data-win-options
="{id:'deleteButton',
label:'Delete',
icon:'delete', section:'selection'}"
type="button"></button>
<button data-win-control="WinJS.UI.AppBarCommand"
data-win-options
="{id:'addButton',
label:'Add', icon:'add',
section:'global'}"
type="button"></button>
<button
data-win-control="WinJS.UI.AppBarCommand"
data-win-options
="{id:'refreshButton',
label:'Refresh',
icon:'refresh',
section:'global'}"
type="button"></button>
</div>
// JavaScript
document.getElementById("addButton").addEventListener(
"click", this.addButtonClick);
document.getElementById("deleteButton").addEventListener(
"click", this.deleteButtonClick);
document.getElementById("refreshButton").addEventListener(
"click", this.refreshButtonClick);
В связанном с HTML-страницей JavaScript-файле подключите слушатели событий к кнопкам AppBar точно так же, как вы это сделали бы для любого другого HTML-элемента. Чтобы появилась сама панель приложения, никакие слушатели не нужны, так как она автоматически появляется и скрывается в ответ на команды пользователя (хотя ее можно вызывать и программным способом). Пример на рис. 5 показывает законченную AppBar с кнопками для добавления, удаления и обновления данных.
Вы можете писать код для отображения, скрытия, включения и выключения кнопок AppBar так, как того требует ваш сценарий.
Всплывающие элементы
Поскольку сенсорный экран — важная часть устройств, вы, по-видимому, замечали, что, когда появляются UI-элементы и диалоговые окна, их можно легко отправить обратно простым касанием пальца или щелчком кнопки мыши в любой части экрана вне самого диалога. Эта концепция неявного прекращения диалога называется упрощенным закрытием (light dismiss) и лежит в основе поведения по умолчанию MessageDialog и PopupMenu в Windows 8 — она намного легче в использовании, чем кнопки закрытия.
Как и предыдущие элементы управления, Flyout использует атрибут datawin-control, чтобы указать, что это действительно элемент управления WinJS.UI.Flyout. Рендеринг дочерних элементов осуществляется внутри Flyout. Например, вы могли бы поместить HTML-форму в Flyout, чтобы пользователь мог заполнить название и дату предстоящего мероприятия. Соответствующий код показан на рис. 6, а его результат — на рис. 7.
Рис. 6. Всплывающий элемент для сбора информации с помощью WinJS-элементов управления
<!-- HTML -->
<div id="eventFlyoutPanel" data-win-control="WinJS.UI.Flyout">
<table width="100%" height="100%">
<tr><td>Event Title:</td><td><input type="text"
id="eventTitle"/></td></tr>
<tr><td>Event Date:</td><td id="eventDate"
data-win-control="WinJS.UI.DatePicker"></td></tr>
<tr><td> </td><td align="right">
<input type="button" id="confirmButton" value="Submit" /></td></tr>
</table>
</div>
// JavaScript
addButtonClick: function () {
document.getElementById("eventFlyoutPanel").winControl.show(
"addButton", "top");
}
Рис. 7. Всплывающий элемент для сбора информации
Заметьте, что Flyout на рис. 7 — это всего лишь HTML-форма. Когда пользователь касается или щелкает команду Add в AppBar, появляется Flyout, как задано в функции addButtonClick на рис. 6. Элементы Flyout появляются на экране относительно других элементов управления, поэтому при вызове метода winControl.show вы передаете имя элемента привязки, а также место размещения, т. е. рядом с верхним или нижним краем элемента привязки.
Пользователь может коснуться или щелкнуть любое место вне Flyout, чтобы убрать его, так как это элемент управления с упрощенным закрытием (light-dismiss control). Вы заметите отсутствие модальных диалоговых окон в приложениях Windows Store, что является частью философии дизайна Microsoft. Те, кто занимается дизайном, давно морщились от модальных диалоговых окон — все, что раздражает пользователя или лишает его свободы в использовании, считается плохим дизайном.
Другой тип Flyout — это SettingsFlyout, который кардинально меняет принцип управления предпочтениями пользователей, принятый в приложениях для более ранних версий Windows.
Настройки приложения
Пользователям Windows знакомы команды меню Tools | Options или Help | About, открывающие диалоги с лабиринтом настроек. К счастью, в мире приложений Windows Store эти диалоги заменены на нечто, более интуитивно понятное пользователям. Страницы Settings и About теперь действуют как вертикальные элементы управления Flyout и вызываются, когда пользователь выбирает значок Settings из набора так называемых чудо-кнопок Windows (bit.ly/146cniM).
Настройки вызываются в приложениях Windows Store одинаково. Когда пользователь выбирает чудо-кнопку Settings, один и тот же SettingsFlyout появляется справа независимо от того, какое приложение выполняется в данный момент. В этом SettingsFlyout содержатся ссылки на вашу политику конфиденциальности, предпочтения, справку и т. д. Создание ссылок на страницу политики конфиденциальности или параметров требует всего нескольких строк кода в обработчике события app.onactivated, который обычно находится в /js/default.js:
// В app.onactivated (default.js)
WinJS.Application.onsettings = function (e) {
e.detail.applicationcommands =
{ "privacypolicy": { title: "Privacy Policy",
href: "privacy.html" } };
WinJS.UI.SettingsFlyout.populateSettings(e);
};
Как только пользователь выбирает одну из ссылок в Settings, появляется соответствующий Flyout. На рис. 8 показан HTML для SettingsFlyout, содержащего информацию о политике конфиденциальности (четкая и ясная политика конфиденциальности является одним из требований при публикации в Windows Store).
Рис. 8. HTML для SettingsFlyout, содержащего информацию о политике конфиденциальности
<div id="settingsFlyout"
data-win-control="WinJS.UI.SettingsFlyout"
data-win-options="{settingsCommandId:'privacypolicy', width:'narrow'}">
<div class="win-header" style="background-color:#312e2e">
<button type="button" onclick="WinJS.UI.SettingsFlyout.show()"
class="win-backbutton"></button>
<div class="win-label">Privacy Policy</div>
</div>
<div class="win-content">
<div class="win-settings-section">
<p>This application does not collect any personal information.</p>
<p>Internet access is only used to retrieve data from the web,
or to allow you to contact the developer:</p>
<p>
<a href="mailto:rachel@rachelappel.com">Email Rachel Appel </a>
<br />
<a href="http://rachelappel.com/privacy-policy"
target="_blank">View privacy statement online</a>
</p>
</div>
</div>
</div>
Не забудьте присвоить файлу параметров политики конфиденциальности то же имя, что и в аргументе href, используемом для регистрации Flyout (рис. 8).
В Settings можно помещать не только правила конфиденциальности. SettingsFlyouts может содержать любой допустимый HTML, часто является хостом для ToggleSwitch, CheckBox и DropDown и функционирует так же, как сейчас диалоги Tools | Options. Однако, как упоминалось, SettingsFlyout — это элементы управления с упрощенным закрытием, поэтому простое касание вне окна такого элемента заставляет их исчезать в противоположность модальным диалогам. Еще одна простая, но новая парадигма в приложениях Windows Store — элемент управления SemanticZoom, удобное вспомогательное средство при навигации.
Контекстное масштабирование
Некоторые приложения интенсивно работают с данными. Навигация в таких приложениях может быть довольно трудной, особенно когда они обрабатывают много данных. Здесь и вступает в игру контекстное масштабирование (semantic zoom). Оно позволяет вам выражать два режима визуализации данных: с увеличенным масштабом (zoomed in) и с уменьшенным (zoomed out). В режиме увеличенного масштаба (по умолчанию) показываются все возможные данные, и пользователь должен панорамировать их или прокручивать. В режиме уменьшенного масштаба обычно формируется агрегатное представление данных, которое облегчает пользователю переход к нужной области данных с последующим масштабированием этой области до конкретного элемента данных.
SemanticZoom — это набор из трех элементов управления: хост-элемента и двух элементов с поддержкой масштабирования, как показано на рис. 9. Дочерние элементы управления должны реализовать интерфейс IZoomable, чтобы участвовать в контекстном масштабировании, поэтому в WinJS-приложениях на эту роль годится только ListView.
Рис. 9. Код для элемента управления SemanticZoom
<div id="semanticZoomDiv" data-win-control="WinJS.UI.SemanticZoom">
<!-- Представление с увеличенным масштабом -->
<div id="zoomedInListView"
data-win-control="WinJS.UI.ListView"
data-win-options
="{ itemDataSource:
myData.groupedItemsList.dataSource,
itemTemplate: select('#mediumListIconTextTemplate'),
groupHeaderTemplate: select('#headerTemplate'),
groupDataSource: myData.groupedItemsList.groups.dataSource,
selectionMode: 'none',
tapBehavior: 'none',
swipeBehavior: 'none' }"
> </div>
<!-- Представление с уменьшенным масштабом -->
<div id="zoomedOutListView"
data-win-control="WinJS.UI.ListView"
data-win-options
="{ itemDataSource:
myData.groupedItemsList.groups.dataSource, itemTemplate:
select('#semanticZoomTemplate'), selectionMode: 'none',
tapBehavior: 'invoke', swipeBehavior: 'none' }"
>
</div>
</div>
Как видите, семантическое масштабирование — это просто переключение между двумя ListView, удобное для пользователей и несложное в реализации для разработчиков.
Другие элементы управления
Частью новой среды Windows являются и другие элементы управления, например ProgressBar, FlipView, всплывающие меню, MessageDialog и Ratings, но у меня нет места в этой статье обсуждать их все. Открытые стандарты, такие как HTML5 и ECMAScript 5 (ES5), являются основой WinJS, поэтому все эти элементы превосходно работают как часть платформы разработки приложений Windows Store.