Россия, г. Москва |
Основы работы с сенсорным вводом
События простого касания в Silverlight
Как и XNA, Silverlight поддерживает два разных программных интерфейса для работы с мультисенсорным вводом, которые можно категоризировать как интерфейс обработки простого и интерфейс обработки сложного касания. Интерфейс обработки простого касания построен на событии Touch.FrameReported, которое очень похоже на XNA-класс TouchPanel. Отличается оно лишь тем, что это событие, и оно не включает обработку жестов.
Интерфейс обработки сложного касания включает три события, определяемые классом UIElement: ManipulationStarted (Обработка началась), ManipulationDelta (Приращение в ходе обработки) и ManipulationCompleted (Обработка завершилась). События Manipulation, как их обобщенно называют, консолидируют взаимодействие множества касаний в движение и коэффициенты масштабирования.
Ядром интерфейса обработки простого касания в Silverlight является класс TouchPoint (Точка касания), экземпляр которого представляет отдельное касание экрана. TouchPoint имеет четыре свойства только для чтения:
- Action (Действие) типа TouchAction (Действие касания) – перечисление с элементами Down (Вниз), Move (Перемещение) и Up (Вверх).
- Position типа Point (Точка), значение которого определяется относительно верхнего левого угла конкретного элемента. Будем называть этот элемент опорным.
- Size типа Size. Это свойство должно представлять область касания (и, следовательно, давление, создаваемое пальцем, в некотором роде), но эмулятор Windows Phone 7 не возвращает полезных значений.
- TouchDevice типа TouchDevice.
Объект TouchDevice имеет два свойства только для чтения:
- Id типа int, используется для идентификации касаний. Каждое отдельное касание ассоциировано с уникальным Id на протяжении всех событий, начиная от Down и до Up.
- DirectlyOver (Непосредственно над) типа UIElement – самый верхний элемент, расположенный прямо под пальцем.
Как видите, Silverlight-объекты TouchPoint и TouchDevice предоставляют преимущественно те же сведения, что и XNA-объект TouchLocation. Свойство DirectlyOver объекта TouchDevice часто очень полезно для определения, какого элемента пользователь касается.
Для использования интерфейса обработки простого касания необходимо установить обработчик статического события Touch.FrameReported:
Touch.FrameReported += OnTouchFrameReported; Метод OnTouchFrameReported выглядит следующим образом: void OnTouchFrameReported(object sender, TouchFrameEventArgs args) { … }
Этот обработчик события принимает все события касания в ходе выполнения приложения. Объект TouchFrameEventArgs (Аргументы события касания рамки) имеет свойство TimeStamp (Отметка времени) типа int и три метода:
- GetTouchPoints(refElement) (Получить точки касания) возвращает TouchPointCollection (Коллекция точек касания)
- GetPrimaryTouchPoint(refElement) (Получить основную точку касания) возвращает один TouchPoint
- SuspendMousePromotionUntilTouchUp() (Приостановить перемещения мыши до завершения касания)
В общем случае вызывается метод GetTouchPoints и в него передается опорный элемент. Значения свойств Position объектов TouchPoint в возвращенной коллекции определяются относительно этого элемента. Если передать null в GetTouchPoints, значения свойств Position будут установлены относительно верхнего левого угла окна просмотра приложения.
Между опорным элементом и элементом DirectlyOver нет никакой связи. Событие всегда обрабатывает все касания приложения в целом. Вызов GetTouchPoints или GetPrimaryTouchPoint для конкретного элемента не означает, что будут обрабатываться касания только этого элемента, это означает лишь то, что значение свойства Position будет определяться относительно этого элемента. (Поэтому координаты Position вполне могут быть отрицательными, если место касания находится слева или над опорным элементом.)
Элемент DirectlyOver определяет элемент, находящийся непосредственно под пальцем.
Для разговора о втором и третьем методах необходимо сделать небольшое вступление. Событие Touch.FrameReported появилось в Silverlight для настольных приложений, в которых для логики обработки событий мыши существующих элементов управления удобно автоматически использовать сенсорный ввод. По этой причине события касания приравнены к событиям мыши.
Но это распространяется только на "первую" точку касания, т.е. на действия пальца, коснувшегося экрана первым, когда ни один другой палец его не касается. Если вы не хотите, чтобы действия этого касания трактовались как события мыши, обработчик события должен начинаться так:
void OnTouchFrameReported(object sender, TouchFrameEventArgs args) { TouchPoint primaryTouchPoint = args.GetPrimaryTouchPoint(null); if (primaryTouchPoint != null && primaryTouchPoint.Action == TouchAction.Down) { args.SuspendMousePromotionUntilTouchUp(); } … }
Метод SuspendMousePromotionUntilTouchUp может вызываться только в момент первого касания первым пальцем, когда все остальные пальцы еще не коснулись экрана.
Для Windows Phone 7 такая логика представляет некоторые проблемы. Как сказано выше, по сути, происходит отключение обработки событий мыши для всего приложения. Если приложение для телефона включает элементы управления Silverlight, изначально написанные для ввода с помощью мыши и не обновленные для приема сенсорного ввода, эти элементы управления фактически будут деактивированы.
Конечно, можно проверять свойство DirectlyOver и делать селективную приостановку обработки событий мыши. Но в телефоне не должно быть элементов, обрабатывающих ввод с помощью мыши, кроме тех которые не обрабатывают сенсорный ввод! Поэтому, вероятно, больше смысла будет в том, чтобы никогда не приостанавливать обработку событий мыши.
Для приложения, которое мы напишем, важна только первоначальная точка касания в момент, когда ее TouchAction имеет значение Down, поэтому мы используем ту же самую логику.
В проекте SilverlightTouchHello TextBlock описывается в XAML-файле:
Проект Silverlight: SilverlightTouchHello Файл: MainPage.xaml (фрагмент)
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0"> <TextBlock Name="txtblk" Text="Hello, Windows Phone 7!" Padding="0 34" HorizontalAlignment="Center" VerticalAlignment="Center" /> </Grid>
Обратите внимание на значение Padding. Свойство FontSize отображаемого здесь текста равно 20 пикселям, это обеспечивает TextBlock высоту, равную около 27 пикселей. Также нам известно о рекомендации, что мишень касания не должна быть меньше 9 миллиметров. Если разрешение экрана телефона равно 264 DPI, 9 миллиметров – это 94 пикселей. (9 миллиметров разделить на 25,4 миллиметра/дюйм и умножить на 264 пикселей/дюйм.) TextBlock не хватает 67 пикселей. Поэтому я задаю значение Padding, которое добавляет по 34 пикселя сверху и снизу (но не по бокам).
Мы применили здесь Padding, а не Margin, потому что Padding – это область внутри TextBlock. Таким образом, TextBlock фактически становится больше, чем предполагает размер текста. Margin – это область вне TextBlock. Она не является частью TextBlock и касания ее не учитываются как касания TextBlock.
Рассмотрим файл выделенного кода полностью. Конструктор MainPage определяет обработчик событий Touch.FrameReported.
Проект Silverlight: SilverlightTouchHello Файл: MainPage.xaml.cs
using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Shapes; using Microsoft.Phone.Controls; namespace SilverlightTouchHello { public partial class MainPage : PhoneApplicationPage { Random rand = new Random(); Brush originalBrush; public MainPage() { InitializeComponent(); originalBrush = txtblk.Foreground; Touch.FrameReported += OnTouchFrameReported; } void OnTouchFrameReported(object sender, TouchFrameEventArgs args) { TouchPoint primaryTouchPoint = args.GetPrimaryTouchPoint(null); if (primaryTouchPoint != null && primaryTouchPoint.Action == TouchAction.Down) { if (primaryTouchPoint.TouchDevice.DirectlyOver == txtblk) { txtblk.Foreground = new SolidColorBrush(Color.FromArgb(255, (byte)rand.Next(256), (byte)rand.Next(256), (byte)rand.Next(256))); } else { txtblk.Foreground = originalBrush; } } } } }
Этот обработчик событий обрабатывает только первые точки касания, для которых Action имеет значение Down. Если свойство DirectlyOver возвращает элемент txtblk, создается случайный цвет. В отличие от XNA структура Color в Silverlight не имеет конструктора для задания цвета как комбинации значений красного, зеленого и синего, но в ней есть статический метод FromArgb, который создает объект Color на основании значений альфа, красного, зеленого и синего каналов, где альфа – это прозрачность. Для получения непрозрачного цвета, задайте альфа-каналу значение 255. Это очевидно не во всех файлах XAML, но свойство Foreground типа Brush (Кисть) – это абстрактный класс, от которого наследуется SolidColorBrush (Одноцветная кисть).
Если DirectlyOver не txtblk, цвет текста не меняется на белый. В случае если бы пользователь выбрал цветовую тему с черным текстом на белом фоне, это привело бы к тому, что текст исчез бы с экрана. Вместо этого свойству Foreground присваивается изначально заданный для TextBlock цвет. Он определяется в конструкторе.
Запустим программу и посмотрим её в действии:
Ключевые термины
TouchCollection (Коллекция касаний) – это коллекция в XNA, включающая нуль или более объектов TouchLocation
TouchPoint (Точка касания), – класс для обработки простого касания в Silverlight, экземпляр которого представляет отдельное касание экрана.
Краткие итоги
В данной лекции мы:
- научились обрабатывать простое касание в XNA;
- написали программу по смене цвета по касанию;
- разобрали обработку жестов в XNA и написали соответствующую программу;
- обработали событие простого касания в Silverlight.
Набор для практики
Упражнения
Добавьте во все проекты вторую строку, измените все программы так, чтобы при нажатии на одну из строк, менялся цвет другой.