Gesty w Windows Phone 7  

Udostępnij na: Facebook

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.