Przewodnik — używanie dotyku w systemie Android
Zobaczmy, jak używać pojęć z poprzedniej sekcji w działającej aplikacji. Utworzymy aplikację z czterema działaniami. Pierwszym działaniem będzie menu lub tablica przełączania, która uruchomi inne działania w celu zademonstrowania różnych interfejsów API. Poniższy zrzut ekranu przedstawia główne działanie:
Pierwsze działanie, Touch Sample, pokaże, jak używać procedur obsługi zdarzeń do dotykania widoków. Działanie rozpoznawania gestów pokaże, jak podklasy Android.View.Views
i obsługiwać zdarzenia, a także pokazuje, jak obsługiwać gesty szczypta. Trzecie i końcowe działanie, Niestandardowe gesty, pokaże, jak używać gestów niestandardowych. Aby ułatwić śledzenie i absorbowanie rzeczy, podzielimy ten przewodnik na sekcje, a każda sekcja koncentruje się na jednej z działań.
Przykładowe działanie touch
Otwórz TouchWalkthrough_Start projektu. Funkcja MainActivity jest ustawiona na przejście — do nas należy zaimplementowanie zachowania dotykowego w działaniu. Jeśli uruchomisz aplikację i klikniesz pozycję Touch Sample, następujące działanie powinno zostać uruchomione:
Teraz, gdy potwierdziliśmy, że działanie zostanie uruchomione, otwórz plik TouchActivity.cs i dodaj procedurę obsługi dla
Touch
zdarzenia :ImageView
_touchMeImageView.Touch += TouchMeImageViewOnTouch;
Następnie dodaj następującą metodę, aby TouchActivity.cs:
private void TouchMeImageViewOnTouch(object sender, View.TouchEventArgs touchEventArgs) { string message; switch (touchEventArgs.Event.Action & MotionEventActions.Mask) { case MotionEventActions.Down: case MotionEventActions.Move: message = "Touch Begins"; break; case MotionEventActions.Up: message = "Touch Ends"; break; default: message = string.Empty; break; } _touchInfoTextView.Text = message; }
Zwróć uwagę, że w powyższym kodzie traktujemy Move
akcję i Down
jako tę samą. Jest to spowodowane tym, że mimo że użytkownik może nie podnieść palca z ImageView
, może poruszać się lub nacisk wywierany przez użytkownika może ulec zmianie. Te typy zmian wygenerują Move
akcję.
Za każdym razem, gdy użytkownik dotyka elementu , Touch
zdarzenie zostanie podniesioneImageView
, a nasza procedura obsługi wyświetli komunikat Touch Rozpoczyna się na ekranie, jak pokazano na poniższym zrzucie ekranu:
Tak długo, jak użytkownik dotyka ImageView
elementu , funkcja Touch Begins będzie wyświetlana w elemecie TextView
. Gdy użytkownik nie dotyka ImageView
już elementu , w pliku zostanie wyświetlony TextView
komunikat Touch Ends, jak pokazano na poniższym zrzucie ekranu:
Działanie rozpoznawania gestów
Teraz zaimplementuj działanie rozpoznawania gestów. To działanie pokaże, jak przeciągnąć widok wokół ekranu i zilustrować jeden sposób implementowania szczypta do powiększenia.
Dodaj nowe działanie do aplikacji o nazwie
GestureRecognizer
. Zmodyfikuj kod dla tego działania, tak aby był podobny do następującego kodu:public class GestureRecognizerActivity : Activity { protected override void OnCreate(Bundle bundle) { base.OnCreate(bundle); View v = new GestureRecognizerView(this); SetContentView(v); } }
Dodaj nowy widok systemu Android do projektu i nadaj mu
GestureRecognizerView
nazwę . Dodaj następujące zmienne do tej klasy:private static readonly int InvalidPointerId = -1; private readonly Drawable _icon; private readonly ScaleGestureDetector _scaleDetector; private int _activePointerId = InvalidPointerId; private float _lastTouchX; private float _lastTouchY; private float _posX; private float _posY; private float _scaleFactor = 1.0f;
Dodaj następujący konstruktor do
GestureRecognizerView
. Ten konstruktor doda elementImageView
do naszego działania. W tym momencie kod nadal nie zostanie skompilowany — musimy utworzyć klasęMyScaleListener
, która ułatwi zmianę rozmiaruImageView
, gdy użytkownik go utworzy:public GestureRecognizerView(Context context): base(context, null, 0) { _icon = context.Resources.GetDrawable(Resource.Drawable.Icon); _icon.SetBounds(0, 0, _icon.IntrinsicWidth, _icon.IntrinsicHeight); _scaleDetector = new ScaleGestureDetector(context, new MyScaleListener(this)); }
Aby narysować obraz naszego działania, musimy zastąpić
OnDraw
metodę klasy View, jak pokazano w poniższym fragmencie kodu. Ten kod przeniesie elementImageView
do pozycji określonej przez_posX
element i_posY
, a także zmieni rozmiar obrazu zgodnie ze współczynnikiem skalowania:protected override void OnDraw(Canvas canvas) { base.OnDraw(canvas); canvas.Save(); canvas.Translate(_posX, _posY); canvas.Scale(_scaleFactor, _scaleFactor); _icon.Draw(canvas); canvas.Restore(); }
Następnie musimy zaktualizować zmienną
_scaleFactor
wystąpienia, ponieważ użytkownik p wywłaszczaImageView
element . Dodamy klasę o nazwieMyScaleListener
. Ta klasa będzie nasłuchiwać zdarzeń skalowania, które będą zgłaszane przez system Android, gdy użytkownik ściągnieImageView
element . Dodaj następującą klasę wewnętrzną doGestureRecognizerView
klasy . Ta klasa toScaleGesture.SimpleOnScaleGestureListener
. Ta klasa to klasa wygody, którą odbiorniki mogą podklasować, gdy interesuje Cię podzbiór gestów:private class MyScaleListener : ScaleGestureDetector.SimpleOnScaleGestureListener { private readonly GestureRecognizerView _view; public MyScaleListener(GestureRecognizerView view) { _view = view; } public override bool OnScale(ScaleGestureDetector detector) { _view._scaleFactor *= detector.ScaleFactor; // put a limit on how small or big the image can get. if (_view._scaleFactor > 5.0f) { _view._scaleFactor = 5.0f; } if (_view._scaleFactor < 0.1f) { _view._scaleFactor = 0.1f; } _view.Invalidate(); return true; } }
Następną metodą, w
GestureRecognizerView
ramach których musimy przesłonić, jestOnTouchEvent
. Poniższy kod zawiera pełną implementację tej metody. Istnieje tu wiele kodu, więc pośmińmy minutę i przyjrzyjmy się temu, co się tutaj dzieje. Pierwszą rzeczą, jaką wykonuje ta metoda, jest skalowanie ikony w razie potrzeby — jest to obsługiwane przez wywołanie metody_scaleDetector.OnTouchEvent
. Następnie spróbujemy ustalić, jaka akcja nosi nazwę tej metody:Jeśli użytkownik dotknął ekranu, rejestrujemy pozycje X i Y oraz identyfikator pierwszego wskaźnika, który dotknął ekranu.
Jeśli użytkownik przeniósł swój dotyk na ekranie, dowiesz się, jak daleko użytkownik przeniósł wskaźnik.
Jeśli użytkownik zniesie palec z ekranu, zatrzymamy śledzenie gestów.
public override bool OnTouchEvent(MotionEvent ev) { _scaleDetector.OnTouchEvent(ev); MotionEventActions action = ev.Action & MotionEventActions.Mask; int pointerIndex; switch (action) { case MotionEventActions.Down: _lastTouchX = ev.GetX(); _lastTouchY = ev.GetY(); _activePointerId = ev.GetPointerId(0); break; case MotionEventActions.Move: pointerIndex = ev.FindPointerIndex(_activePointerId); float x = ev.GetX(pointerIndex); float y = ev.GetY(pointerIndex); if (!_scaleDetector.IsInProgress) { // Only move the ScaleGestureDetector isn't already processing a gesture. float deltaX = x - _lastTouchX; float deltaY = y - _lastTouchY; _posX += deltaX; _posY += deltaY; Invalidate(); } _lastTouchX = x; _lastTouchY = y; break; case MotionEventActions.Up: case MotionEventActions.Cancel: // We no longer need to keep track of the active pointer. _activePointerId = InvalidPointerId; break; case MotionEventActions.PointerUp: // check to make sure that the pointer that went up is for the gesture we're tracking. pointerIndex = (int) (ev.Action & MotionEventActions.PointerIndexMask) >> (int) MotionEventActions.PointerIndexShift; int pointerId = ev.GetPointerId(pointerIndex); if (pointerId == _activePointerId) { // This was our active pointer going up. Choose a new // action pointer and adjust accordingly int newPointerIndex = pointerIndex == 0 ? 1 : 0; _lastTouchX = ev.GetX(newPointerIndex); _lastTouchY = ev.GetY(newPointerIndex); _activePointerId = ev.GetPointerId(newPointerIndex); } break; } return true; }
Teraz uruchom aplikację i uruchom działanie rozpoznawania gestów. Po uruchomieniu ekranu powinien wyglądać podobnie do poniższego zrzutu ekranu:
Teraz dotknij ikony i przeciągnij ją wokół ekranu. Spróbuj wykonać gest uszczypnięcia do powiększenia. W pewnym momencie ekran może wyglądać podobnie do poniższego zrzutu ekranu:
W tym momencie powinieneś dać sobie pat na plecach: właśnie zaimplementowano szczypta do powiększenia aplikacji systemu Android! Wykonaj szybką przerwę i przejdź do trzeciego i ostatniego działania w tym przewodniku — za pomocą gestów niestandardowych.
Niestandardowe działanie gestu
Ostatni ekran w tym przewodniku będzie używać gestów niestandardowych.
Na potrzeby tego przewodnika biblioteka gestów została już utworzona przy użyciu narzędzia gestów i dodana do projektu w pliku Resources/raw/gestures. Dzięki temu trochę utrzymania domu z drogi, możemy przejść z ostateczną aktywnością w przewodniku.
Dodaj plik układu o nazwie custom_gesture_layout.axml do projektu z następującą zawartością. Projekt zawiera już wszystkie obrazy w folderze Resources :
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" /> <ImageView android:src="@drawable/check_me" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="3" android:id="@+id/imageView1" android:layout_gravity="center_vertical" /> <LinearLayout android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" /> </LinearLayout>
Następnie dodaj nowe działanie do projektu i nadaj mu
CustomGestureRecognizerActivity.cs
nazwę . Dodaj dwie zmienne wystąpienia do klasy, jak pokazano w następujących dwóch wierszach kodu:private GestureLibrary _gestureLibrary; private ImageView _imageView;
Zmodyfikuj metodę
OnCreate
tego działania, tak aby przypominał następujący kod. Pośmińmy chwilę, aby wyjaśnić, co się dzieje w tym kodzie. Pierwszą rzeczą, którą robimy, jest utworzenie wystąpieniaGestureOverlayView
elementu i ustawienie, które jest głównym widokiem działania. Przypisujemy również program obsługi zdarzeń doGesturePerformed
zdarzeniaGestureOverlayView
. Następnie zawyżamy utworzony wcześniej plik układu i dodamy go jako widok podrzędny obiektuGestureOverlayView
. Ostatnim krokiem jest zainicjowanie zmiennej_gestureLibrary
i załadowanie pliku gestów z zasobów aplikacji. Jeśli z jakiegoś powodu nie można załadować pliku gestów, nie można wykonać zbyt wielu czynności, więc jest ono zamykane:protected override void OnCreate(Bundle bundle) { base.OnCreate(bundle); GestureOverlayView gestureOverlayView = new GestureOverlayView(this); SetContentView(gestureOverlayView); gestureOverlayView.GesturePerformed += GestureOverlayViewOnGesturePerformed; View view = LayoutInflater.Inflate(Resource.Layout.custom_gesture_layout, null); _imageView = view.FindViewById<ImageView>(Resource.Id.imageView1); gestureOverlayView.AddView(view); _gestureLibrary = GestureLibraries.FromRawResource(this, Resource.Raw.gestures); if (!_gestureLibrary.Load()) { Log.Wtf(GetType().FullName, "There was a problem loading the gesture library."); Finish(); } }
Ostatnia rzecz, którą musimy wykonać, aby zaimplementować metodę
GestureOverlayViewOnGesturePerformed
, jak pokazano w poniższym fragmencie kodu. Po wykryciu gestuGestureOverlayView
wywołuje on z powrotem do tej metody. PierwsząIList<Prediction>
rzeczą, którą staramy się uzyskać obiekty pasujące do gestu przez wywołanie metody_gestureLibrary.Recognize()
. Używamy trochę LINQ, aby uzyskaćPrediction
najwyższy wynik dla gestu.Jeśli nie było pasującego gestu z wystarczająco wysokim wynikiem, program obsługi zdarzeń kończy działanie bez wykonywania niczego. W przeciwnym razie sprawdzamy nazwę przewidywania i zmieniamy wyświetlany obraz na podstawie nazwy gestu:
private void GestureOverlayViewOnGesturePerformed(object sender, GestureOverlayView.GesturePerformedEventArgs gesturePerformedEventArgs) { IEnumerable<Prediction> predictions = from p in _gestureLibrary.Recognize(gesturePerformedEventArgs.Gesture) orderby p.Score descending where p.Score > 1.0 select p; Prediction prediction = predictions.FirstOrDefault(); if (prediction == null) { Log.Debug(GetType().FullName, "Nothing seemed to match the user's gesture, so don't do anything."); return; } Log.Debug(GetType().FullName, "Using the prediction named {0} with a score of {1}.", prediction.Name, prediction.Score); if (prediction.Name.StartsWith("checkmark")) { _imageView.SetImageResource(Resource.Drawable.checked_me); } else if (prediction.Name.StartsWith("erase", StringComparison.OrdinalIgnoreCase)) { // Match one of our "erase" gestures _imageView.SetImageResource(Resource.Drawable.check_me); } }
Uruchom aplikację i uruchom działanie niestandardowego rozpoznawania gestów. Powinien wyglądać podobnie do poniższego zrzutu ekranu:
Teraz narysuj znacznik wyboru na ekranie, a wyświetlana mapa bitowa powinna wyglądać mniej więcej tak, jak pokazano na następnych zrzutach ekranu:
Na koniec narysuj scribble na ekranie. Pole wyboru powinno powrócić do oryginalnego obrazu, jak pokazano na poniższych zrzutach ekranu:
Wiesz już, jak zintegrować dotyk i gesty w aplikacji systemu Android przy użyciu platformy Xamarin.Android.