Winrt, сенсорний ввід

Однією з найбільш перспективних особливостей Windows Runtime стала консолідація введення з сенсорного екрану, миші і пера. Тепер розробнику не потрібно додавати підтримку сенсорного введення в існуючу програму, орієнтоване на роботу з мишею, або додавати підтримку миші в додаток з сенсорним введенням. З самого початку програміст використовує всі ці форми введення як в цілому взаємозамінні. Відповідно до термінології інтерфейсу програмування Windows Runtime я буду використовувати слово «покажчик» (pointer) для позначення введення з сенсорного екрану, миші і пера (також званого стилусом) в тих випадках, коли розрізняти фактичне пристрій введення не обов'язково.

Для обробки введення від покажчика найкраще використовувати існуючі елементи управління Windows Runtime. Як ви вже бачили, всі стандартні елементи управління - Button, Slider, ScrollViewer і Thumb - реагують на введення від покажчика і використовують його для передачі з додатком вхідних даних більш високого рівня. Однак в деяких випадках, програмісту буває потрібно отримати безпосереднє введення від покажчика. Для цих цілей UIElement визначає три сімейства подій:

Вісім низькорівневих подій, імена яких починаються із префікса Pointer.

Вісім високорівневих подій, імена яких починаються із префікса Manipulation.

Події Tapped, RightTapped, DoubleTapped і Holding.

Клас Control доповнює ці події віртуальними захищеними методами, імена яких починаються із префікса On, за яким слідує ім'я події.

Для отримання введення від покажчика у класу, похідного від FrameworkElement, властивість IsHitTestVisible має дорівнювати true, а властивість Visibility має дорівнювати Visible. У класу, похідного від Control, властивість IsEnabled має дорівнювати true. Елемент повинен мати графічне представлення на екрані; фон класу, похідного від Panel, може бути прозорим, але не рівним null.

Всі ці події асоціюються з елементом, що знаходяться під пальцем, мишею або пером на момент виникнення події. Єдиний виняток - «захоплення» покажчика елементом.

В якомусь сенсі можна обійтися тільки подіями Pointer. Наприклад, щоб реалізувати функцію «розтягування» фотографій двома пальцями, можна відстежувати дві події Pointer для цих пальців і оцінити величину їх розбіжності. Однак обчислення такого роду вже реалізовані в подіях Manipulation. Ці події, які об'єднують дії кількох пальців в одну операцію, ідеально підходять для переміщення, розтягнення, масштабування і обертання візуальних об'єктів.

У деяких додатках вибір між подіями Pointer і Manipulation не настільки очевидний. Ймовірно, першим кандидатом все ж повинні стати події Manipulation - особливо якщо ви думаєте «Сподіваюся, користувачеві не прийде в голову використовувати другий палець, тому що я його все одно ігнорую». Якщо користувач використовує два пальця там, де досить одного, введення з декількох пальців буде об'єднаний.

Однак події Manipulation пов'язані з неминучою затримкою. Палець, торкатися до екрану, повинен злегка переміститися, щоб його дія була інтерпретована як частина операції. Події Manipulation не спрацьовують при торканні або простому натисканні. Іноді цієї затримки буває досить для переходу на події Pointer. Прикладом служить для користувача елемент управління XYSlider, який ми створимо пізніше. У наведеній версії використовуються події Manipulation, тому що дії кількох пальців в цьому контексті не потрібні. Але час затримки представляє безперечну проблему, тому пізніше буде приведена інша версія, яка використовує події Pointer.

Події Pointer генеруються на рівні вікна об'єктом CoreWindow. і ви можете визначати власні події Manipulation з використанням GestureRecognizer. але я обмежуся подіями, обумовленими UIElement, і віртуальними методами, визначеними Control. Також ми не будемо детально розглядати інформацію про пристрої, яку можна отримати з класів простору імен Windows.Devices.Input.

події Pointer

З восьми подій Pointer п'ять зустрічаються дуже часто. Якщо доторкнутися пальцем до доступного і мабуть примірнику класу, похідного від UIElement, перемістити і підняти палець, будуть згенеровані п'ять подій Pointer в наступній послідовності:

Події Pointer від пальців генеруються тільки при дотику до екрану або відпуску. В області сенсорного введення не існує такого поняття, як «наведення» (hover). З мишею інша справа. Миша генерує події PointerHoved навіть без натискання кнопки. Припустимо, ви підвели курсор миші до конкретного елементу, натиснули кнопку, перемістили миша, відпустили кнопку, а потім зрушили миша за межі елементу. Елемент генерує наступну серію подію:

Множинні події PointerPressed і PointerReleased також можуть генеруватися при натисканні і відпуску різних кнопок миші.

Тепер займемося пером. Елемент починає реагувати на перо ще до того, як користувач доторкнеться до екрану, тому спочатку відбувається подія PointerEntered, а за ним слід подія PointerMoved. При дотику до екрану генерується подія PointerPressed. Перемістіть перо і підійміть його. Елемент продовжує видавати події PointerMoved після PointerReleased, але при подальшому переміщенні пера серія завершується подією PointerExited. В результаті генерується та ж послідовність подій, що і з мишею.

Коли користувач повертає колесо миші, генерується подія PointerWheelChanged.

Два залишилися події зустрічаються ще рідше: PointerCaptureLost і PointerCanceled. Захоплення покажчика буде розглядатися пізніше, коли важливість події PointerCaptureLost стане більш очевидною.

Я ще ніколи не бачив події PointerCanceled - навіть при відключенні миші від комп'ютера, але подія існує для передачі інформації про таких помилках.

Клас PointerRoutedEventArgs визначає два властивості, загальних для перенаправляє подій:

Властивість OriginalSource позначає елемент, який ініціював подію.

Властивість Handled дозволяє зупинити подальше перенаправлення події але візуальному дереву.

Об'єкт PointerRoutedEventArgs містить багато корисної інформації; нижче виділені лише найважливіші моменти. Клас також визначає наступні члени:

Властивість Pointer типу Pointer.

Властивість KeyModifiers. позначає стан клавіш Shift, Control, Menu (також відомої як Alt) і Windows.

Метод GetCurrentPoint (). повертає об'єкт PointerPoint.

Будьте уважні: ми вже починаємо працювати з класами Pointer (визначеним у просторі імен Windows.UI.Xaml.Input) і PointerPoint (визначеним у просторі імен Windows.UI.Input).

Клас Pointer містить всього чотири властивості:

Містить цілочисельний ідентифікатор, що визначає миша, перо або окремий палець.

Приймає значення Touch, Mouse або Pen.

Логічне властивість IsInRange

Визначає, чи знаходиться пристрій в діапазоні екрану.

Логічне властивість IsInContact

Визначає, торкається чи палець або перо до екрану, або натиснута кнопка миші.

Властивість PointerId грає виключно важливу роль - воно використовується для відстеження рухів окремих пальців. Програма, обробна події Pointer, майже завжди визначає словник, в якому властивість PointerId служить ключем.

Судячи з імені, метод GetCurrentPoint () класу PointerRoutedEventArgs повертає поточні координати покажчика - і це дійсно так, але крім цього, метод також повертає багато іншої корисної інформації. Так як позицію зручно відраховувати щодо конкретного елементу, метод GetCurrentPoint () отримує аргумент типу UIElement. Об'єкт PointerPoint, що повертається цим методом, дублює частину інформації з Pointer (властивості PointerId і IsInContact), а також надає іншу корисну інформацію:

Тип Point, повертає координати (x, y) покажчика на момент події.

Тип ulong, тимчасова мітка.

Тип PointerPointProperties, визначається в Windows.UI.Input - додаткова інформація.

Властивість Position завжди задається щодо лівого верхнього кута елемента, переданого методу GetCurrentPoint ().

Клас PointerRoutedEventArgs також визначає метод з ім'ям GetIntermediatePoints (). який схожий на GetCurrentPoint (), але повертає колекцію об'єктів PointerPoint. Дуже часто ця колекція містить всього один об'єкт (той самий об'єкт PointerPoint, який повертає GetCurrentPoint), але для події PointerMoved таких об'єктів може бути кілька, особливо якщо обробник події працює не дуже швидко. Зокрема, я помітив, що GetIntermediatePoints повертає кілька об'єктів PointerPoint на планшеті Microsoft Surface.

Клас PointerPointProperties визначає 22 властивості, що надають детальну інформацію про подію: ознаки натискання кнопок миші, ознака натискання кнопки на корпусі пера, нахил пера, контактний прямокутник пальця на екрані (якщо інформація доступна), сила натискання пальця або пера (якщо інформація доступна) і MouseWheelDelta . Ви можете використовувати всю цю інформацію на свій розсуд. Зрозуміло деякі властивості будуть недоступні на деяких пристроях, а отже, збережуть значення за замовчуванням.

Просте сенсорне додаток

Ймовірно, класичним мультисенсорним додатком є ​​програма, яка дозволяє малювати пальцями на екрані. Щоб написати таку програму, досить обробляти всього три події Pointer і перевіряти дві властивості з аргументів події, але результат має серйозний недолік, який не компенсується його простотою.

Файл MainPage.xaml проекту FingerPaint1 просто призначає ім'я для стандартної панелі Grid:

Перш за все файл фонового коду визначає об'єкт Dictionary з ключем типу uint. Раніше я згадував про те, що практично кожна програма, обробна події Pointer, містить певний різновид Dictionary. Тип даних, що зберігаються в словнику, залежить від програми; іноді додаток визначає клас або структуру спеціально для цієї мети. У простій програмі для малювання кожен палець, торкатися до екрану, буде малювати свій об'єкт Polyline, так що в Dictionary може зберігатися цей екземпляр Polyline:

У перевизначенні OnPointerPressed програма створює об'єкт Polyline і призначає йому випадковий колір. Перша точка визначається за місцезнаходженням покажчика. Об'єкт Polyline додається на панель Grid і в словник.

При наступних викликах OnPointerMoved властивість PointerId ідентифікує палець, так що програма може звернутися до об'єкта Polyline, пов'язаного з цим пальцем, і включити новий об'єкт Point в Polyline. Так як це той же примірник, що і Polyline в Grid, довжина екранного об'єкта збільшується в міру руху пальця.

Оброблювач OnPointerReleased просто видаляє об'єкт Polyline зі словника, вважаючи його завершеним.

Запустіть програму і проведіть декількома пальцями по екрану:

Winrt, сенсорний ввід

Кожен палець малює власну лінію як серію з'єднаних точок певного кольору. Для малювання можна використовувати мишу і перо.

Раніше я згадував про те, що у цього коду є недолік. Перевизначення OnPointerMoved і OnPointerReleased перевіряють, що ідентифікатор існує як ключ в словнику, перш ніж використовувати його для звернення до словника. Це дуже важливо для обробки операцій з мишею і пером, тому що ці пристрої генерують події PointerMoved до подій OnPointerPressed. Але спробуйте зробити наступне: переведіть програму в режим Snap View, пальцем проведіть лінію за межі сторінки, а потім назад.

Winrt, сенсорний ввід

Зверніть увагу на пряму лінію на лівій стороні. Вона малюється, коли палець знову входить на сторінку, і вказує на те, що програма не отримує події PointerMoved в той час, коли палець виходить за її межі. Повторіть досвід з мишею - те ж саме.

А тепер спробуйте зробити наступне: проведіть пальцем лінію зсередини сторінки в зовнішню точку і відпустіть палець. Потім знову проведіть всередині сторінки. Начебто все нормально.

Тепер спробуйте зробити те ж з мишею. Натисніть кнопку миші над сторінкою FingerPaint1, перемістіть мишу за межі сторінки та відпустіть кнопку. Тепер знову переведіть миша на сторінку FingerPaint1. Програма продовжує малювати лінію, незважаючи на те, що кнопка миші відпущена! Це безумовно неправильно (хоча вам напевно зустрічалися програми, які ведуть себе подібним чином). Тепер при натисканні кнопки миші видається виняток, тому що метод OnPointerPressed намагається додати в словник об'єкт з ключем, вже існуючим в словнику. На відміну від пера чи торкань пальцем, всі події миші мають однакові ідентифікатори. Давайте виправимо ці недоліки в наступній статті.