Udostępnij za pośrednictwem


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:

Przykładowy zrzut ekranu z przyciskiem Touch Me

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:

    Zrzut ekranu przedstawiający działanie z wyświetlonym działaniem Touch Begins

  • 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:

Zrzut ekranu przedstawiający działanie z rozpoczęciem dotyku

Tak długo, jak użytkownik dotyka ImageViewelementu , funkcja Touch Begins będzie wyświetlana w elemecie TextView. Gdy użytkownik nie dotyka ImageViewjuż elementu , w pliku zostanie wyświetlony TextViewkomunikat Touch Ends, jak pokazano na poniższym zrzucie ekranu:

Zrzut ekranu przedstawiający działanie z funkcją Touch Ends

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 GestureRecognizerViewnazwę . 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 element ImageView do naszego działania. W tym momencie kod nadal nie zostanie skompilowany — musimy utworzyć klasę MyScaleListener , która ułatwi zmianę rozmiaru ImageView , 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 element ImageView 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łaszcza ImageViewelement . Dodamy klasę o nazwie MyScaleListener. Ta klasa będzie nasłuchiwać zdarzeń skalowania, które będą zgłaszane przez system Android, gdy użytkownik ściągnie ImageViewelement . Dodaj następującą klasę wewnętrzną do GestureRecognizerViewklasy . Ta klasa to ScaleGesture.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ć, jest OnTouchEvent. 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:

    Ekran startowy rozpoznawania gestów z ikoną systemu Android

  • 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:

    Gesty przesuwają ikonę wokół 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.csnazwę . 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ąpienia GestureOverlayView elementu i ustawienie, które jest głównym widokiem działania. Przypisujemy również program obsługi zdarzeń do GesturePerformed zdarzenia GestureOverlayView. Następnie zawyżamy utworzony wcześniej plik układu i dodamy go jako widok podrzędny obiektu GestureOverlayView. 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 gestu GestureOverlayView 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:

    Zrzut ekranu przedstawiający obraz Check Me

    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:

    Rysowana znacznik wyboru, znacznik wyboru jest rozpoznawany

    Na koniec narysuj scribble na ekranie. Pole wyboru powinno powrócić do oryginalnego obrazu, jak pokazano na poniższych zrzutach ekranu:

    Na ekranie jest wyświetlany oryginalny obraz

Wiesz już, jak zintegrować dotyk i gesty w aplikacji systemu Android przy użyciu platformy Xamarin.Android.