Поддержка сенсорного ввода в Metro-приложениях Windows 8* с помощью C#

Автор David Medawar

Скачать статью

Скачать Enabling Touch in Windows 8* Metro Style Apps with C# [Eng., PDF 314KB]

 

Цель

В этой статье описывается реализация сенсорного ввода в Metro-приложениях Windows 8* с помощью языка программирования C# в среде разработки Visual Studio* 11. Здесь рассматривается привязка событий сенсорного ввода с помощью языка XAML и «базового» кода (C#) для создания привлекательных пользовательских интерфейсов. В качестве примера работы сенсорного ввода используется детская математическая игра.

 

1. Введение

Приложения, в которых активно задействуются сенсоры, получают все более широкое распространения и приобретают особую популярность среди пользователей. Metro-приложения Windows 8 и соответствующая среда разработки Visual Studio (VS) поддерживают сенсорный ввод: разработчики получают в свое распоряжение сенсорные API, обеспечивающие удобное сенсорное управление для конечных пользователей. В этой статье описывается реализация сенсорного ввода в Metro-приложениях с помощью XAML и базового кода C#. Сочетание разметки элементов интерфейса с помощью XAML и базового кода C# реализует всю скрытую от конечных пользователей механику работы элементов интерфейса.

Исходный код, приведенный ниже, был написан с помощью Windows 8 Release Preview (сборка 8400, дата выпуска: 31 мая 2012 г.), Visual Studio Ultimate 2012 RC (11.0.50522.1 RCEL) и .NET* Framework версии 4.5.50501.

 

2. Обработка событий касания в XAML

Предположим, что приложение должно что-то сделать всякий раз, когда пользователь коснется элемента пользовательского интерфейса. Рассмотрим пример с Image. Если начать с объявления рисунка в XAML, соответствующий обработчик события касания можно определить следующим образом:

<Image x:Name="c29" Height="100" Width="100" Tapped="Image_Tapped" Grid.Row="1" Grid.Column="8" />

В этом примере определяется изображение «с29», указываются параметры его размера и положения в сетке. Обратите внимание на элемент:

Tapped="Image_Tapped"

Этот код означает, что при всяком касании данного изображения будет вызван метод Image_Tapped из кода C#.

 

3. Код обработчика события касания в C#

Элементы пользовательского интерфейса могут использовать метод обработчика касаний, указанный в предыдущем разделе на примере определения XAML рисунка. Для этого выполняемый код должен определять, какого именно элемента интерфейса коснулся пользователей. Эта задача решается с помощью FrameworkElement. Код может выглядеть примерно следующим образом:

        //called when one of the game images is clicked
        private void Image_Tapped(object sender, RoutedEventArgs e)
        {
            //find out which game object was clicked
            FrameworkElement source_image = e.OriginalSource as FrameworkElement;

            for (int row = 0; row < ROWS; row++)
            {
                for (int col = 0; col < COLS; col++)
                {
                    //we found a match!
                    if (source_image.Name.Equals(images[row, col].Name))
                    { …//handler logic

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

В дополнение к дескриптору DoubleTapped в XAML, можно использовать и DoubleTapped. Другие дескрипторы, такие как Clicked, не рассматриваются в данном документе.

 

4. События указателя

Дескриптор Tapped довольно прост в использовании. Пора продвинуться немного дальше. Откройте следующую ссылку:

http://msdn.microsoft.com/en-us/library/windows/apps/xaml/hh465387#using_manipulation_events

Прочтите раздел под названием «Использование событий манипуляции». Этот метод можно использовать для обнаружения и сенсорного ввода, и ввода с помощью мыши. Но у этого API есть ограничение: поддерживается только касание одним пальцем. Обработка мультисенсорного ввода описывается в следующем разделе.

Использование Pointer аналогично использованию Tapped: это простой дескриптор XAML. Документация описывает использование дескрипторов XAML PointerPressed, PointerReleased и PointerExited. Прочтите статью в MSDN, чтобы ознакомиться с примером работы обработчика в соответствующем базовом коде.

Обратите внимание на подпись обработчика событий. В этом примере кода используется PointerRoutedEventArgs. В предыдущем разделе описывался класс RoutedEventArgs. Благодаря разным классам обработки событий разработчики могут различать разные типы событий касания.

 

5. События манипуляции

Сейчас станет немного интереснее. Для событий манипуляции предусмотрено больше дескрипторов XAML (см. раздел «Использование событий манипуляции» по приведенной выше ссылке на MSDN).

Для простоты будем рассматривать событие манипуляции как последовательность следующих этапов:

Этап 1: ManipulationStarted. Инициируется в начале манипуляции, например, когда пользователь удерживает палец на элементе интерфейса.

Этап 2: ManipulationDelta. Манипуляция продолжается, пользователь по-прежнему удерживает нажатым элемент интерфейса, но при этом перетаскивает его. Даже для простых последовательностей «удержал - перетащил - отпустил» это событие может вызываться несколько раз. Вследствие этого нужно внимательно проверить код.

Этап 3: ManipulationInertiaStarting. Возникает, когда пользователь снимает пальцы с экрана, а элементы интерфейса движутся по инерции.

Прочие типы событий манипуляции описаны по ссылке MSDN. Обратите внимание, что манипуляции можно тестировать с помощью мыши и при использовании колесика прокрутки.

Использование дескрипторов XAML см. по приведенной ссылке на MSDN. Интересной особенностью этого API является возможность получения скорости и изменения в расположении элемента интерфейса в течение манипуляции. Это позволяет манипулировать элементами интерфейса с помощью матричных преобразований (описание см. по этой же ссылке).

При обработке событий манипуляции поддерживается и мультисенсорный ввод, но в этой статье он не описывается. Дополнительные сведения см. по приведенной ссылке на MSDN.

 

6. Тестирование перед развертыванием на устройстве: эмуляция в VS

После написания связующего кода для обнаружения касаний пора переходить к тестированию. Переносить пакет приложения на устройство вручную с помощью проводного подключения после каждой сборки кода - непроизводительно. Намного более удобными представляются два следующих метода: использовать средства удаленной работы VS Remote Tools (в их состав входит удаленный отладчик) или просто развернуть код в эмуляторе VS. В этом документе описывается второй способ.

Совет. В меню «Отладка» в VS выберите «Свойства <имя проекта>» в раскрывающемся списке, затем выберите «Отладка» в области слева. Здесь можно выбрать устройство назначения: локальный компьютер (эмуляция в VS) или удаленный компьютер (беспроводная удаленная отладка на физическом устройстве).

В Visual Studio разработчик может добавить в исходный код точки останова. Можно использовать сценарий отладки, чтобы гарантировать, что процедура события касания удовлетворяет двум следующим условиям: вход в процедуру никогда не выполняется при отсутствии событий касания и вход в процедуру всегда выполняется при возникновении любого события касания. Добавьте точку останова в VS в начало связующего кода процедуры:

После успешной сборки проекта выберите «Начать отладку» меню «Отладка» в VS (или нажмите клавишу F5). Приложение запустится. Затем, после входа в процедуру Image_Tapped, в VS будет выделена точка останова, когда выполнение дойдет до данной строки:

 

7. Пример использования: детская математическая игра

В этом разделе мы применяем на практике принципы, описанные в предыдущих разделах: мы создаем детскую математическую игру. Игра устроена следующим образом. Пользователю дается возможность выбрать уровень сложности. На каждом уровне пользователь выбирает операнды (цифры) и операторы (+,-,*,/). Пользователь должен создать арифметическую последовательность из операторов и операндов таким образом, чтобы получилось математически верное уравнение. Если уравнение верно, то плитки исчезают, если нет, то ничего не происходит. Цель игры - убрать с экрана все плитки операндов. При неправильном создании уравнений можно «застрять» на уровне без возможности его завершить. Поэтому для игры требуется заранее продумывать «ходы».

При запуске приложения пользователь видит на экране следующее:

Например, если пользователь выберет уровень 3, будет загружен XAML игрового поля и начнется игра:

На этом игровом поле математически верное уравнение можно составить следующим образом:

(1+2)-2 = 1

Обратите внимание, что игра не использует формальный порядок операции. Уравнения обрабатываются слева направо. Итак, здесь пользователь может выбрать следующие плитки в данной последовательности:

1, + , 2, - , 2, 1

В этой игре не нужен знак равенства. Логика игры отслеживает сумму по мере выбора игроком плиток. На следующем рисунке показан неполный выбор: выбраны все плитки, кроме заключительной. Чтобы выбрать плитку, пользователю достаточно просто коснуться ее.

Выбранные плитки затеняются и выглядят более темными. Теперь, если пользователь выберет «1», получится верное уравнение, и плитки исчезнут.

Существует одна проблема. Игра не может арифметически убрать все оставшиеся плитки операндов. При победе с исходной структурой уровней на экране остаются приведенные выше плитки, с которыми пользователь уже ничего не сможет сделать

 

8. Математическая логика

Логика обработки создания уравнений состоит из двух основных компонентов:

Очередь: выбранные плитки добавляются в очередь, а при необходимости очередь очищается.

Конечный автомат (КА): поддерживает состояние для определения готовности уравнения.

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

//after selecting num in Q0, user must now specify an operator 
                case selection_state.Q1:
                    if (v == value.DIV || v == value.MINUS || v == value.MULT || v == value.PLUS) 
                    {

                        sel_state = selection_state.Q2; //move to next state
                        move_queue.Enqueue(v); //add last user operation to the queue
                        //set update flag for redraw
                        updates_pending[row, col] = true;
                    }

                    //else, user selected a number, so we just stay in Q1
                    else
                    {
                        clearSelection();
                        move_queue.Clear(); //wipe the moves the user has made
                        state[row, col] = SELECTED.YES;
                        sel_state = selection_state.Q1;
                        move_queue.Enqueue(v); //add last user operation to the queue

                        //set update flag for redraw
                        updates_pending[row, col] = true;
                    }
                    break;

В приведенном фрагменте кода Q0 обозначает первое состояние, в котором ожидается самый первый операнд уравнения. В Q1 код ожидает выбора пользователем оператора. Если это так, то выбранный оператор помещается в очередь, а плитка помечается для обновления. Логика обновления представляет собой массив, устанавливающий логическое значение для каждой плитки таким образом, что при повторной прорисовке обновляются только помеченные плитки (по соображениям производительности).

Если же пользователь выбрал операнд вместо оператора, состояние не изменяется. Уравнение обновляется таким образом, чтобы этот операнд стал последним выбранным элементом.

 

9. Использование манипуляции для перемещения элементов интерфейса

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

Фрагменты кода в этом разделе иллюстрируют реализацию жеста «бросания». Имеется в виду жест, когда пользователь удерживает игровую плитку, а затем пальцем «бросает» ее по экрану (мультисенсорный ввод не рассматривается).

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

                //specify the manipulation events to be invoked for each game tile 

                for (x = 0; x < images.GetLength(0); x++)
                {
                    for (y = 0; y < images.GetLength(1); y++)
                    {
                        images[x, y].ManipulationStarted += manip_start;
                        images[x, y].ManipulationDelta += manip_delta;
                        images[x, y].ManipulationInertiaStarting += manip_complete;
                    }
                }

Мы определили специальные обработчики для +=. Например, при касании соответствующего элемента вызывается следующая процедура:

//invoked when user begins a gesture on game tile
        void manip_start(object sender, ManipulationStartedRoutedEventArgs e)
        {
            //store the elapsed time to mark the beginning of this event
            start_ms = game_clock.Elapsed.TotalMilliseconds + (1000 * game_clock.Elapsed.TotalSeconds);

            //grab the image (game tile) where manipulation began!!!
            img = sender as Image;

            //retain image location in canvas at start of gesture
            initial_x = Canvas.GetLeft(img);
            initial_y = Canvas.GetTop(img);
…

Для получения рисунка, которого коснулся пользователь, используется параметр sender. В коде также показано получение времени начала манипуляции (это помогает правильно вычислить скорость, чтобы определить изменение положения рисунка в зависимости от времени при движении в заданном направлении). После этого класс Canvas используется для получения координат «x» и «y» выбранного рисунка в начале манипуляции. Как мы это уже обсуждали в приведенных выше разделах, разработчик может указать в процедуре определенный аргумент событий. В данном случае в коде используется ManipulationStartedRoutedEventArgs.

Основной текст процедуры manip_delta здесь не показан. Когда пользователь убирает пальцы с экрана, следующий фрагмент кода показывает вызываемое событие и вычисление скорости. После этого разработчик может, к примеру, создать событие трения, то есть применить линейное снижение скорости со временем, чтобы постепенно замедлить движущийся «брошенный» объект.

//invoked when user ends a gesture on game tile...use final velocity for projection
        void manip_complete(object sender, ManipulationInertiaStartingRoutedEventArgs e)
        {
            //we will compute the average linear velocity as
            //change in position over time of the manipulation interval
            //note that velocity is a vector, not a scalar, as it
            //has both magnitude and directional components

            finish_ms = game_clock.Elapsed.TotalMilliseconds + (1000 * game_clock.Elapsed.TotalSeconds);

            vel_x = e.Cumulative.Translation.X / (finish_ms - start_ms);
            vel_y = e.Cumulative.Translation.Y / (finish_ms - start_ms);
…

Скорость вычисляется как частное от деления разницы в расположении на время. Разница в расположении определяется относительно начального положения рисунка в момент начала манипуляции. Также можно использовать e.Velocities для прямого доступа к скорости, переданной событию. Помните, что в отличие от скалярной скорости, в данном случае используется векторная скорость, для которой задается величина и направление. Для этого используются обозначения vel_x и vel_y.

 

10. Итоги

В данной статье мы говорили о реализации сенсорного ввода в Metro-приложениях Windows 8. Сначала мы дали базовое определение события касания для элемента пользовательского интерфейса в разметке XAML, а затем рассказали о поддерживающем «базовом» коде C#. В качестве примера мы рассмотрели обнаружение одиночного касания. Также был приведен пример определения и использования интервала двойного касания.

Помимо рассказа о возможностях Metro-приложений, мы продемонстрировали простоту использования среды разработки VS с точки зрения библиотеки API и с точки зрения возможностей отладки исполняемого кода. В новой версии этого программного пакета можно создавать самый удобный и привлекательный пользовательский интерфейс с возможностями сенсорного ввода.

 

Об авторе

Дэвид Медавар (David Medawar) - инженер по программному обеспечению корпорации Intel. Дэвид работает в Intel уже 7 лет, а до этого он занимался разработкой BIOS и системных загрузчиков. В настоящее время Дэвид занимается приложениями для операционных систем Windows 8 и Android.



*Другие наименования и торговые марки могут быть собственностью третьих лиц.

Теги:
Пожалуйста, обратитесь к странице Уведомление об оптимизации для более подробной информации относительно производительности и оптимизации в программных продуктах компании Intel.
Возможность комментирования русскоязычного контента была отключена. Узнать подробнее.