Gesty w Windows Phone 7
Pobierz i uruchom |
Autor: Marcin Kruszyński
Opublikowano: 2011-03-18
W Windows Phone 7 dotyk jest podstawowym sposobem na interakcję użytkownika z systemem. Przesuwając palcem lub palcami po ekranie dotykowym, wykonujemy gesty, co stanowi naturalny i intuicyjny sposób komunikacji.
Po przeczytaniu tego artykułu będziesz:
- wiedział, jakie gesty są wspierane w Windows Phone 7,
- znał sposoby ich obsługi z poziomu Silverlight,
- wiedział, jak jest wspierany dotyk na emulatorze.
Wprowadzenie
Telefony z Windows Phone 7 posiadają nowoczesne ekrany dotykowe multi-touch, które pozwalają na obsługę aplikacji przy jednoczesnym użyciu czterech lub więcej palców.
Wspierane kontrolki na WP7 mają wbudowaną obsługę gestów. Możemy również sami bezpośrednio przetwarzać dotyk i gesty za pomocą odpowiedniego API w Silverlight i XNA.
Wspierane gesty są wykonywane przy użyciu jednego lub dwóch palców. Niskopoziomowe API pozwala nam śledzić więcej punktów dotykowych, co może być przydatne np. w grach lub aplikacjach z instrumentami muzycznymi.
Wspierane gesty
Na rysunku 1. przedstawiono gesty wspierane w Windows Phone 7:
Rys.1. Gesty w Windows Phone 7.
Gesty możemy podzielić na te wykonywane z użyciem jednego palca i gesty multi-touch.
Do pierwszej grupy zaliczamy gesty:
- Tap – pojedyncze, krótkotrwałe dotknięcie ekranu zakończone podniesieniem palca (np. przy wyborze elementu lub opcji, używaniu przycisków na ekranie).
- Double tap – dwa szybkie dotknięcia ekranu (typowo używa się go do przełączania między normalnym i powiększonym widokiem).
- Touch and hold – dotknięcie ekranu i przytrzymanie przez określoną ilość czasu (z reguły używany do wyświetlania menu kontekstowego lub strony z opcjami dla danego elementu).
- Pan – umieszczenie palca na ekranie i przesuwanie nim w dowolnych kierunkach, podniesienie palca kończy gest (służy do przesuwania elementów lub zmiany ich kolejności).
- Flick - szybkie przesunięcie palcem po ekranie w dowolnym kierunku, podniesienie palca kończy gest (służy do przemieszczania zawartości, może następować po geście pan).
Pinch and stretch jest jedynym wspieranym gestem multi-touch. Polega on na umieszczeniu w różnych punktach ekranu dwóch palców i zbliżaniu ich do siebie (pinch) lub oddalaniu (stretch). Gest umożliwia płynne skalowanie zawartości względem środka między dwoma palcami.
Sposoby obsługi
Dotyk i gesty możemy obsługiwać w Silverlight na Windows Phone 7 na kilka różnych sposobów.
Proste gesty, tj. tap, double-tap i tap-and-hold, możemy przechwytywać za pomocą zdarzeń myszy:
- Tap – obsługujemy zdarzenie MouseLeftButtonUp.
- Double tap – sprawdzamy, czy w danym okresie czasu wystąpiły po dwa zdarzenia MouseLeftButtonDown i MouseLeftButtonUp. Pomiędzy dwoma zdarzeniami MouseLeftButtonDown nie może wystąpić wystąpić zdarzenie MouseMove.
- Touch and hold – po zajściu zdarzenia MouseLeftButtonDown, uruchamiamy timer na określony czas i zapamiętujemy miejsce dotyku. Za pomocą zdarzenia MouseMove sprawdzamy, czy użytkownik nie przesunął palca o więcej niż zadaną małą liczbę pikseli. Jeśli tak się zdarzy, anulujemy timer. Postępujemy też tak w sytuacji, kiedy przed zadanym czasem wystąpi zdarzenie MouseLeftButtonUp. Interesujący nas gest nastąpi, gdy upłynie czas określony przez timer i nie zostanie on anulowany.
Dotyk możemy obsługiwać bezpośrednio z użyciem niskopoziomowego lub wysokopoziomowego API.
Niskopoziomowa obsługa bazuje na statycznym zdarzeniu Touch.FrameReported, które otrzymuje informacje z całej aplikacji. Możemy m.in dowiedzieć się o położeniu każdego punktu dotykowego i jego stanie (czy palec jest opuszczony, przesuwany lub podniesiony). Nie mamy tutaj gotowego rozpoznawania gestów.
W Silverlight możemy skorzystać z trzech bardziej wysokopoziomowych mechanizmów:
- Zdarzeń manipulacji.
- GestureListener z Silverlight for Windows Phone Toolkit.
- TouchPanel z XNA.
Omówimy je kolejno w dalszych częściach artykułu.
Zdarzenia manipulacji
W skład zdarzeń manipulacji wchodzą trzy routowane zdarzenia z klasy UIElement:
- ManipulationStarted – zachodzi wtedy, gdy użytkownik rozpoczyna gest, umieszczając palec lub palce na ekranie,
- ManipulationDelta – zachodzi wielokrotnie, za każdym razem, kiedy użytkownik przesunie palec lub palce po ekranie,
- ManipulationCompleted – zachodzi wówczas, gdy użytkownik zabiera swój palec lub palce z ekranu.
Proste gesty jednym palcem są odpowiednikami zdarzeń MouseDown, MouseMove i MouseUp.
Zdarzenia manipulacji oferują jednak znacznie więcej:
- dostarczają wartości translacji,
- potrafią przechwytywać proste gesty z dwoma palcami, tj. pinch and stretch, i na ich podstawie podawać współczynniki skalowania,
- dostarczają informacji przydatnych do implementacji inercji np. prędkości.
Zdarzenia manipulacji nie wykrywają konkretnych gestów. Aby tego dokonać, musimy sami przekształcać otrzymywane wartości. Zdarzenia manipulacji najbardziej pasują do zadań, w których obiekty są przesuwane i skalowane. Do rozróżnienia zdarzeń, tj. tap, double tap i hold, wymagany jest dodatkowy kod.
Używanie zdarzeń manipulacji omówimy na praktycznym przykładzie. Zaimplementujemy przeciąganie i skalowanie prostokąta:
<Rectangle Name="rect" Width="250" Height="200" Fill="Yellow"
ManipulationStarted="OnManipulationStarted" ManipulationDelta="OnManipulationDelta" ManipulationCompleted="OnManipulationCompleted">
<Rectangle.RenderTransform>
<CompositeTransform x:Name="rectTransform" />
</Rectangle.RenderTransform>
</Rectangle>
W handlerze zdarzenia ManipulationStarted dokonujemy czynności związanych z rozpoczęciem manipulacji (np. zapamiętujemy jej początkowe parametry, zmieniamy kolor obiektu, uruchamiamy timer – w przypadku wykrywania gestów double tap i hold).
void OnManipulationStarted(object sender, ManipulationStartedEventArgs e)
{
rect.Fill = new SolidColorBrush(Colors.Green);
}
Wartości translacji w argumencie zdarzenia ManipulationDelta używane są typowo do zmiany położenia obiektu, a wartości skali do zmiany jego rozmiarów. Gdy posługujemy się jednym palcem, jedynie wartości translacji będą poprawne. Skala może mieć wartość zero, być ujemna i różnić się w kierunkach X i Y. W poniższym kodzie zabezpieczamy się przed niepożądanymi wartościami i stosujemy tę samą skalę w obu kierunkach.
void OnManipulationDelta(object sender, ManipulationDeltaEventArgs e)
{
if (e.DeltaManipulation.Scale.X > 0 || e.DeltaManipulation.Scale.Y > 0)
{
double maxScale = Math.Max(e.DeltaManipulation.Scale.X,
e.DeltaManipulation.Scale.Y);
rectTransform.ScaleX *= maxScale;
rectTransform.ScaleY *= maxScale;
}
rectTransform.TranslateX += e.DeltaManipulation.Translation.X;
rectTransform.TranslateY += e.DeltaManipulation.Translation.Y;
}
W handlerze zdarzenia ManipulationCompleted podejmujemy czynności związane z zakończeniem manipulacji (np. przywracamy kolor obiektu, przetwarzamy dane dotyczące całej manipulacji):
void OnManipulationCompleted(object sender, ManipulationCompletedEventArgs e)
{
rect.Fill = new SolidColorBrush(Colors.Yellow);
}
GestureListener
Jeśli chcemy wykrywać w prosty sposób konkretne gesty, powinniśmy skorzystać z komponentu GestureListener z Silverlight for Windows Phone Toolkit (dołączamy referencję do Microsoft. Phone.Controls.Toolkit.dll). Mamy do dyspozycji następujące zdarzenia: Tap, DoubleTap, Hold, DragStarted, DragDelta, DragCompleted, Flick, PinchStarted, PinchDelta, PinchCompleted. Pozwalają one uzyskać informacje na temat położenia, translacji, skalowania, rotacji, prędkości. W przypadku niektórych gestów kontrolka może otrzymywać kombinację zdarzeń. Na przykład mogą występować zdarzenia Drag podczas trwania gestu Flick i przy jego zakończeniu.
Używanie GestureListener pokażemy w praktyce. Weźmy prostokąt z poprzedniego przykładu i przypnijmy do niego omawiany komponent w następujący sposób:
<Rectangle Name="rect" Width="250" Height="200" Fill="Yellow"
RenderTransformOrigin="0.5,0.5">
<Rectangle.RenderTransform>
<CompositeTransform x:Name="rectTransform" />
</Rectangle.RenderTransform>
<toolkit:GestureService.GestureListener>
<toolkit:GestureListener DragStarted="OnDragStarted"
DragDelta="OnDragDelta" DragCompleted="OnDragCompleted"
PinchStarted="OnPinchStarted" PinchDelta="OnPinchDelta"
PinchCompleted="OnPinchCompleted"/>
</toolkit:GestureService.GestureListener>
</Rectangle>
Obsługujemy osobno gesty odpowiedzialne za przeciąganie (drag) i skalowanie (pinch).
Przy przeciąganiu prostokąt zmienia swój kolor na zielony:
private void OnDragStarted(object sender, DragStartedGestureEventArgs e)
{
rect.Fill = new SolidColorBrush(Colors.Green);
}
private void OnDragDelta(object sender, DragDeltaGestureEventArgs e)
{
rectTransform.TranslateX += e.HorizontalChange;
rectTransform.TranslateY += e.VerticalChange;
}
private void OnDragCompleted(object sender, DragCompletedGestureEventArgs e)
{
rect.Fill = new SolidColorBrush(Colors.Yellow);
}
Gdy wykonujemy gest pinch and stretch, prostokąt staje się czerwony. W handlerze zdarzenia PinchStarted zapamiętujemy początkowe wartości skali i kąta obrotu:
double initialAngle;
double initialScale;
private void OnPinchStarted(object sender, PinchStartedGestureEventArgs e)
{
initialAngle = rectTransform.Rotation;
initialScale = rectTransform.ScaleX;
rect.Fill = new SolidColorBrush(Colors.Red);
}
W zdarzeniu PichDelta oprócz skalowania obiektu dokonujemy także jego obrotu:
private void OnPinchDelta(object sender, PinchGestureEventArgs e)
{
rectTransform.Rotation = initialAngle + e.TotalAngleDelta;
rectTransform.ScaleX = rectTransform.ScaleY = initialScale * e.DistanceRatio;
}
private void OnPinchCompleted(object sender, PinchGestureEventArgs e)
{
rect.Fill = new SolidColorBrush(Colors.Yellow);
}
TouchPanel
Z poziomu Silverlight możemy również skorzystać z klasy TouchPanel z XNA. Zapewnia ona zaawansowaną obsługę wszystkich gestów oraz niskopoziomową obsługę dotyku (m.in. kolekcję punktów dotykowych z ich położeniem i stanem). W wielu przypadkach nie musimy korzystać z rozpoznawania gestów z XNA, ponieważ prostszy w użyciu komponent GestureListener opakowuje właśnie ten mechanizm.
Przy stosowaniu TouchPanel w Silverlight wykonujemy następujące czynności:
1. Dodajemy do projektu referencje do assemblies Microsoft.Xna.Framework.dll i Microsoft.Xna. Framework.Input.Touch.dll.
2. Określamy rodzaje gestów, jakie chcemy wykrywać:
TouchPanel.EnabledGestures = GestureType.Tap | GestureType.DoubleTap |
GestureType.Hold | GestureType.FreeDrag | GestureType.DragComplete |
GestureType.Flick | GestureType.Pinch | GestureType.PinchComplete;
W przedstawionym kodzie wykrywamy wszystkie rodzaje gestów. Ze względu na wydajność najlepiej podać tylko te, które będą używane w danej aplikacji.
3. Implementujemy kod, w którym:
- Sprawdzamy za pomocą propercji TouchPanel.IsGestureAvailable, czy są jakieś gesty do przetworzenia,
- Odczytujemy je pojedynczo za pomocą metody TouchPanel.ReadGesture(),
- W zależności od ich rodzaju dokonujemy przetwarzania odpowiednich danych z klasy GestureSample.
while (TouchPanel.IsGestureAvailable)
{
GestureSample sample = TouchPanel.ReadGesture();
var samplePosition = sample.Position;
var samplePosition2 = sample.Position2;
var sampleDelta = sample.Delta;
var sampleDelta2 = sample.Delta2;
switch (sample.GestureType)
{
case GestureType.Tap:
//...
break;
//...
case GestureType.PinchComplete:
//...
break;
}
}
Gesty mogą się składać z wielu pojedynczych składowych, np. przy double tap wystąpią gesty Tap i DoubleTap. Powinniśmy sprawdzać sekwencję gestów, aby móc podjąć odpowiednie działanie.
4. Kod z punktu 3 wywołujemy okresowo, stosując DispatcherTimer. Nie musi być on uruchomiony przez cały czas. W implementacji GestureListener jest uruchamiany przy wykryciu dotyku i zatrzymywany przy jego ustaniu. Odczyt i przetwarzanie gestów odbywa się dodatkowo przy wykryciu dotyku, przesuwaniu palcami po ekranie i przy zakończeniu dotyku.
Kod z punktu 3 wykrywa gesty z całej aplikacji. Aby reagować tylko na gesty z danego elementu, należy je odfiltrować na podstawie informacji o położeniu. Niekiedy zawężenia możemy dokonać za pomocą zdarzeń manipulacji. Przykładowo dla gestu flick kod, który go odczytuje i przetwarza, możemy wywołać w zdarzeniu ManipulationCompleted danego elementu.
Więcej informacji na temat przetwarzania gestów z użyciem klasyTouchPanel możesz znaleźć m.in. w dokumentacji na stronie Detecting Gestures on a Multitouch Screen (Windows Phone) oraz w przewodniku Windows Phone 7 Developer Guide.
Emulator
Za pomocą emulatora i myszy możemy łatwo przetestować gesty wymagające użycia jednego palca. Aktywność myszy jest wykrywana i zamieniana na zdarzenia dotyku. Emulator nie wspiera sam w sobie obsługi multi-touch za pomocą myszy, dlatego gesty wymagające więcej niż jednego palca najlepiej testować na komputerze z ekranem dotykowym lub bezpośrednio na telefonie. Jeśli nie dysponujesz odpowiednim ekranem czy urządzeniem, możesz spróbować zainstalować sterownik multi-touch na komputerze z systemem Windows 7, a następnie podpiąć do niego dwie myszy.
Podsumowanie
W tym artykule poznaliśmy gesty wspierane w Windows Phone 7. Potrafimy z nich skorzystać w Silverlight na kilka różnych sposobów. Wiemy, w jakim zakresie dotyk jest wspierany przez emulator.