Когда объект MSGesture получает несколько указателей через свое событиет addPointer, он так же запускает события MSGestureStart, MSGestureChange, MSGestureEnd для жестов поворота и масштабирования, вместе с событием MSInertiaStart. В подобных случаях свойства scale, rotation, velocityAngular, expansion, и velocityExpansion в объекте eventArgs приобретают смысл.
Вы можете выборочно просматривать эти свойства для событий MSGestureChange посредством выпадающего списка, который расположен в правом верхнем углу, в упражнении PointerEvents. Вы можете заметить, что, когда вы выполняете многоточечные жесты в имитаторе Visual Studio, вы никогда не видите событие MSGestureTap для отдельных точек касания. Это происходит потому, что распознаватель жестов может видеть подобные множественные события MSPointerDown, которые происходят почти одновременно (здесь играет роль свойство hwTimestamp) и тут же объединяет их в одно событие MSGestureStart (например, начинает жест масштабирования или вращения).
Теперь я уверен, что вы готовы задать несколько важных вопросов. До сих пор я говорил о сенсорных жестах изменения масштаба и вращения, о жестах переноса, как о рахных вещах, но как, на самом деле, различать их, если все они поступют в приложение посредством одного и того же события MSGestureChange? Разве это не запутывает все? Какова стратегия для работы с жестами переноса, вращения и масштабирования?
Хорошо, ответ заключается в том, что вам не нужно их разделять! Что, если вы подумаете немного о том, как вы обрабатываете события MSGestureChange и данные, которые каждое из них содержит в зависимости от вида манипуляции, который вы поддерживаете в пользовательском интерфейсе:
scale и rotation и применяете только необходимые, такие, как translationX и translationY. Это было бы ожидаемым поведением при выборе элемента в элементе управления коллекцией, например (или в элементе управления, который позволяет манипулировать элементами по технологии "перетащить и опустить", меняя порядок их расположения).scale, expansion, и/или со свойством velocityExpansion. Это будет тем поведением, которое вы хотите реализовать для элемента управления, который поддерживает оптическое или семантическое масштабирование.rotation и velocityAngular.Конечно, если вы хотите поддерживать несколько видов манипуляций, вы можете просто реализовать обработку всех этих свойств, передавая их, например в CSS-трансформацию. Подобное может быть применимо к приложениям, которые реализуют различные манипуляции для экранных объектов, и именно этому посвящен один из примеров Windows SDK, рассматривающий жесты.
В то время, как упражнение PointerEvents, включенное в эту лекцию, давало нам низкоуровневый вид событий указателей и жестов, для приложений по-настоящему важно то, как применять эти события для реальной манипуляции экранными объектами, что является реализацией частей языка касаний, такими, как жесты сжатия/растяжения и вращения. Для того, чтобы увидеть это, мы можем обратиться к примеру "Ввод: жесты, допускающие создание экземпляров" (http://code.msdn.microsoft.com/windowsapps/Input-Instantiable-deda69ca).
Этот пример, в основном, показывает, как использовать события жестов одновременно с несколькими элементами. В Сценарии 1 и 2, приложение реализует простой пример головоломки, как упоминалось выше. Каждым закрашенным прямоугольником можно управлять по отдельности, используя перетаскивание для перемещения (с инерцией и без), жест сжатия-растяжения для изменения масштаба, и жест вращения для вращения, как показано на рис. 4.1.
(рис 4.1) Пример "Ввод: жесты, допускающие создание экземпляров" после того, как с ним немного поработали. Словосочетание "допускающие создание экземпляров" (instantiable) использовано потому, что нам нужно создать экземпляр (instance) объекта MSGesture для приема событий жестов
В Сценарии 1 (js/instantiableGesture.js) объект MSGesture создается для кажого экранного элемента вместе с еще одним для черного фона, "стола" при инициализации (в функции initialize). Мы уже видели подобное. Похожим бразом, обработчик MSPointerDown (onPointerDown) добавляет указатели к объектам жестов для каждого элемента, уделяя немного больше внимания обработке их положения по оси Z. Это предовращает одновременные касания, указатели мыши и пера работают с тем же самым элементом (что было бы странно!):
function onPointerDown(e) {
if (e.target.gesture.pointerType === null) {
// Первый контакт e.target.gesture.addPointer(e.pointerId);
// Присоединение указателя к элементу
e.target.gesture.pointerType = e.pointerType;
}
else if (e.target.gesture.pointerType === e.pointerType)
{ // Контакты сходного типа e.target.gesture.addPointer(e.pointerId);
// Присоединение указателя к элементу
}
// ZIndex изменяется при опускании указателя.
// Элемент, на который опустился указатель, становится самым верхним
var zOrderCurr = e.target.style.zIndex;
var elts = document.getElementsByClassName("GestureElement");
for (var i = 0; i < elts.length; i++) {
if (elts[i].style.zIndex === 3) {
elts[i].style.zIndex = zOrderCurr;
}
e.target.style.zIndex = 3;
}
}
Обработчик MSGestureChange выполняется для каждого отдельного фрагмента (onGestureChange), затем собирает все данные по переносу, вращению, масштабированию в объекте eventArgs и применяет их с помощью CSS. Это показывает, как удобно то, что все эти свойства уже описаны в нужной нам системе координат:
function onGestureChange(e) {
var elt = e.target;
var m = new MSCSSMatrix(elt.style.msTransform);
elt.style.msTransform = m.
translate(e.offsetX, e.offsetY).
translate(e.translationX, e.translationY).
rotate(e.rotation * 180 / Math.PI).
scale(e.scale).
translate(-e.offsetX, -e.offsetY);
}
В коде примера выполняется немного больше всего, но то, что мы показали здесь – это самые важные части. Очевидно, если вам не нужна поддержка манипуляций определенного вида, вы можете просто игнорировать определенные свойства в объекте eventArgs.
Сценарий 2 этого примера формирует такое же изображение, но он реализован несколько иначе. Как вы можете видеть, в его функции initialize (js/gesture.js), изначально регистрируется лишь одно событие, применяющееся к "столу", который имеет черный фон и окружающую его границу.
Объекты жестов для отдельных фрагментов головоломки создаются и прикрепляются к указателям в событии MSPointerDown (onTableTopPointerDown). Этот подход более эффективен и масштабируем для приложения-головоломки, которое может иметь сотни или даже тысячи фрагментов, так как объекты жестов хранятся лишь до тех пор, пока осуществляется манипуляция конкретным фрагментом. Эти манипуляции так же похожи на те, что были в Сценарии 1, где свойства MSGestureChange применялись посредством CSS-трансформаций. Для того чтобы узнать подробности, обратитесь к комментариям к коду в js/gesture.js, так как они весьма обширны.
Сценарий 3 этого примера предоставляет еще одну демонстрацию выполнения жестов переноса, сжатия-расширения и вращения с использованием колеса мыши. Как показано в упражнении PointerEvents, единственное, что нужно для того, чтобы сделать это – это обработать событие wheel, установить eventArgs.pointerId в 1 и передать это в обработчик MSPointerDown, который затем добавит указатель к объекту жеста:
function onMouseWheel(e) {
e.pointerId = 1; // Фиксированный pointerId для MouseWheel
onPointerDown(e);
}
Опять же, это все, что нужно. (Мне нравится, когда все так просто!). В качестве упражнения, вы можете попытаться добавить этот код в Сценарии 1 и 2.
В случае с инерционными жестами, которые продолжают посылать некоторое количество событий MSGestureChange, когда указатель освобожден, вы можете задаться вопросами: "А что, на самом деле, управляет этими событиями? Очевидно, существует особая модель уменьшения скорости, встроенная в эти события, а именно та, на основе которой построены внешний вид и впечатления от взаимодействия с Windows. Но что, если мне нужно другое поведение? И что, если я хочу совсем иначе интерпретировать события указателя?"
Вспомогательный механизм, который интерпретирует события указателя, превращая их в жесты, называется средством распознавания жестов (gesture recognizer). Его вы можете получить напрямую посредством объекта ). После создания экземпляра этого объекта с помощью команды ) для тех видов манипуляций и жестов, которые вам интересны. Документация по Windows.UI.Input.GestureSettings предоставляет полный набор возможностей, в том числе, такие жесты, как ) (ballineight.js) мы можем видеть, как настраивают средство распознавания жестов для жестов касания (tap), вращения (rotate), переноса (translate) и масштабирования (scale) (с инерцией):
gr = new Windows.UI.Input.GestureRecognizer(); // Настройка GestureRecognizer для определения манипуляций вращения, переноса, масштабирования, // с инерцией для данных трех компонентов манипуляции и обработки касания gr.gestureSettings = Windows.UI.Input.GestureSettings.manipulationRotate | Windows.UI.Input.GestureSettings.manipulationTranslateX | Windows.UI.Input.GestureSettings.manipulationTranslateY | Windows.UI.Input.GestureSettings.manipulationScale | Windows.UI.Input.GestureSettings.manipulationRotateInertia | Windows.UI.Input.GestureSettings.manipulationScaleInertia | Windows.UI.Input.GestureSettings.manipulationTranslateInertia | Windows.UI.Input.GestureSettings.tap; // Выключение обратной связи пользовательского интерфейса для жестов // (обратная связь все еще используется для PointerPoints) gr.showGestureFeedback = false;
Объект GestureRecognizer так же имеет множество свойств для настройки его специфических событий. Например, для жестов скольжения по диагонали (cross-slides) вы можете задать свойства crossSlideThresholds, crossSlideExact, и crossSlideHorizontally. Вы можете задать быстроту снижения скорости (в пикселях/мс2) посредством свойств inertiaExpansionDeceleration, inertiaRotationDeceleration, и inertiaTranslationDeceleration.
После настройки, вы начинаете передавать событий MSPointer* к объекту средсва распознавания жестов, в частности, его методам, которые называются processDownEvent, processMoveEvents, и processUpEvent (так же, если нужно, processMouseWheelEvent, и processInertia). В ответ, в зависимости от конфигурации, средство распознавания жестов запустит некоторое количество собственных событий. Во-первых, это дискретные события, наподобие crossSliding, dragging, holding, rightTapped, и tapped. Для остальных он запустит серию событий manipuationStarted, manipulationUpdated, manipulationInertiaStarting, и manipulationCompleted. Обратите внимание на то, что все эти события исходят из WinRT, поэтому убедитесь, что вызвали, когда нужно, removeEventListener.
Когда вы используете средство распознавания жестов напрямую, другими совами, прослушиваете события MSPointer*, передаете их средству распознавания жестов и затем прослушиваете его специфические события и действуете, опираясь на них, как в вышеприведенном примере, вместо работы с событиями MSGesture*, которые исходят от средства распознавания жестов по умолчанию, сконфигурированного с помощью объекта MSGesture.
Обратитесь к документации ) для того, чтобы узнать подробности и к примерам, чтобы посмотреть на образцы кода. В качестве дополнительного примера, вот фрагмент кода, который перехватывает небольшие горизонтальные перемещения с использованием установки manipulationTranslateX:
var recognizer = new Windows.UI.Input.GestureRecognizer();
recognizer.gestureSettings = Windows.UI.Input.GestureSettings.manipulationTranslateX;
var DELTA = 10;
myElement.addEventListener('MSPointerDown', function (e) {
recognizer.processDownEvent(e.getCurrentPoint(e.pointerId));
});
myElement.addEventListener('MSPointerUp', function (e) {
recognizer.processUpEvent(e.getCurrentPoint(e.pointerId));
});
myElement.addEventListener('MSPointerMove', function (e) {
recognizer.processMoveEvents(e.getIntermediatePoints(e.pointerId));
});
// Помните, что при работе с этими событиями нужен removeEventListener
recognizer.addEventListener('manipulationcompleted', function (args) {
var pt = args.cumulative.translation;
if (pt.x < -DELTA) {
// переместить вправо
}
else if (pt.x > DELTA) {
// переместить влево
}
});
Помимо использования средства распознавания жестов, помните, что вы всегда можете перейти на более низкий уровень и выполнить собственную обработку событий MSPointer* , если вам это нужно, и, таким образом, полностью обойти средство распознавания жестов. Это может быть нужно, если конфигурации, допустимые в средстве распознавания жестов, не соответствуют вашим особенным нуждам. В то же время, сейчас неплохая возможность для того, чтобы перечитать врезку "Создание совершенно новых жестов?" в конце раздела о языке касаний. Она посвящена некоторым вопросам о том, когда действительно нужны пользовательские жесты.
После всего, что мы узнали о сенсорном вводе данных и о других способах ввода, может показаться, что рассмотрение скромной клавиатуры нас разочарует. Но, все же, клавиатура остается крайне важной для ввода текста, будь это физическая клавиатура или экранная "программная" клавиатура. Она особенно важна для обеспечения специальных возможностей, так как некоторые пользователи физически не могут использовать мышь или другие устройства. На самом деле, раздел 3.5. "Сертификационных требований к приложениям для Windows 8" (http://msdn.microsoft.com/library/windows/apps/hh694083.aspx) говорит об обязательной поддержке приложениями ввода с клавиатуры.
К счастью, в приложениях для Магазина Windows нет ничего особенного в обработке ввода с клавиатуры, но длинный путь можно пройти короткими шагами. Вот общие положения, взятые из материала "Реализация специальных возможностей клавиатуры" (http://msdn.microsoft.com/library/windows/apps/hh700327.aspx):
keydown, keyup и keypress так, как вы уже знаете, особенно для реализации специальных сочетаний клавиш. Смотрите раздел "Стандартные сочетания клавиш" далее в этом разделе для того, чтобы взглянуть на типичные сопоставления команд.tabindex интерактивным элементам, которые должны поддерживать возможность переключения на них с помощью клавиши табуляции. Старайтесь не добавлять tabindex к неинтерактивным элементам, так как это может помешать программам чтения с экрана.accesskey для тех элементов, которым следует иметь клавиатурные сокращения.В приложении "Here My Am!", над которым мы работаем в этом курсе, обновленный вариант которого содержится в дополнительных материалах к этой лекции, добавлена полная поддержка клавиатуры. Это, в основном, коснулось добавления ) для сравнения кодов клавиш:
var Key = WinJS.Utilities.Key;
var image = document.getElementById("photo");
image.addEventListener("keydown", function (e) {
if (e.keyCode == Key.enter || e.keyCode==Key.space) {
image.click();
}
});
Все это работает и для физической клавиатуры, и для программной. Дело закрыто? Не вполне. Есть две особенности, касающиеся экранной клавиатуры: первая – это как показать ее, вторая – как ее появление подействует на макет приложения. В конце этого раздела мы так же взглянем на стандартные клавиатурные сокращения для команд приложения.
Показ программной клавиатуры происходит по одной и только одной причине: пользователь касается элемента управления для ввода текста с атрибутом contenteditable="true" (такого, как div или canvas). Нет API, которое отображает клавиатуру, она не появляется и тогда, когда вы щелкаете по подобному элементу управления мышью или пером, или перемещаетесь к нему, нажимая клавишу табуляции на физической клавиатуры.
Конфигурация клавиатуры зависит от типа элемента управления для ввода текста. Мы можем увидеть это в Сценарии 2 примера "Ввод: сенсорная клавиатура для ввода текста" (http://code.msdn.microsoft.com/windowsapps/Input-Touch-keyboard-text-f86e9bd9), где в файле html/ScopedView.html содержится несколько элементов управления input (окружающая разметка опущена), которые выглядят так, как показано на рис. 4.2.
<input type="url" name="url" id="url" size="50" /> <input type="email" name="email" id="email" size="50" /> <input type="password" name="password" id="password" size="50" /> <input type="text" name="text" id="text" size="50" /> <input type="number" name="number" id="number" /> <input type="search" name="search" id="search" size="50" /> <input type="tel" name="tel" id="tel" size="50" />
(рис 4.2) Программная клавиатура появляется, когда вы касаетесь поля ввода, как показано в примере
То, что показано на рис. 4.2. – это клавиатура по умолчанию. Если вы прикоснетесь к полю Search (Поиск), вы получите практически то же самое, за исключением того, что клавиша Enter превратится в клавишу Search (Поиск). Для поля Email клавиатура тоже похожа на стандартную, за исключением того, что появятся кнопки @ и .com у клавиши пробела:
Клавиатура для ввода URL выглядит так же, но клавиши @ нет, а Enter превращается в Go (Перейти):
Для поля ввода пароля на клавиатуре появляется клавиша Hide keypresses (Скрыть нажатия клавиш), которая отключает видимую анимацию нажатий клавиш – очень полезная функция, если вы записываете видео!
И, наконец, поля Number (Число) и Telephone (Телефон) вызывают клавиатуру, ориентированную на ввод чисел:
Во всех этих случаях кнопка в нижнем правом углу (значок которой очень похож на клавиатуру) позволяет вам переключаться в другие режимы отображения клавиатуры:
Доступные возможности здесь – это нормальная (широкая) клавиатура, разделенная клавиатура, панель распознавания рукописного ввода, и кнопка для того, чтобы полностью скрыть клавиатуру. Вот как выглядят разделеная клавиатура и панель для ввода рукописного текста:
Панель для рукописного ввода, это просто еще один режим программной клавиатуры: вы можете переключаться между режимами, и ваш выбор сохраняется между вызовами. (По этой причине, Windows не активирует автоматически панель рукописного ввода для указателя ручки, так как пользователь может предпочитать использование программной клавиатуры даже с пером).
Так же клавиатура настроаивает свой внешний вид при работе с элементами управления для ввода текста для предоставления подсказок и выделения появляющейся кнопки Insert (Вставить). Это показано в Сценарии 1 примера:

Вторая задача, касающаяся программной клавиатуры (да, я не забыл!) заключается в поддержке макета, когда клавиатура может перекрыть поле для ввода, имеющее фокус.
Когда появляется программная клавиатура или панель для ввода рукописного текста, система пытается убедиться в том, что поле ввода видимо, прокручивая содержимое, если это возможно. Это значает просто установку отрицательного вертикального смещения для всей вашей страницы в соответствии с высотой программной клавиатуры. Например, если я добавлю (в качестве радикального решения!) кучу элементов </br> в верхнюю часть страницы html/ScopedView.html в примере, так, чтобы элементы управления для ввода текста оказались внизу страницы, и затем коснусь одного из них, вся страница сдвинется вверх, как показано на рис. 4.3.:
(рис 4.3) Когда появляется программная клавиатура, Windows автоматически смещает страницу приложения вверх для того, чтобы поле ввода не оказалось перекрытым
Хотя такой подход может быть самым простым решением этой задачи, он не всегда идеален. К счастью, вы можете сделать кое-что более интеллектуальное, если хотите, прослушивая события ) и непосредственно подстраивая макет. Код для того, чтобы это сделать, можно найти (вы готовы к этому?) в примере "Реагирование на отображение экранной клавиатуры" (http://code.msdn.microsoft.com/windowsapps/Keyboard-Events-Sample-866ba41c)
var inputPane = Windows.UI.ViewManagement.InputPane.getForCurrentView();
inputPane.addEventListener("showing", showingHandler, false);
inputPane.addEventListener("hiding", hidingHandler, false);
В обработчике события showing объект eventArgs.occludedRect (имеющий тип Windows.Foundation.Rect) дает вам координаты и размеры обрасти, которую перекроет экранная клавиатура. В ответ на это мы можете настроить любые необходимые параметры макета и установить свойство eventArgs.ensuredFocusedElementInView в true. Это сообщит Windows, что автоматический сдвиг выполнять не нужно:
function showingHandler(e) {
if (document.activeElement.id === "customHandling") {
keyboardShowing(e.occludedRect);
// Будьте осторожны с этим свойством. Как только оно установлено, система
// не будет делать ничего для того, чтобы элемент, имеющий фокус, был видимым.
e.ensuredFocusedElementInView = true;
}
}
В примере показаны оба случая. Если вы коснетесь расположенного в нижней левой части приложения элемента defaultHandling, который имеет цвет морской волны, как показано на рис. 4.4, его showingHandler ничего не будет делать, и мы увидим поведение по умолчанию
(рис 4.4) Прикосновение к левому элементу defaultHandling в нижней части приведет к стандартному поведению при появлении клавиатуры, при котором содержимое страницы сдвигается вертикально
Если вы коснетесь элемента customHandling (справа), он вызовет функцию keyboardShowing для настройки макета:
function keyboardShowing(keyboardRect) {
// Часть кода опущена...
var elementToAnimate = document.getElementById("middleContainer");
var elementToResize = document.getElementById("appView");
var elementToScroll = document.getElementById("middleList");
// Записываем расстояние перемещения. Это упрощает расчеты
displacement = keyboardRect.height;
var displacementString = -displacement + "px";
// Находим последний видимый элемент в списке
var bottomOfList = elementToScroll.scrollTop + elementToScroll.clientHeight;
// Анимируем
showingAnimation = KeyboardEventsSample.Animations.inputPaneShowing(elementToAnimate,
{ top: displacementString, left: "0px" }).then(function () {
// После анимации макет занимает меньшее окно просмотра над клавиатурой
elementToResize.style.height = keyboardRect.y + "px";
// Прокручиваем список в нужное место, так чтобы прокрутка не была заметна
elementToScroll.scrollTop = bottomOfList - elementToScroll.clientHeight;
showingAnimation = null;
});
}
Этот код немного сложный, так как он анимирует перемещение различных элементов страницы. В результате макет задействованных элементов, а именно, того, к которому вы прикоснулись – подстраивается для того, чтобы дать место для клавиатуры. Другие элементы на странице, в противном случае, не затрагиваются. Результат показан на рис. 4.5.
(рис 4.5) Прикосновение к серому элементу customHandling справа демонстрирует пользовательскую обработку отображения клавиатуры
Последнее, что мне хотелось бы включить в раздел, посвященный клавиатуре – это таблица командных сочетаний клавиш, которые вы можете поддерживать в своем приложении. Это дополняет набор эквивалентов языка касаний, и вы, возможно уже привыкли пользоваться многими из них. Это полезно рассмотреть, так как приложения должны быть доступны для использования исключительно с помощью клавиатуры, и реализация сочетаний клавиш, наподобие этих – это один из шагов к удовлетворении данного требования и позволяет пользователям клавиатуры более эффективно использовать ваше приложение.
| Действие или команда | Сочетание клавиш |
|---|---|
| Перемещение фокуса | Tab |
| Назад (Навигация) | Кнопка "Назад" на специальных клавиатурах; клавиша backspace , везде, кроме текстовых полей; Alt+стрелка влево |
| Вперед (Навигация) | Alt+стрелка вправо |
| Вверх | Alt+стрелка вверх |
| Отмена/Выход из режима | ESC |
| Перемещение по элементам в списке | Клавиши-стрелки (и Tab) |
| Быстрое по элементам списка к соедующей группе, если выделение не следует автоматически за фокусом | Ctrl+клавиши-стрелки |
| Масштабирование (семантическое и оптическое) | Ctrl+"+" b Ctrl+ "-" |
| Быстрое перемещение к именованному элементу коллекции | Начало ввода текста |
| Перемещение на большое расстояние | Page up/down ( должно работать в пользовательском интерфейсе, поддерживающем сдвиг в горизонтальном и вертикальном направлениях) |
| Следующая позиция табуляции или группа | Ctrl+Tab |
| Предыдущая позиция табуляции или группа | Ctrl+Shift+Tab |
| N-ная позиция табуляции или группа | Ctrl+N (1-9) |
| Открыть панель приложения (Windows обрабатывает это автоматически) | Win+Z |
| Контектстное меню | Клавиша контекстного меню |
| Открыть дополнительную панель/выбрать элемент меню | Enter |
| Перейти куда-либо/активировать | Enter (в выделении) |
| Выбрать | Пробел |
| Выбрать смежные элементы | Shift+клавиши-стрелки |
| Закрепить элемент | Ctrl+Shift+! |
| Сохранить | Ctrl+S |
| Поиск | Ctrl+F |
| Печать | Ctrl+P (вызывает Windows.Graphics.Printing.PrintManager.showPrintUIAsync) |
| Копировать | Ctrl+C |
| Вырезать | Ctrl+X |
| Вставить | Ctrl+V |
| Новый элемент | Ctrl+N |
| Открыть адрес | Ctrl+L или Alt+D |
| Вращение | Ctrl+"," и Ctrl+"." |
| Проигрывание/Пауза | Ctrl+P (только медиа-приложения) |
| Следующий элемент | Ctrl+F (конфликтует с Поиском) |
| Предыдущий элемент | Ctrl+B |
| Перемотка назад | Ctrl+Shift+B |
| Перемотка вперед | Ctrl+Shift+F |
Помимо встроенной программной клавиатуры и панели для рукописного ввода, приложениям может понадобиться предоставить пользователю поверхность, которая может непосредственно принимать данные указателя в виде рукописного ввода (ink). Под этим я подразумеваю больше, чем использование элемента canvas и обработку событий MSPointer для рисования или создания растровых изображений. Рукописные штрихи – это структура данных, которая поддерживает реально вводимую информацию (в том числе – сведения о нажиме, угле наклона сенсора, скорости, если аппаратное обеспечение это поддерживает), что позволяет выполнять распознавание рукописного ввода и другие виды высокоуровневой обработки информации, которые невозможны при работе с растровыми данными. Работа с рукописным вводом, другими словами, напоминае работу с процессом рисования изображения, а не работу с готовым изображением, и это возможно для всех типов указателей ввода.
Поддержка рукописного ввода в WinRT располагается в пространстве имен ). Это API не зависит от конкретного презентационного фреймворка, не предоставляет она и средств визуализации: она имеет дело лишь с управлением структурами данных, которые приложение может вывести самостятельно, на поверхности для рисования наподобие canvas. Вот базовые функции этого API:
new Windows.UI.Input.Inking.InkManager().Windows.UI.Input.Inking.InkDrawingAttributes и установки атрибутов штриха, таких, как color, fitToCurve (в противоположность прямым линиям, проводимым по умолчанию), ignorePressure, penTip (Windows.UI.Input.Inking.PenTipShape.circle или rectangle), и size (объект Windows.Foundation.Size со свойствами height и width).MSPointerDown, MSPointerMove, и MSPointerUp, которые обычно нужны для целей визуализации. eventArgs.currentPoint это объект Windows.UI.Input.PointerPoint, который содержит идентификатор указателя, координаты точки и свойства наподобие pressure, tilt, и twist.PointerPoint методам управляющего объекта processPointerDown, processPointerUpdate, и processPointerUp.processPointerUp, управляющий объект рукописного ввода создает объект Windows.UI.Input.Inking.InkStroke для проведенного штриха. Штрихи можно получить посредством метода getStrokes управляющего объекта и вывести так, как нужно.InkStroke и переданы управляющему объекту посредством метода addStroke. Объекты штрихов можно удалять с использованием метода deleteStroke.Управляющий объект, кроме того, предоставляет методы для распознавания рукописного текста на основе содержащихся в нем штрихов, сохранения и загрузки данных и обработки различных режимов, таких, как рисование и стирание штрихов. Полную демонстрацию этого вы можете найти в примере "Ввод: рукописный ввод" (http://code.msdn.microsoft.com/windowsapps/Ink-App-sample-61abaec3), который показан на рис. 4.6. Этот пример позволит вам увидеть полный набор возможностей платформы рукописного ввода, в том числе – распознавание рукописного текста.
(рис 4.6) Пример работы с платформой рукописного ввода содержит множество команд на панели приложения. Зеленый текст "Hello" в верхнем левом углу был создан после выделения рукописного "Hello" и использования команды Recognition (Распознавание)
SDK так же включает в себя пример "Ввод: упрощенный рукописный ввод" (http://code.msdn.microsoft.com/windowsapps/Input-simplified-ink-sample-11614bbf) для демонстрации сценария, ориентированного на распознавание рукописного текста, как показано на рис. 4.7. Вам следует знать, что это пример, который вовсе не поддерживает касания – он ограничен мышью и пером и использует для активации команд клавиатурные сокращения вместо панели приложения. Взгляните на функцию keydown в файле simpleink.js для того, чтобы увидеть список команд вида "Ctrl + другая клавиша". Клавиша пробел активирует распознавание введенного текста, клавиша backspace очищает рабочую область. Как вы можете видеть на рисунке, я могу думать, что распознавание рукописного текста работает очень хорошо! (Это говорит мне о том, что образцы рукописного текста, которые я передал команде инженеров Microsoft где-то в середине 1990-х, стали значительным вкладом в их дело).
(рис 4.7) Пример отлично распознает мой небрежный текст, написанный мышью
Прежде чем мы рассмотрим датчики в целом, я хочу отдельно рассмотреть возможности определения географического расположения в приложениях для Магазина Windows, так как это API устроено не так, как у других датчиков. Мы уже использовали эту возможность, начиная с Главы 2 курса "Введение в разработку приложений для Windows 8 с использованием HTML, CSS и JavaScript", в приложении "Here My Am!", но нам нужен более полной обзор этой крайне полезной возможности.
В отличие от других датчиков, только определение местоположения имеет выделенную возможность, которую вы должны объявить в манифесте. То, где вы находитесь на Земле, это абсолютные данные, которые являются частью личной информации. Таким образом, пользователи должны дать согласие, прежде чем приложение сможет получить эти сведения, и ваше приложение так же должно предоставлять "Соглашение о конфиденциальности" в Магазине Windows. Другие датчики, в отличие от этого, предоставляют относительную информацию – вы не можете, например, узнать что-либо о человеке из данных о наклоне устройства, о его перемещении или из сведений о том, насколько яркий свет его освещает. Соответственно, вы можете использовать другие датчики без объявления каких-либо особых возможностей.
Как вы можете знать, данные о географическом местоположении могут быть получены двумя различными путями. Первый, и наиболее точный, конечно, это получение данных с GPS-приемника, который получает информацию от геостационарных спутников, расположенных на орбитах в несколько сотен миль. Другим, достаточно полезным, хотя и не всегда точным средством является попытка узнать местоположение на основе IP-адреса проводного сетевого соединения или триангуляция на основе расположения доступных точек доступа WiFi. В любом случае, WinRT делает все возможное для того, чтобы дать вам достойные показатели.
Для доступа к данным о географическом местоположении, вы должны сначала создать экземпляр средства определения местоположения WinRT, ). Когда он у вас есть, вы затем можете вызвать его метод getGeopositionAsync, результаты работы которого (помещенные в обработчик завершения), представляют собой объект типа Geoposition (в том же пространстве имен Windows.Devices.Geolocation, если не указано иное). Вот код из "Here My Am!":
var gl = new Windows.Devices.Geolocation.Geolocator();
gl.getGeopositionAsync().done(function (position) {
//Сохранить для целей общего доступа
lastPosition = { latitude: position.coordinate.latitude,
longitude: position.coordinate.longitude };
}
У метода ), применяя который вы можете указать два параметра: максимальную давность для кэшированных данных (то есть то, насколько свежими должны быть данные) и значение тайм-аута, в течение которого вы хотите ждать ответ. И то и другое значение измеряются в миллисекундах.
Объект ) содержит два свойства. Первое, это свойство ), который предоставляет свойства ), который содержит свойство ), в частности – класс MapAddress (http://msdn.microsoft.com/library/jj663670.aspx) для того, чтобы узнать адрес по имеющимся координатам.
Вы можете задать желаемую точность измерений посредством свойства desiredAccuracy средства определения местоположения, которое может принимать значение PositionAccuracy.default или PositionAccuracy.high. Последнее, учтите, создаст большую нагрузку на аппаратное обеспечение. Это может привести к повышению стоимости использования лимитированного сетевого соединения и ускорить разряд батарей, поэтому устанавливайте этой свойство в значение high только тогда, когда это имеет ключевую важность для работы с приложением.
Средство определения местоположения так же предоставляет свойство ), содержащим такие члены, как ready, initializing, noData, disabled, notInitialized, и notAvailable. Должно быть очевидным, что вы не сможете получить данные от средства определения местоположения, которое находится в состоянии, отличном от ready (готово). Для того, чтобы отследить это, вы можете прослушивать событие средства определения местоположения statusChanged, свойство eventArgs.status которого в вашем обработчике будет содержать PositionStatus. Это полезно, когда вы обнаруживаете, что GPS-приемнику может понадобиться пара секунд на то, чтобы предоставить необходимые данные. Например, вот как используется данное событие в примере "Определение местопол
ожения" (http://code.msdn.microsoft.com/windowsapps/Geolocation-2483de66) из Windows SDK:
geolocator = new Windows.Devices.Geolocation.Geolocator();
geolocator.addEventListener("statuschanged", onStatusChanged); //Позже не забудьте удалить
function onStatusChanged(e) {
switch (e.status) {
// ...
}
}
Обратите внимание на то, что PositionStatus и statusChanged отражают чтение данных с GPS-устройства, и на чтение не влияет разрешение на получение данных о местоположении в панели параметров. Как показано в "Here My Am!", приложению не нужно проверять разрешение, пытаясь получить значения параметров, что является особенностью получения данных от устройства.
Два других интересных свойства средства определения местоположения – это movementThreshold, расстояние в метрах, на которое может переместиться устройство, прежде чем будет вызвана следующая операция чтения данных (это может быть использовано в сценариях с установкой географических границ), и reportInterval, который задается в миллисекундах между попытками чтения данных. Используйте последний параметр умеренно, устанавливайте его в то значение, которое вам действительно нужно, так как следует стремиться к минимизации сетевой активности и активности GPS-датчика. В любом случае, когда средство определения местоположения получает новые данные и обнаруживает, что устройство переместилось за пределы movementThreshold, оно вызывает событие positionChanged, свойство которого eventArgs.position является новым объектом Geoposition. Это так же показано в Сценарии 1 примера об определении местоположения:
geolocator.addEventListener("positionchanged", onPositionChanged);
function onPositionChanged(e) {
var coord = e.position.coordinate;
document.getElementById("latitude").innerHTML = coord.latitude;
document.getElementById("longitude").innerHTML = coord.longitude;
document.getElementById("accuracy").innerHTML = coord.accuracy;
}
В случае с movementThreshold и reportInterval, рассматривайте их с позиций нужд приложения, основанных на точности и/или интервале обновления данных о местоположении, которые вы используете. Например, данные о погоде относятся к регионам и могут обновляться лишь раз в час. Поэтому movementThreshold может быть установлено в милях или километрах, reportInterval – на 15, 30, 60 минут или на более длительное время. Приложения работы с картами или предназначенные для автомобилистов, с другой стороны, работают с данными, весьма чувствительными к местоположению и, таким образом, будут иметь гораздо меньшие пороговые значения и более короткие интервалы обновления.
Когда имеет значение время жизни устройства от батарей, лучше всего просто получать новые данные, когда они нужны пользователю, вместо того, чтобы выполнять это через некоторые регулярные интервалы. Но это, опять же, зависит от сценария приложения, и вы можете так же предоставить параметр, который позволяет пользователю управлять активностью подсистемы определения местоположения.
Так же очень важно отметить, что приложения не получают события positionChanged или statusChanged, когда они приостановлены, если только вы не зарегистрировали для этой цели фоновую задачу, запускаемую по таймеру и пользователь не добавил приложение на экран блокировки. Мы поговорим об этом подробнее в лекции 2 курса "Программная логика приложений для Windows 8, созданных с использованием HTML, CSS и JavaScript и их взаимодействие с системой", и вы так же сможете увидеть, как это работает в Сценарии 3 примера об определении местоположения. Если, однако, вы не используете фоновые задачи или пользователь не поместил приложение на экран блокировки, и вам все еще нужно отслеживать местоположение пользователя, обрабатывайте соответствующим образом событие resuming и обновляйте там эти данные.
С другой стороны, некоторые сценарии, исопользующие определение местоположения, такие, как навигация, нуждаются в том, чтобы экран устройства был активен (предотвращая автоматическое выключение экрана), даже если пользователь ничего не делает. Для этой цели вы можете использовать класс в частности, его методы requestActive и requestRelease, которые вы можете вызывать в начале и в конце сеанса навигации. Конечно, поддержание экрана во включенном состоянии приводит к потреблению большей энергии батарей, используйте эту возможность только когда это необходимо – когда приложение используется для целей навигации – и не прибегайте к выполнению подобного запроса при запуске приложения. В противном случае ваше приложение, возможно, прослывет в Магазине Windows как неумеренный потребитель энергии!
Опытные разработчики HTML/JavaScript могут удивиться, почему WinRT предоставляет API для определения местоположения в то время, как в HTML5 оно уже есть: window.navigator.geolocation и его метод getCurrentPosition , который возвращает объект с координатами. Причина подобного заключается в том, что другие языки, такие, как C#, VisualBasic и C++ не имеют других API для получения подобных данных, что предоставляет выбор разработчикам на HTML/JavaScript. Если посмотреть глубже, то API HTML5 пользуется теми же данными, что и API WinRT, требует объявления той же возможности Расположение (Location) в манифесте, и нуждается в таком же разрешении пользователя, то есть, в основном, эти два API идентичны. Я дал бы API WinRT небольшое преимущество за счет опции movementThreshold , которая помогает приложению экономить электроэнергию и делает возможной простую постановку географических границ. Выполнение тех же действий в HTML5 потребует более частых запросов и приведет к большему потреблению энергии. Во многих сценариях, однако, вы можете использовать либо то, либо другое, с одинаковыми результатами. Однако, как и другие API WinRT, Windows.Devices.Geolocation доступно лишь страницам локального контекста приложений для Магазина Windows. На страницах веб-контекста вы можете пользоваться API HTML5.
Как я говорил во введении к лекции, мне нравится думать о датчиках как об одной из форм ввода данных. Это имеет смысл, так как каждое устройство, которое интегрированно в наши компьютеры, что мы принимаем как должное, является одним из видов интерфейса для взаимодействия пользователя и машины. Со временем, я полагаю, многие датчики, которые сейчас кажутся нам новыми, станут стандартным оборудование почти везде.
Датчики, опять же, это способ понять взаимоотношения устройства и физического мира, окружающего его, и их использование подразумевает ввод данных, так как вы, будучи человеком, можете влиять на эти взаимоотношения, в основном, перемещая устройство в пространстве или меняя их окружение. Датчики так же могут быть использованы как средства прямого ввода информации, вызывая перемещение объектов на экране, вместо использования какой-то формы абстрактного ввода данных наподобие мыши или клавиатуры. Например, вместо того, чтобы использовать абстрактное сочетание клавиш для того, чтобы наклонить игровое поле, вы можете, используя датчики, просто наклонить устройство. Встряхивание устройства, на самом деле, является хорошо известным физическим жестом, который может быть связан с командой вроде "Повтори, будь ты проклят! Почему ты не делаешь то, что я хочу?". Разве мы долгие годы не трясли наши компьютеры или не стучали по ним, когда они не делали то, что нам нужно? А компьютер с датчиками вполне может отреагировать на такое воздействие!
Вот, какую информацию могут нам сообщить различные датчики:
Эти датчики предоставляет API WinRT
Так как работа всех этих датчиков очень похожа друг на друга (это сделано специально, за исключением простого датчика ориентации, который не похож на другие), я хочу показать общий шаблон работы с API датчиков вместо детальных примеров для каждого из них. Подобные примеры можно найти среди SDK-примеров "Акселерометр" (http://code.msdn.microsoft.com/windowsapps/Accelerometer-Sensor-Sample-22982671), "Компас" (http://code.msdn.microsoft.com/windowsapps/Compass-Sensor-Sample-0ed09c55), "Гирометр" (http://code.msdn.microsoft.com/windowsapps/Gyrometer-Sensor-Sample-4fe891d9), "Инклинометр" (http://code.msdn.microsoft.com/windowsapps/Inclinometer-Sensor-Sample-0cd0bf84), "Датчик освещенности" (http://code.msdn.microsoft.com/windowsapps/LightSensor-Sample-4477824c) и "Датчик ориентации" (http://code.msdn.microsoft.com/windowsapps/OrientationSensor-sample-0b1732be).
Шаблон их использования выглядит следующим образом, некоторые особенности приведены в таблице ниже:
Получение объекта датчика посредством Windows.Devices.Sensors.<sensor>.getDefault().
Вызов метода getCurrentReading этого объекта для получения данных.
Для последующего получения данных, настроить свойства объекта minimumReportInterval и reportInterval (и то и другое – в миллисекундах) и прослушивать событие объекта readingchanged. В ответ обработчик события получит объект подходящего типа с прочитанными данными. В случае с определением местоположения, разумно подойтите к установке этих параметров, что поможет увеличить время работы устройства от батарей, предотвращая излишний расход электроэнергии датчиками.
| Название датчика (Windows.Devices.Sensors.) | Дополнительные члены | Тип возвращаемых данных (Windows.Devices.Sensors) | Свойства возвращаемых данных (timestamp Имеет тип Date; остальные являются числами, если не указано иное) |
|---|---|---|---|
| Акселерометр (http://msdn.microsoft.com/library/windows/apps/windows.devices.sensors.accelerometer.aspx) | Событие: shaken (аррументы события содержат лишь свойство timestamp) |
) | accelerationX (в G), accelerationY,
accelerationZ, timestamp
|
| Компас (http://msdn.microsoft.com/library/windows/apps/windows.devices.sensors.compass.aspx) | n/a | ) | headingMagneticNorth (градусы),
headingTrueNorth, timestamp
|
| Гирометр (http://msdn.microsoft.com/library/windows/apps/windows.devices.sensors.gyrometer.aspx) | n/a | ) | angularVelocityX (градусы в секунду), angularVelocityY, angularVelocityZ, timestamp |
| Инклинометр (http://msdn.microsoft.com/library/windows/apps/windows.devices.sensors.inclinometer.aspx) | n/a | ) | pitchDegrees (градусы), rollDegrees (градусы), yawDegrees (градусы), timestamp |
| Датчик освещенности (http://msdn.microsoft.com/library/windows/apps/windows.devices.sensors.lightsensor.aspx) | n/a | ) | illuminenceInLux (люксы), timestamp |
| Датчик ориентации (http://msdn.microsoft.com/library/windows/apps/windows.devices.sensors.orientationsensor.aspx) | n/a | ) | quaternion, (SensorQuaternion содержит свойства w, x, y, и z) rotationMatrix
(Sensor-RotationMatrix содержит свойства
m11, m12, m13, m21, m22, m23, m31, m32, m33), timestamp
|
Вот образец кода из примера "Гирометр" (js/scenario1.js):
gyrometer = Windows.Devices.Sensors.Gyrometer.getDefault();
var minimumReportInterval = gyrometer.minimumReportInterval;
var reportInterval = minimumReportInterval > 16 ? minimumReportInterval : 16;
gyrometer.reportInterval = reportInterval;
gyrometer.addEventListener("readingchanged", onDataChanged);
// Не забудьте удалить при необходимости
function onDataChanged(e) {
var reading = e.reading;
document.getElementById("eventOutputX").innerHTML = reading.angularVelocityX.toFixed(2);
document.getElementById("eventOutputY").innerHTML = reading.angularVelocityY.toFixed(2);
document.getElementById("eventOutputZ").innerHTML = reading.angularVelocityZ.toFixed(2);
}
В случае с датчиком ориентации, понятие кватернион (quaternion) легче всего понять как вращение точки [x,y,z] вокруг одной произвольной оси. В этом – отличие от матрицы вращения, которая представляет вращение по всем трем осям. Математическая основа кватернионов довольно экзотична, она включает в себя геометрические понятия комплексных чисел, математические понятия мнимых чисел, но работать с ними просто и фреймворки наподобие DirectX поддерживают их. Посмотрите пример "Сенсор ориентации" для того, чтобы больше узнать об этом.
Говоря об ориентации, я упоминал, что ) работает немного не так, как остальные. Его цель – в предоставлении информации о квадранте (quadrant) ориентации, а не в предоставлении данных о точной ориентации устройства, и, возможно, это все, что вам нужно. Например, приложению для просмотра звездной карты нужно знать, расположено ли устройство вниз экраном, таким образом оно сможет настроить вывод данных (используя и показания компаса) для того, чтобы то, что оно отображает на экране, соответствовало бы виду неба.
В итоге, использование этого датчика выглядит так:
Windows.Devices.Sensors.SimpleOrientation.getDefault для того, чтобы получить объект.getCurrentOrientation для того, чтобы получить результаты чтения данных.orientationChanged используется для последующего получения данных, где eventArgs содержит свойства orientation (результат чтения данных) и timestamp.notRotated ("portrait up", книжная-сверху), rotated90DegreesCounterclockwise ("landscape left" альбомная-слева), rotated180DegreesCounterclockwise ("portrait down" книжная-снизу), rotated270DegreesCounterclockwise ("landscape right" альбомная-справа). Обратите внимание на то, что все это не имеет никакого отношения к состояниям просмотра наподобие полноэкранного альбомного или полноэкранного портретного режимов просмотра.faceup, facedown (лицевой сторонй вверх или вниз, только для планшентых устройств).Демонстрацию использования этого датчика вы можете найти в примере "Простой датчик ориентации" (http://code.msdn.microsoft.com/windowsapps/SimpleOrientationSensor-d948ac62).
click, в то время как другие требуют создания объекта MSGesture, связи этого объекта с указателем и обработку последовательностей событий MSGesture*, которая используется для реализации манипуляций и перемещения с инерцией.wheel в объект жеста). Поддержка клавиатуры должна быть реализована отдельно, она использует обычные события HTML/JavaScript.readingChanged. С ними легко работать, что оставляет большую часть сил на поиск креативных способов их применения! Когда объект MSGesture получает несколько указателей через свое событиет addPointer, он так же запускает события MSGestureStart, MSGestureChange, MSGestureEnd для жестов поворота и масштабирования, вместе с событием MSInertiaStart. В подобных случаях свойства scale, rotation, velocityAngular, expansion, и velocityExpansion в объекте eventArgs приобретают смысл.
Вы можете выборочно просматривать эти свойства для событий MSGestureChange посредством выпадающего списка, который расположен в правом верхнем углу, в упражнении PointerEvents. Вы можете заметить, что, когда вы выполняете многоточечные жесты в имитаторе Visual Studio, вы никогда не видите событие MSGestureTap для отдельных точек касания. Это происходит потому, что распознаватель жестов может видеть подобные множественные события MSPointerDown, которые происходят почти одновременно (здесь играет роль свойство hwTimestamp) и тут же объединяет их в одно событие MSGestureStart (например, начинает жест масштабирования или вращения).
Теперь я уверен, что вы готовы задать несколько важных вопросов. До сих пор я говорил о сенсорных жестах изменения масштаба и вращения, о жестах переноса, как о рахных вещах, но как, на самом деле, различать их, если все они поступют в приложение посредством одного и того же события MSGestureChange? Разве это не запутывает все? Какова стратегия для работы с жестами переноса, вращения и масштабирования?
Хорошо, ответ заключается в том, что вам не нужно их разделять! Что, если вы подумаете немного о том, как вы обрабатываете события MSGestureChange и данные, которые каждое из них содержит в зависимости от вида манипуляции, который вы поддерживаете в пользовательском интерфейсе:
scale и rotation и применяете только необходимые, такие, как translationX и translationY. Это было бы ожидаемым поведением при выборе элемента в элементе управления коллекцией, например (или в элементе управления, который позволяет манипулировать элементами по технологии "перетащить и опустить", меняя порядок их расположения).scale, expansion, и/или со свойством velocityExpansion. Это будет тем поведением, которое вы хотите реализовать для элемента управления, который поддерживает оптическое или семантическое масштабирование.rotation и velocityAngular.Конечно, если вы хотите поддерживать несколько видов манипуляций, вы можете просто реализовать обработку всех этих свойств, передавая их, например в CSS-трансформацию. Подобное может быть применимо к приложениям, которые реализуют различные манипуляции для экранных объектов, и именно этому посвящен один из примеров Windows SDK, рассматривающий жесты.
В то время, как упражнение PointerEvents, включенное в эту лекцию, давало нам низкоуровневый вид событий указателей и жестов, для приложений по-настоящему важно то, как применять эти события для реальной манипуляции экранными объектами, что является реализацией частей языка касаний, такими, как жесты сжатия/растяжения и вращения. Для того, чтобы увидеть это, мы можем обратиться к примеру "Ввод: жесты, допускающие создание экземпляров" (http://code.msdn.microsoft.com/windowsapps/Input-Instantiable-deda69ca).
Этот пример, в основном, показывает, как использовать события жестов одновременно с несколькими элементами. В Сценарии 1 и 2, приложение реализует простой пример головоломки, как упоминалось выше. Каждым закрашенным прямоугольником можно управлять по отдельности, используя перетаскивание для перемещения (с инерцией и без), жест сжатия-растяжения для изменения масштаба, и жест вращения для вращения, как показано на рис. 4.1.
(рис 4.1) Пример "Ввод: жесты, допускающие создание экземпляров" после того, как с ним немного поработали. Словосочетание "допускающие создание экземпляров" (instantiable) использовано потому, что нам нужно создать экземпляр (instance) объекта MSGesture для приема событий жестов
В Сценарии 1 (js/instantiableGesture.js) объект MSGesture создается для кажого экранного элемента вместе с еще одним для черного фона, "стола" при инициализации (в функции initialize). Мы уже видели подобное. Похожим бразом, обработчик MSPointerDown (onPointerDown) добавляет указатели к объектам жестов для каждого элемента, уделяя немного больше внимания обработке их положения по оси Z. Это предовращает одновременные касания, указатели мыши и пера работают с тем же самым элементом (что было бы странно!):
function onPointerDown(e) {
if (e.target.gesture.pointerType === null) {
// Первый контакт e.target.gesture.addPointer(e.pointerId);
// Присоединение указателя к элементу
e.target.gesture.pointerType = e.pointerType;
}
else if (e.target.gesture.pointerType === e.pointerType)
{ // Контакты сходного типа e.target.gesture.addPointer(e.pointerId);
// Присоединение указателя к элементу
}
// ZIndex изменяется при опускании указателя.
// Элемент, на который опустился указатель, становится самым верхним
var zOrderCurr = e.target.style.zIndex;
var elts = document.getElementsByClassName("GestureElement");
for (var i = 0; i < elts.length; i++) {
if (elts[i].style.zIndex === 3) {
elts[i].style.zIndex = zOrderCurr;
}
e.target.style.zIndex = 3;
}
}
Обработчик MSGestureChange выполняется для каждого отдельного фрагмента (onGestureChange), затем собирает все данные по переносу, вращению, масштабированию в объекте eventArgs и применяет их с помощью CSS. Это показывает, как удобно то, что все эти свойства уже описаны в нужной нам системе координат:
function onGestureChange(e) {
var elt = e.target;
var m = new MSCSSMatrix(elt.style.msTransform);
elt.style.msTransform = m.
translate(e.offsetX, e.offsetY).
translate(e.translationX, e.translationY).
rotate(e.rotation * 180 / Math.PI).
scale(e.scale).
translate(-e.offsetX, -e.offsetY);
}
В коде примера выполняется немного больше всего, но то, что мы показали здесь – это самые важные части. Очевидно, если вам не нужна поддержка манипуляций определенного вида, вы можете просто игнорировать определенные свойства в объекте eventArgs.
Сценарий 2 этого примера формирует такое же изображение, но он реализован несколько иначе. Как вы можете видеть, в его функции initialize (js/gesture.js), изначально регистрируется лишь одно событие, применяющееся к "столу", который имеет черный фон и окружающую его границу.
Объекты жестов для отдельных фрагментов головоломки создаются и прикрепляются к указателям в событии MSPointerDown (onTableTopPointerDown). Этот подход более эффективен и масштабируем для приложения-головоломки, которое может иметь сотни или даже тысячи фрагментов, так как объекты жестов хранятся лишь до тех пор, пока осуществляется манипуляция конкретным фрагментом. Эти манипуляции так же похожи на те, что были в Сценарии 1, где свойства MSGestureChange применялись посредством CSS-трансформаций. Для того чтобы узнать подробности, обратитесь к комментариям к коду в js/gesture.js, так как они весьма обширны.
Сценарий 3 этого примера предоставляет еще одну демонстрацию выполнения жестов переноса, сжатия-расширения и вращения с использованием колеса мыши. Как показано в упражнении PointerEvents, единственное, что нужно для того, чтобы сделать это – это обработать событие wheel, установить eventArgs.pointerId в 1 и передать это в обработчик MSPointerDown, который затем добавит указатель к объекту жеста:
function onMouseWheel(e) {
e.pointerId = 1; // Фиксированный pointerId для MouseWheel
onPointerDown(e);
}
Опять же, это все, что нужно. (Мне нравится, когда все так просто!). В качестве упражнения, вы можете попытаться добавить этот код в Сценарии 1 и 2.
В случае с инерционными жестами, которые продолжают посылать некоторое количество событий MSGestureChange, когда указатель освобожден, вы можете задаться вопросами: "А что, на самом деле, управляет этими событиями? Очевидно, существует особая модель уменьшения скорости, встроенная в эти события, а именно та, на основе которой построены внешний вид и впечатления от взаимодействия с Windows. Но что, если мне нужно другое поведение? И что, если я хочу совсем иначе интерпретировать события указателя?"
Вспомогательный механизм, который интерпретирует события указателя, превращая их в жесты, называется средством распознавания жестов (gesture recognizer). Его вы можете получить напрямую посредством объекта ). После создания экземпляра этого объекта с помощью команды ) для тех видов манипуляций и жестов, которые вам интересны. Документация по Windows.UI.Input.GestureSettings предоставляет полный набор возможностей, в том числе, такие жесты, как ) (ballineight.js) мы можем видеть, как настраивают средство распознавания жестов для жестов касания (tap), вращения (rotate), переноса (translate) и масштабирования (scale) (с инерцией):
gr = new Windows.UI.Input.GestureRecognizer(); // Настройка GestureRecognizer для определения манипуляций вращения, переноса, масштабирования, // с инерцией для данных трех компонентов манипуляции и обработки касания gr.gestureSettings = Windows.UI.Input.GestureSettings.manipulationRotate | Windows.UI.Input.GestureSettings.manipulationTranslateX | Windows.UI.Input.GestureSettings.manipulationTranslateY | Windows.UI.Input.GestureSettings.manipulationScale | Windows.UI.Input.GestureSettings.manipulationRotateInertia | Windows.UI.Input.GestureSettings.manipulationScaleInertia | Windows.UI.Input.GestureSettings.manipulationTranslateInertia | Windows.UI.Input.GestureSettings.tap; // Выключение обратной связи пользовательского интерфейса для жестов // (обратная связь все еще используется для PointerPoints) gr.showGestureFeedback = false;
Объект GestureRecognizer так же имеет множество свойств для настройки его специфических событий. Например, для жестов скольжения по диагонали (cross-slides) вы можете задать свойства crossSlideThresholds, crossSlideExact, и crossSlideHorizontally. Вы можете задать быстроту снижения скорости (в пикселях/мс2) посредством свойств inertiaExpansionDeceleration, inertiaRotationDeceleration, и inertiaTranslationDeceleration.
После настройки, вы начинаете передавать событий MSPointer* к объекту средсва распознавания жестов, в частности, его методам, которые называются processDownEvent, processMoveEvents, и processUpEvent (так же, если нужно, processMouseWheelEvent, и processInertia). В ответ, в зависимости от конфигурации, средство распознавания жестов запустит некоторое количество собственных событий. Во-первых, это дискретные события, наподобие crossSliding, dragging, holding, rightTapped, и tapped. Для остальных он запустит серию событий manipuationStarted, manipulationUpdated, manipulationInertiaStarting, и manipulationCompleted. Обратите внимание на то, что все эти события исходят из WinRT, поэтому убедитесь, что вызвали, когда нужно, removeEventListener.
Когда вы используете средство распознавания жестов напрямую, другими совами, прослушиваете события MSPointer*, передаете их средству распознавания жестов и затем прослушиваете его специфические события и действуете, опираясь на них, как в вышеприведенном примере, вместо работы с событиями MSGesture*, которые исходят от средства распознавания жестов по умолчанию, сконфигурированного с помощью объекта MSGesture.
Обратитесь к документации ) для того, чтобы узнать подробности и к примерам, чтобы посмотреть на образцы кода. В качестве дополнительного примера, вот фрагмент кода, который перехватывает небольшие горизонтальные перемещения с использованием установки manipulationTranslateX:
var recognizer = new Windows.UI.Input.GestureRecognizer();
recognizer.gestureSettings = Windows.UI.Input.GestureSettings.manipulationTranslateX;
var DELTA = 10;
myElement.addEventListener('MSPointerDown', function (e) {
recognizer.processDownEvent(e.getCurrentPoint(e.pointerId));
});
myElement.addEventListener('MSPointerUp', function (e) {
recognizer.processUpEvent(e.getCurrentPoint(e.pointerId));
});
myElement.addEventListener('MSPointerMove', function (e) {
recognizer.processMoveEvents(e.getIntermediatePoints(e.pointerId));
});
// Помните, что при работе с этими событиями нужен removeEventListener
recognizer.addEventListener('manipulationcompleted', function (args) {
var pt = args.cumulative.translation;
if (pt.x < -DELTA) {
// переместить вправо
}
else if (pt.x > DELTA) {
// переместить влево
}
});
Помимо использования средства распознавания жестов, помните, что вы всегда можете перейти на более низкий уровень и выполнить собственную обработку событий MSPointer* , если вам это нужно, и, таким образом, полностью обойти средство распознавания жестов. Это может быть нужно, если конфигурации, допустимые в средстве распознавания жестов, не соответствуют вашим особенным нуждам. В то же время, сейчас неплохая возможность для того, чтобы перечитать врезку "Создание совершенно новых жестов?" в конце раздела о языке касаний. Она посвящена некоторым вопросам о том, когда действительно нужны пользовательские жесты.
После всего, что мы узнали о сенсорном вводе данных и о других способах ввода, может показаться, что рассмотрение скромной клавиатуры нас разочарует. Но, все же, клавиатура остается крайне важной для ввода текста, будь это физическая клавиатура или экранная "программная" клавиатура. Она особенно важна для обеспечения специальных возможностей, так как некоторые пользователи физически не могут использовать мышь или другие устройства. На самом деле, раздел 3.5. "Сертификационных требований к приложениям для Windows 8" (http://msdn.microsoft.com/library/windows/apps/hh694083.aspx) говорит об обязательной поддержке приложениями ввода с клавиатуры.
К счастью, в приложениях для Магазина Windows нет ничего особенного в обработке ввода с клавиатуры, но длинный путь можно пройти короткими шагами. Вот общие положения, взятые из материала "Реализация специальных возможностей клавиатуры" (http://msdn.microsoft.com/library/windows/apps/hh700327.aspx):
keydown, keyup и keypress так, как вы уже знаете, особенно для реализации специальных сочетаний клавиш. Смотрите раздел "Стандартные сочетания клавиш" далее в этом разделе для того, чтобы взглянуть на типичные сопоставления команд.tabindex интерактивным элементам, которые должны поддерживать возможность переключения на них с помощью клавиши табуляции. Старайтесь не добавлять tabindex к неинтерактивным элементам, так как это может помешать программам чтения с экрана.accesskey для тех элементов, которым следует иметь клавиатурные сокращения.В приложении "Here My Am!", над которым мы работаем в этом курсе, обновленный вариант которого содержится в дополнительных материалах к этой лекции, добавлена полная поддержка клавиатуры. Это, в основном, коснулось добавления ) для сравнения кодов клавиш:
var Key = WinJS.Utilities.Key;
var image = document.getElementById("photo");
image.addEventListener("keydown", function (e) {
if (e.keyCode == Key.enter || e.keyCode==Key.space) {
image.click();
}
});
Все это работает и для физической клавиатуры, и для программной. Дело закрыто? Не вполне. Есть две особенности, касающиеся экранной клавиатуры: первая – это как показать ее, вторая – как ее появление подействует на макет приложения. В конце этого раздела мы так же взглянем на стандартные клавиатурные сокращения для команд приложения.
Показ программной клавиатуры происходит по одной и только одной причине: пользователь касается элемента управления для ввода текста с атрибутом contenteditable="true" (такого, как div или canvas). Нет API, которое отображает клавиатуру, она не появляется и тогда, когда вы щелкаете по подобному элементу управления мышью или пером, или перемещаетесь к нему, нажимая клавишу табуляции на физической клавиатуры.
Конфигурация клавиатуры зависит от типа элемента управления для ввода текста. Мы можем увидеть это в Сценарии 2 примера "Ввод: сенсорная клавиатура для ввода текста" (http://code.msdn.microsoft.com/windowsapps/Input-Touch-keyboard-text-f86e9bd9), где в файле html/ScopedView.html содержится несколько элементов управления input (окружающая разметка опущена), которые выглядят так, как показано на рис. 4.2.
<input type="url" name="url" id="url" size="50" /> <input type="email" name="email" id="email" size="50" /> <input type="password" name="password" id="password" size="50" /> <input type="text" name="text" id="text" size="50" /> <input type="number" name="number" id="number" /> <input type="search" name="search" id="search" size="50" /> <input type="tel" name="tel" id="tel" size="50" />
(рис 4.2) Программная клавиатура появляется, когда вы касаетесь поля ввода, как показано в примере
То, что показано на рис. 4.2. – это клавиатура по умолчанию. Если вы прикоснетесь к полю Search (Поиск), вы получите практически то же самое, за исключением того, что клавиша Enter превратится в клавишу Search (Поиск). Для поля Email клавиатура тоже похожа на стандартную, за исключением того, что появятся кнопки @ и .com у клавиши пробела:
Клавиатура для ввода URL выглядит так же, но клавиши @ нет, а Enter превращается в Go (Перейти):
Для поля ввода пароля на клавиатуре появляется клавиша Hide keypresses (Скрыть нажатия клавиш), которая отключает видимую анимацию нажатий клавиш – очень полезная функция, если вы записываете видео!
И, наконец, поля Number (Число) и Telephone (Телефон) вызывают клавиатуру, ориентированную на ввод чисел:
Во всех этих случаях кнопка в нижнем правом углу (значок которой очень похож на клавиатуру) позволяет вам переключаться в другие режимы отображения клавиатуры:
Доступные возможности здесь – это нормальная (широкая) клавиатура, разделенная клавиатура, панель распознавания рукописного ввода, и кнопка для того, чтобы полностью скрыть клавиатуру. Вот как выглядят разделеная клавиатура и панель для ввода рукописного текста:
Панель для рукописного ввода, это просто еще один режим программной клавиатуры: вы можете переключаться между режимами, и ваш выбор сохраняется между вызовами. (По этой причине, Windows не активирует автоматически панель рукописного ввода для указателя ручки, так как пользователь может предпочитать использование программной клавиатуры даже с пером).
Так же клавиатура настроаивает свой внешний вид при работе с элементами управления для ввода текста для предоставления подсказок и выделения появляющейся кнопки Insert (Вставить). Это показано в Сценарии 1 примера:

Вторая задача, касающаяся программной клавиатуры (да, я не забыл!) заключается в поддержке макета, когда клавиатура может перекрыть поле для ввода, имеющее фокус.
Когда появляется программная клавиатура или панель для ввода рукописного текста, система пытается убедиться в том, что поле ввода видимо, прокручивая содержимое, если это возможно. Это значает просто установку отрицательного вертикального смещения для всей вашей страницы в соответствии с высотой программной клавиатуры. Например, если я добавлю (в качестве радикального решения!) кучу элементов </br> в верхнюю часть страницы html/ScopedView.html в примере, так, чтобы элементы управления для ввода текста оказались внизу страницы, и затем коснусь одного из них, вся страница сдвинется вверх, как показано на рис. 4.3.:
(рис 4.3) Когда появляется программная клавиатура, Windows автоматически смещает страницу приложения вверх для того, чтобы поле ввода не оказалось перекрытым
Хотя такой подход может быть самым простым решением этой задачи, он не всегда идеален. К счастью, вы можете сделать кое-что более интеллектуальное, если хотите, прослушивая события ) и непосредственно подстраивая макет. Код для того, чтобы это сделать, можно найти (вы готовы к этому?) в примере "Реагирование на отображение экранной клавиатуры" (http://code.msdn.microsoft.com/windowsapps/Keyboard-Events-Sample-866ba41c)
var inputPane = Windows.UI.ViewManagement.InputPane.getForCurrentView();
inputPane.addEventListener("showing", showingHandler, false);
inputPane.addEventListener("hiding", hidingHandler, false);
В обработчике события showing объект eventArgs.occludedRect (имеющий тип Windows.Foundation.Rect) дает вам координаты и размеры обрасти, которую перекроет экранная клавиатура. В ответ на это мы можете настроить любые необходимые параметры макета и установить свойство eventArgs.ensuredFocusedElementInView в true. Это сообщит Windows, что автоматический сдвиг выполнять не нужно:
function showingHandler(e) {
if (document.activeElement.id === "customHandling") {
keyboardShowing(e.occludedRect);
// Будьте осторожны с этим свойством. Как только оно установлено, система
// не будет делать ничего для того, чтобы элемент, имеющий фокус, был видимым.
e.ensuredFocusedElementInView = true;
}
}
В примере показаны оба случая. Если вы коснетесь расположенного в нижней левой части приложения элемента defaultHandling, который имеет цвет морской волны, как показано на рис. 4.4, его showingHandler ничего не будет делать, и мы увидим поведение по умолчанию
(рис 4.4) Прикосновение к левому элементу defaultHandling в нижней части приведет к стандартному поведению при появлении клавиатуры, при котором содержимое страницы сдвигается вертикально
Если вы коснетесь элемента customHandling (справа), он вызовет функцию keyboardShowing для настройки макета:
function keyboardShowing(keyboardRect) {
// Часть кода опущена...
var elementToAnimate = document.getElementById("middleContainer");
var elementToResize = document.getElementById("appView");
var elementToScroll = document.getElementById("middleList");
// Записываем расстояние перемещения. Это упрощает расчеты
displacement = keyboardRect.height;
var displacementString = -displacement + "px";
// Находим последний видимый элемент в списке
var bottomOfList = elementToScroll.scrollTop + elementToScroll.clientHeight;
// Анимируем
showingAnimation = KeyboardEventsSample.Animations.inputPaneShowing(elementToAnimate,
{ top: displacementString, left: "0px" }).then(function () {
// После анимации макет занимает меньшее окно просмотра над клавиатурой
elementToResize.style.height = keyboardRect.y + "px";
// Прокручиваем список в нужное место, так чтобы прокрутка не была заметна
elementToScroll.scrollTop = bottomOfList - elementToScroll.clientHeight;
showingAnimation = null;
});
}
Этот код немного сложный, так как он анимирует перемещение различных элементов страницы. В результате макет задействованных элементов, а именно, того, к которому вы прикоснулись – подстраивается для того, чтобы дать место для клавиатуры. Другие элементы на странице, в противном случае, не затрагиваются. Результат показан на рис. 4.5.
(рис 4.5) Прикосновение к серому элементу customHandling справа демонстрирует пользовательскую обработку отображения клавиатуры
Последнее, что мне хотелось бы включить в раздел, посвященный клавиатуре – это таблица командных сочетаний клавиш, которые вы можете поддерживать в своем приложении. Это дополняет набор эквивалентов языка касаний, и вы, возможно уже привыкли пользоваться многими из них. Это полезно рассмотреть, так как приложения должны быть доступны для использования исключительно с помощью клавиатуры, и реализация сочетаний клавиш, наподобие этих – это один из шагов к удовлетворении данного требования и позволяет пользователям клавиатуры более эффективно использовать ваше приложение.
| Действие или команда | Сочетание клавиш |
|---|---|
| Перемещение фокуса | Tab |
| Назад (Навигация) | Кнопка "Назад" на специальных клавиатурах; клавиша backspace , везде, кроме текстовых полей; Alt+стрелка влево |
| Вперед (Навигация) | Alt+стрелка вправо |
| Вверх | Alt+стрелка вверх |
| Отмена/Выход из режима | ESC |
| Перемещение по элементам в списке | Клавиши-стрелки (и Tab) |
| Быстрое по элементам списка к соедующей группе, если выделение не следует автоматически за фокусом | Ctrl+клавиши-стрелки |
| Масштабирование (семантическое и оптическое) | Ctrl+"+" b Ctrl+ "-" |
| Быстрое перемещение к именованному элементу коллекции | Начало ввода текста |
| Перемещение на большое расстояние | Page up/down ( должно работать в пользовательском интерфейсе, поддерживающем сдвиг в горизонтальном и вертикальном направлениях) |
| Следующая позиция табуляции или группа | Ctrl+Tab |
| Предыдущая позиция табуляции или группа | Ctrl+Shift+Tab |
| N-ная позиция табуляции или группа | Ctrl+N (1-9) |
| Открыть панель приложения (Windows обрабатывает это автоматически) | Win+Z |
| Контектстное меню | Клавиша контекстного меню |
| Открыть дополнительную панель/выбрать элемент меню | Enter |
| Перейти куда-либо/активировать | Enter (в выделении) |
| Выбрать | Пробел |
| Выбрать смежные элементы | Shift+клавиши-стрелки |
| Закрепить элемент | Ctrl+Shift+! |
| Сохранить | Ctrl+S |
| Поиск | Ctrl+F |
| Печать | Ctrl+P (вызывает Windows.Graphics.Printing.PrintManager.showPrintUIAsync) |
| Копировать | Ctrl+C |
| Вырезать | Ctrl+X |
| Вставить | Ctrl+V |
| Новый элемент | Ctrl+N |
| Открыть адрес | Ctrl+L или Alt+D |
| Вращение | Ctrl+"," и Ctrl+"." |
| Проигрывание/Пауза | Ctrl+P (только медиа-приложения) |
| Следующий элемент | Ctrl+F (конфликтует с Поиском) |
| Предыдущий элемент | Ctrl+B |
| Перемотка назад | Ctrl+Shift+B |
| Перемотка вперед | Ctrl+Shift+F |
Помимо встроенной программной клавиатуры и панели для рукописного ввода, приложениям может понадобиться предоставить пользователю поверхность, которая может непосредственно принимать данные указателя в виде рукописного ввода (ink). Под этим я подразумеваю больше, чем использование элемента canvas и обработку событий MSPointer для рисования или создания растровых изображений. Рукописные штрихи – это структура данных, которая поддерживает реально вводимую информацию (в том числе – сведения о нажиме, угле наклона сенсора, скорости, если аппаратное обеспечение это поддерживает), что позволяет выполнять распознавание рукописного ввода и другие виды высокоуровневой обработки информации, которые невозможны при работе с растровыми данными. Работа с рукописным вводом, другими словами, напоминае работу с процессом рисования изображения, а не работу с готовым изображением, и это возможно для всех типов указателей ввода.
Поддержка рукописного ввода в WinRT располагается в пространстве имен ). Это API не зависит от конкретного презентационного фреймворка, не предоставляет она и средств визуализации: она имеет дело лишь с управлением структурами данных, которые приложение может вывести самостятельно, на поверхности для рисования наподобие canvas. Вот базовые функции этого API:
new Windows.UI.Input.Inking.InkManager().Windows.UI.Input.Inking.InkDrawingAttributes и установки атрибутов штриха, таких, как color, fitToCurve (в противоположность прямым линиям, проводимым по умолчанию), ignorePressure, penTip (Windows.UI.Input.Inking.PenTipShape.circle или rectangle), и size (объект Windows.Foundation.Size со свойствами height и width).MSPointerDown, MSPointerMove, и MSPointerUp, которые обычно нужны для целей визуализации. eventArgs.currentPoint это объект Windows.UI.Input.PointerPoint, который содержит идентификатор указателя, координаты точки и свойства наподобие pressure, tilt, и twist.PointerPoint методам управляющего объекта processPointerDown, processPointerUpdate, и processPointerUp.processPointerUp, управляющий объект рукописного ввода создает объект Windows.UI.Input.Inking.InkStroke для проведенного штриха. Штрихи можно получить посредством метода getStrokes управляющего объекта и вывести так, как нужно.InkStroke и переданы управляющему объекту посредством метода addStroke. Объекты штрихов можно удалять с использованием метода deleteStroke.Управляющий объект, кроме того, предоставляет методы для распознавания рукописного текста на основе содержащихся в нем штрихов, сохранения и загрузки данных и обработки различных режимов, таких, как рисование и стирание штрихов. Полную демонстрацию этого вы можете найти в примере "Ввод: рукописный ввод" (http://code.msdn.microsoft.com/windowsapps/Ink-App-sample-61abaec3), который показан на рис. 4.6. Этот пример позволит вам увидеть полный набор возможностей платформы рукописного ввода, в том числе – распознавание рукописного текста.
(рис 4.6) Пример работы с платформой рукописного ввода содержит множество команд на панели приложения. Зеленый текст "Hello" в верхнем левом углу был создан после выделения рукописного "Hello" и использования команды Recognition (Распознавание)
SDK так же включает в себя пример "Ввод: упрощенный рукописный ввод" (http://code.msdn.microsoft.com/windowsapps/Input-simplified-ink-sample-11614bbf) для демонстрации сценария, ориентированного на распознавание рукописного текста, как показано на рис. 4.7. Вам следует знать, что это пример, который вовсе не поддерживает касания – он ограничен мышью и пером и использует для активации команд клавиатурные сокращения вместо панели приложения. Взгляните на функцию keydown в файле simpleink.js для того, чтобы увидеть список команд вида "Ctrl + другая клавиша". Клавиша пробел активирует распознавание введенного текста, клавиша backspace очищает рабочую область. Как вы можете видеть на рисунке, я могу думать, что распознавание рукописного текста работает очень хорошо! (Это говорит мне о том, что образцы рукописного текста, которые я передал команде инженеров Microsoft где-то в середине 1990-х, стали значительным вкладом в их дело).
(рис 4.7) Пример отлично распознает мой небрежный текст, написанный мышью
Прежде чем мы рассмотрим датчики в целом, я хочу отдельно рассмотреть возможности определения географического расположения в приложениях для Магазина Windows, так как это API устроено не так, как у других датчиков. Мы уже использовали эту возможность, начиная с Главы 2 курса "Введение в разработку приложений для Windows 8 с использованием HTML, CSS и JavaScript", в приложении "Here My Am!", но нам нужен более полной обзор этой крайне полезной возможности.
В отличие от других датчиков, только определение местоположения имеет выделенную возможность, которую вы должны объявить в манифесте. То, где вы находитесь на Земле, это абсолютные данные, которые являются частью личной информации. Таким образом, пользователи должны дать согласие, прежде чем приложение сможет получить эти сведения, и ваше приложение так же должно предоставлять "Соглашение о конфиденциальности" в Магазине Windows. Другие датчики, в отличие от этого, предоставляют относительную информацию – вы не можете, например, узнать что-либо о человеке из данных о наклоне устройства, о его перемещении или из сведений о том, насколько яркий свет его освещает. Соответственно, вы можете использовать другие датчики без объявления каких-либо особых возможностей.
Как вы можете знать, данные о географическом местоположении могут быть получены двумя различными путями. Первый, и наиболее точный, конечно, это получение данных с GPS-приемника, который получает информацию от геостационарных спутников, расположенных на орбитах в несколько сотен миль. Другим, достаточно полезным, хотя и не всегда точным средством является попытка узнать местоположение на основе IP-адреса проводного сетевого соединения или триангуляция на основе расположения доступных точек доступа WiFi. В любом случае, WinRT делает все возможное для того, чтобы дать вам достойные показатели.
Для доступа к данным о географическом местоположении, вы должны сначала создать экземпляр средства определения местоположения WinRT, ). Когда он у вас есть, вы затем можете вызвать его метод getGeopositionAsync, результаты работы которого (помещенные в обработчик завершения), представляют собой объект типа Geoposition (в том же пространстве имен Windows.Devices.Geolocation, если не указано иное). Вот код из "Here My Am!":
var gl = new Windows.Devices.Geolocation.Geolocator();
gl.getGeopositionAsync().done(function (position) {
//Сохранить для целей общего доступа
lastPosition = { latitude: position.coordinate.latitude,
longitude: position.coordinate.longitude };
}
У метода ), применяя который вы можете указать два параметра: максимальную давность для кэшированных данных (то есть то, насколько свежими должны быть данные) и значение тайм-аута, в течение которого вы хотите ждать ответ. И то и другое значение измеряются в миллисекундах.
Объект ) содержит два свойства. Первое, это свойство ), который предоставляет свойства ), который содержит свойство ), в частности – класс MapAddress (http://msdn.microsoft.com/library/jj663670.aspx) для того, чтобы узнать адрес по имеющимся координатам.
Вы можете задать желаемую точность измерений посредством свойства desiredAccuracy средства определения местоположения, которое может принимать значение PositionAccuracy.default или PositionAccuracy.high. Последнее, учтите, создаст большую нагрузку на аппаратное обеспечение. Это может привести к повышению стоимости использования лимитированного сетевого соединения и ускорить разряд батарей, поэтому устанавливайте этой свойство в значение high только тогда, когда это имеет ключевую важность для работы с приложением.
Средство определения местоположения так же предоставляет свойство ), содержащим такие члены, как ready, initializing, noData, disabled, notInitialized, и notAvailable. Должно быть очевидным, что вы не сможете получить данные от средства определения местоположения, которое находится в состоянии, отличном от ready (готово). Для того, чтобы отследить это, вы можете прослушивать событие средства определения местоположения statusChanged, свойство eventArgs.status которого в вашем обработчике будет содержать PositionStatus. Это полезно, когда вы обнаруживаете, что GPS-приемнику может понадобиться пара секунд на то, чтобы предоставить необходимые данные. Например, вот как используется данное событие в примере "Определение местопол
ожения" (http://code.msdn.microsoft.com/windowsapps/Geolocation-2483de66) из Windows SDK:
geolocator = new Windows.Devices.Geolocation.Geolocator();
geolocator.addEventListener("statuschanged", onStatusChanged); //Позже не забудьте удалить
function onStatusChanged(e) {
switch (e.status) {
// ...
}
}
Обратите внимание на то, что PositionStatus и statusChanged отражают чтение данных с GPS-устройства, и на чтение не влияет разрешение на получение данных о местоположении в панели параметров. Как показано в "Here My Am!", приложению не нужно проверять разрешение, пытаясь получить значения параметров, что является особенностью получения данных от устройства.
Два других интересных свойства средства определения местоположения – это movementThreshold, расстояние в метрах, на которое может переместиться устройство, прежде чем будет вызвана следующая операция чтения данных (это может быть использовано в сценариях с установкой географических границ), и reportInterval, который задается в миллисекундах между попытками чтения данных. Используйте последний параметр умеренно, устанавливайте его в то значение, которое вам действительно нужно, так как следует стремиться к минимизации сетевой активности и активности GPS-датчика. В любом случае, когда средство определения местоположения получает новые данные и обнаруживает, что устройство переместилось за пределы movementThreshold, оно вызывает событие positionChanged, свойство которого eventArgs.position является новым объектом Geoposition. Это так же показано в Сценарии 1 примера об определении местоположения:
geolocator.addEventListener("positionchanged", onPositionChanged);
function onPositionChanged(e) {
var coord = e.position.coordinate;
document.getElementById("latitude").innerHTML = coord.latitude;
document.getElementById("longitude").innerHTML = coord.longitude;
document.getElementById("accuracy").innerHTML = coord.accuracy;
}
В случае с movementThreshold и reportInterval, рассматривайте их с позиций нужд приложения, основанных на точности и/или интервале обновления данных о местоположении, которые вы используете. Например, данные о погоде относятся к регионам и могут обновляться лишь раз в час. Поэтому movementThreshold может быть установлено в милях или километрах, reportInterval – на 15, 30, 60 минут или на более длительное время. Приложения работы с картами или предназначенные для автомобилистов, с другой стороны, работают с данными, весьма чувствительными к местоположению и, таким образом, будут иметь гораздо меньшие пороговые значения и более короткие интервалы обновления.
Когда имеет значение время жизни устройства от батарей, лучше всего просто получать новые данные, когда они нужны пользователю, вместо того, чтобы выполнять это через некоторые регулярные интервалы. Но это, опять же, зависит от сценария приложения, и вы можете так же предоставить параметр, который позволяет пользователю управлять активностью подсистемы определения местоположения.
Так же очень важно отметить, что приложения не получают события positionChanged или statusChanged, когда они приостановлены, если только вы не зарегистрировали для этой цели фоновую задачу, запускаемую по таймеру и пользователь не добавил приложение на экран блокировки. Мы поговорим об этом подробнее в лекции 2 курса "Программная логика приложений для Windows 8, созданных с использованием HTML, CSS и JavaScript и их взаимодействие с системой", и вы так же сможете увидеть, как это работает в Сценарии 3 примера об определении местоположения. Если, однако, вы не используете фоновые задачи или пользователь не поместил приложение на экран блокировки, и вам все еще нужно отслеживать местоположение пользователя, обрабатывайте соответствующим образом событие resuming и обновляйте там эти данные.
С другой стороны, некоторые сценарии, исопользующие определение местоположения, такие, как навигация, нуждаются в том, чтобы экран устройства был активен (предотвращая автоматическое выключение экрана), даже если пользователь ничего не делает. Для этой цели вы можете использовать класс в частности, его методы requestActive и requestRelease, которые вы можете вызывать в начале и в конце сеанса навигации. Конечно, поддержание экрана во включенном состоянии приводит к потреблению большей энергии батарей, используйте эту возможность только когда это необходимо – когда приложение используется для целей навигации – и не прибегайте к выполнению подобного запроса при запуске приложения. В противном случае ваше приложение, возможно, прослывет в Магазине Windows как неумеренный потребитель энергии!
Опытные разработчики HTML/JavaScript могут удивиться, почему WinRT предоставляет API для определения местоположения в то время, как в HTML5 оно уже есть: window.navigator.geolocation и его метод getCurrentPosition , который возвращает объект с координатами. Причина подобного заключается в том, что другие языки, такие, как C#, VisualBasic и C++ не имеют других API для получения подобных данных, что предоставляет выбор разработчикам на HTML/JavaScript. Если посмотреть глубже, то API HTML5 пользуется теми же данными, что и API WinRT, требует объявления той же возможности Расположение (Location) в манифесте, и нуждается в таком же разрешении пользователя, то есть, в основном, эти два API идентичны. Я дал бы API WinRT небольшое преимущество за счет опции movementThreshold , которая помогает приложению экономить электроэнергию и делает возможной простую постановку географических границ. Выполнение тех же действий в HTML5 потребует более частых запросов и приведет к большему потреблению энергии. Во многих сценариях, однако, вы можете использовать либо то, либо другое, с одинаковыми результатами. Однако, как и другие API WinRT, Windows.Devices.Geolocation доступно лишь страницам локального контекста приложений для Магазина Windows. На страницах веб-контекста вы можете пользоваться API HTML5.
Как я говорил во введении к лекции, мне нравится думать о датчиках как об одной из форм ввода данных. Это имеет смысл, так как каждое устройство, которое интегрированно в наши компьютеры, что мы принимаем как должное, является одним из видов интерфейса для взаимодействия пользователя и машины. Со временем, я полагаю, многие датчики, которые сейчас кажутся нам новыми, станут стандартным оборудование почти везде.
Датчики, опять же, это способ понять взаимоотношения устройства и физического мира, окружающего его, и их использование подразумевает ввод данных, так как вы, будучи человеком, можете влиять на эти взаимоотношения, в основном, перемещая устройство в пространстве или меняя их окружение. Датчики так же могут быть использованы как средства прямого ввода информации, вызывая перемещение объектов на экране, вместо использования какой-то формы абстрактного ввода данных наподобие мыши или клавиатуры. Например, вместо того, чтобы использовать абстрактное сочетание клавиш для того, чтобы наклонить игровое поле, вы можете, используя датчики, просто наклонить устройство. Встряхивание устройства, на самом деле, является хорошо известным физическим жестом, который может быть связан с командой вроде "Повтори, будь ты проклят! Почему ты не делаешь то, что я хочу?". Разве мы долгие годы не трясли наши компьютеры или не стучали по ним, когда они не делали то, что нам нужно? А компьютер с датчиками вполне может отреагировать на такое воздействие!
Вот, какую информацию могут нам сообщить различные датчики:
Эти датчики предоставляет API WinRT
Так как работа всех этих датчиков очень похожа друг на друга (это сделано специально, за исключением простого датчика ориентации, который не похож на другие), я хочу показать общий шаблон работы с API датчиков вместо детальных примеров для каждого из них. Подобные примеры можно найти среди SDK-примеров "Акселерометр" (http://code.msdn.microsoft.com/windowsapps/Accelerometer-Sensor-Sample-22982671), "Компас" (http://code.msdn.microsoft.com/windowsapps/Compass-Sensor-Sample-0ed09c55), "Гирометр" (http://code.msdn.microsoft.com/windowsapps/Gyrometer-Sensor-Sample-4fe891d9), "Инклинометр" (http://code.msdn.microsoft.com/windowsapps/Inclinometer-Sensor-Sample-0cd0bf84), "Датчик освещенности" (http://code.msdn.microsoft.com/windowsapps/LightSensor-Sample-4477824c) и "Датчик ориентации" (http://code.msdn.microsoft.com/windowsapps/OrientationSensor-sample-0b1732be).
Шаблон их использования выглядит следующим образом, некоторые особенности приведены в таблице ниже:
Получение объекта датчика посредством Windows.Devices.Sensors.<sensor>.getDefault().
Вызов метода getCurrentReading этого объекта для получения данных.
Для последующего получения данных, настроить свойства объекта minimumReportInterval и reportInterval (и то и другое – в миллисекундах) и прослушивать событие объекта readingchanged. В ответ обработчик события получит объект подходящего типа с прочитанными данными. В случае с определением местоположения, разумно подойтите к установке этих параметров, что поможет увеличить время работы устройства от батарей, предотвращая излишний расход электроэнергии датчиками.
| Название датчика (Windows.Devices.Sensors.) | Дополнительные члены | Тип возвращаемых данных (Windows.Devices.Sensors) | Свойства возвращаемых данных (timestamp Имеет тип Date; остальные являются числами, если не указано иное) |
|---|---|---|---|
| Акселерометр (http://msdn.microsoft.com/library/windows/apps/windows.devices.sensors.accelerometer.aspx) | Событие: shaken (аррументы события содержат лишь свойство timestamp) |
) | accelerationX (в G), accelerationY,
accelerationZ, timestamp
|
| Компас (http://msdn.microsoft.com/library/windows/apps/windows.devices.sensors.compass.aspx) | n/a | ) | headingMagneticNorth (градусы),
headingTrueNorth, timestamp
|
| Гирометр (http://msdn.microsoft.com/library/windows/apps/windows.devices.sensors.gyrometer.aspx) | n/a | ) | angularVelocityX (градусы в секунду), angularVelocityY, angularVelocityZ, timestamp |
| Инклинометр (http://msdn.microsoft.com/library/windows/apps/windows.devices.sensors.inclinometer.aspx) | n/a | ) | pitchDegrees (градусы), rollDegrees (градусы), yawDegrees (градусы), timestamp |
| Датчик освещенности (http://msdn.microsoft.com/library/windows/apps/windows.devices.sensors.lightsensor.aspx) | n/a | ) | illuminenceInLux (люксы), timestamp |
| Датчик ориентации (http://msdn.microsoft.com/library/windows/apps/windows.devices.sensors.orientationsensor.aspx) | n/a | ) | quaternion, (SensorQuaternion содержит свойства w, x, y, и z) rotationMatrix
(Sensor-RotationMatrix содержит свойства
m11, m12, m13, m21, m22, m23, m31, m32, m33), timestamp
|
Вот образец кода из примера "Гирометр" (js/scenario1.js):
gyrometer = Windows.Devices.Sensors.Gyrometer.getDefault();
var minimumReportInterval = gyrometer.minimumReportInterval;
var reportInterval = minimumReportInterval > 16 ? minimumReportInterval : 16;
gyrometer.reportInterval = reportInterval;
gyrometer.addEventListener("readingchanged", onDataChanged);
// Не забудьте удалить при необходимости
function onDataChanged(e) {
var reading = e.reading;
document.getElementById("eventOutputX").innerHTML = reading.angularVelocityX.toFixed(2);
document.getElementById("eventOutputY").innerHTML = reading.angularVelocityY.toFixed(2);
document.getElementById("eventOutputZ").innerHTML = reading.angularVelocityZ.toFixed(2);
}
В случае с датчиком ориентации, понятие кватернион (quaternion) легче всего понять как вращение точки [x,y,z] вокруг одной произвольной оси. В этом – отличие от матрицы вращения, которая представляет вращение по всем трем осям. Математическая основа кватернионов довольно экзотична, она включает в себя геометрические понятия комплексных чисел, математические понятия мнимых чисел, но работать с ними просто и фреймворки наподобие DirectX поддерживают их. Посмотрите пример "Сенсор ориентации" для того, чтобы больше узнать об этом.
Говоря об ориентации, я упоминал, что ) работает немного не так, как остальные. Его цель – в предоставлении информации о квадранте (quadrant) ориентации, а не в предоставлении данных о точной ориентации устройства, и, возможно, это все, что вам нужно. Например, приложению для просмотра звездной карты нужно знать, расположено ли устройство вниз экраном, таким образом оно сможет настроить вывод данных (используя и показания компаса) для того, чтобы то, что оно отображает на экране, соответствовало бы виду неба.
В итоге, использование этого датчика выглядит так:
Windows.Devices.Sensors.SimpleOrientation.getDefault для того, чтобы получить объект.getCurrentOrientation для того, чтобы получить результаты чтения данных.orientationChanged используется для последующего получения данных, где eventArgs содержит свойства orientation (результат чтения данных) и timestamp.notRotated ("portrait up", книжная-сверху), rotated90DegreesCounterclockwise ("landscape left" альбомная-слева), rotated180DegreesCounterclockwise ("portrait down" книжная-снизу), rotated270DegreesCounterclockwise ("landscape right" альбомная-справа). Обратите внимание на то, что все это не имеет никакого отношения к состояниям просмотра наподобие полноэкранного альбомного или полноэкранного портретного режимов просмотра.faceup, facedown (лицевой сторонй вверх или вниз, только для планшентых устройств).Демонстрацию использования этого датчика вы можете найти в примере "Простой датчик ориентации" (http://code.msdn.microsoft.com/windowsapps/SimpleOrientationSensor-d948ac62).
click, в то время как другие требуют создания объекта MSGesture, связи этого объекта с указателем и обработку последовательностей событий MSGesture*, которая используется для реализации манипуляций и перемещения с инерцией.wheel в объект жеста). Поддержка клавиатуры должна быть реализована отдельно, она использует обычные события HTML/JavaScript.readingChanged. С ними легко работать, что оставляет большую часть сил на поиск креативных способов их применения!Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.