Exemplarische Vorgehensweise – Verwenden von Touch in Android
Sehen wir uns an, wie die Konzepte aus dem vorherigen Abschnitt in einer funktionierenden Anwendung verwendet werden. Wir erstellen eine Anwendung mit vier Aktivitäten. Die erste Aktivität ist ein Menü oder eine Übersicht, die die anderen Aktivitäten startet, um die verschiedenen APIs zu veranschaulichen. Der folgende Screenshot zeigt die Standard Aktivität:
Im ersten Aktivitätsbeispiel wird gezeigt, wie Ereignishandler zum Berühren der Ansichten verwendet werden. Die Gestenerkennungsaktivität veranschaulicht, wie Sie Unterklassen Android.View.Views
und Ereignisse behandeln und wie Gesten behandelt werden. Die dritte und letzte Aktivität, benutzerdefinierte Geste, zeigt, wie benutzerdefinierte Gesten verwendet werden. Um Dies zu vereinfachen und zu absorbieren, werden wir diese exemplarische Vorgehensweise in Abschnitte unterteilen, wobei sich jeder Abschnitt auf einen der Aktivitäten konzentriert.
Touch-Beispielaktivität
Öffnen Sie das Projekt TouchWalkthrough_Start. Die MainActivity ist alles im Griff – es liegt an uns, das Touchverhalten in der Aktivität umzusetzen. Wenn Sie die Anwendung ausführen und auf "Touchbeispiel" klicken, sollte die folgende Aktivität gestartet werden:
Nachdem wir nun bestätigt haben, dass die Aktivität gestartet wird, öffnen Sie die Datei TouchActivity.cs , und fügen Sie einen Handler für das
Touch
Ereignis derImageView
:_touchMeImageView.Touch += TouchMeImageViewOnTouch;
Fügen Sie als Nächstes die folgende Methode zum TouchActivity.cs hinzu:
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; }
Beachten Sie im obigen Code, dass wir die und Down
die Move
Aktion genauso behandeln. Dies liegt daran, dass sich der Benutzer, auch wenn er den Finger nicht vom Finger hebt ImageView
, sich bewegen kann oder der vom Benutzer ausgeübte Druck geändert werden kann. Diese Arten von Änderungen generieren eine Move
Aktion.
Jedes Mal, wenn der Benutzer das ImageView
Ereignis berührt, wird das Touch
Ereignis ausgelöst, und unser Handler zeigt die Meldung "Touch Begins " auf dem Bildschirm an, wie im folgenden Screenshot gezeigt:
Solange der Benutzer die ImageView
Fingereingabe berührt, wird "Touch Begins " im TextView
Bereich angezeigt. Wenn der Benutzer die ImageView
Fingereingabe nicht mehr berührt, wird die Meldung "Touch Ends " im TextView
folgenden Screenshot angezeigt:
Gestikerkennungsaktivität
Ermöglicht jetzt die Implementierung der Gestenerkennungsaktivität. Diese Aktivität veranschaulicht, wie Sie eine Ansicht um den Bildschirm ziehen und eine Möglichkeit zum Implementieren des Zusammendrückens zum Zoomen veranschaulichen.
Fügen Sie der Anwendung eine neue Aktivität hinzu, die aufgerufen wird
GestureRecognizer
. Bearbeiten Sie den Code für diese Aktivität so, dass er dem folgenden Code ähnelt:public class GestureRecognizerActivity : Activity { protected override void OnCreate(Bundle bundle) { base.OnCreate(bundle); View v = new GestureRecognizerView(this); SetContentView(v); } }
Fügen Sie dem Projekt eine neue Android-Ansicht hinzu, und nennen Sie sie
GestureRecognizerView
. Fügen Sie dieser Klasse die folgenden Variablen hinzu: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;
Fügen Sie den folgenden Konstruktor zu
GestureRecognizerView
. Dieser Konstruktor fügt unserer Aktivität einenImageView
hinzu. An diesem Punkt wird der Code immer noch nicht kompiliert – wir müssen die KlasseMyScaleListener
erstellen, die beim Zusammendrücken derImageView
Größe des Benutzers hilft: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)); }
Um das Bild für unsere Aktivität zu zeichnen, müssen wir die
OnDraw
Methode der View-Klasse außer Kraft setzen, wie im folgenden Codeausschnitt gezeigt. Dieser Code verschebt dieImageView
position, die durch_posX
und_posY
ändert die Größe des Bilds entsprechend dem Skalierungsfaktor:protected override void OnDraw(Canvas canvas) { base.OnDraw(canvas); canvas.Save(); canvas.Translate(_posX, _posY); canvas.Scale(_scaleFactor, _scaleFactor); _icon.Draw(canvas); canvas.Restore(); }
Als Nächstes müssen wir die Instanzvariable
_scaleFactor
aktualisieren, während der Benutzer dieImageView
. Wir fügen eine Klasse mit dem NamenMyScaleListener
hinzu. Diese Klasse lauscht auf die Skalierungsereignisse, die von Android ausgelöst werden, wenn der Benutzer dieImageView
. Fügen Sie die folgende innere Klasse zuGestureRecognizerView
. Diese Klasse ist einScaleGesture.SimpleOnScaleGestureListener
. Diese Klasse ist eine Komfortklasse, die Listener unterklassen können, wenn Sie an einer Teilmenge von Gesten interessiert sind: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; } }
Die nächste Methode, in
GestureRecognizerView
der wir außer Kraft setzen müssen, istOnTouchEvent
. Der folgende Code listet die vollständige Implementierung dieser Methode auf. Hier gibt es viel Code, also lassen Sie sich eine Minute dauern und sehen, was hier passiert. Als Erstes skaliert diese Methode das Symbol bei Bedarf – dies wird durch Aufrufen_scaleDetector.OnTouchEvent
behandelt. Als Nächstes versuchen wir herauszufinden, welche Aktion diese Methode aufgerufen hat:Wenn der Benutzer den Bildschirm berührt hat, zeichnen wir die X- und Y-Position und die ID des ersten Zeigers auf, der den Bildschirm berührt hat.
Wenn der Benutzer seine Toucheingabe auf dem Bildschirm bewegt hat, stellen wir fest, wie weit der Benutzer den Zeiger bewegt hat.
Wenn der Benutzer seinen Finger vom Bildschirm entfernt hat, beenden wir die Nachverfolgung der Gesten.
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; }
Führen Sie nun die Anwendung aus, und starten Sie die Gestenerkennungsaktivität. Wenn der Bildschirm gestartet wird, sollte etwa wie der folgende Screenshot aussehen:
Tippen Sie nun auf das Symbol, und ziehen Sie es um den Bildschirm. Probieren Sie die Zusammendrücken-zu-Zoom-Geste aus. Irgendwann sieht der Bildschirm ungefähr wie der folgende Screenshot aus:
An dieser Stelle sollten Sie sich einen Paten auf den Rücken geben: Sie haben gerade Zusammendrücken in einer Android-Anwendung implementiert! Machen Sie eine kurze Pause und können sie in dieser exemplarischen Vorgehensweise mit benutzerdefinierten Gesten zur dritten und endgültigen Aktivität wechseln.
Benutzerdefinierte Gestenaktivität
Der letzte Bildschirm in dieser exemplarischen Vorgehensweise verwendet benutzerdefinierte Gesten.
Für diese exemplarische Vorgehensweise wurde die Gestenbibliothek bereits mithilfe des Gestiktools erstellt und dem Projekt in den Dateiressourcen /Roh-/Gesten hinzugefügt. Mit diesem Stück Haushaltung aus dem Weg können Sie mit der endgültigen Aktivität in der exemplarischen Vorgehensweise loswerden.
Fügen Sie dem Projekt eine Layoutdatei mit dem Namen custom_gesture_layout.axml mit dem folgenden Inhalt hinzu. Das Projekt verfügt bereits über alle Bilder im Ordner "Ressourcen ":
<?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>
Fügen Sie dem Projekt als Nächstes eine neue Aktivität hinzu, und nennen Sie sie
CustomGestureRecognizerActivity.cs
. Fügen Sie der Klasse zwei Instanzenvariablen hinzu, wie in den folgenden beiden Codezeilen dargestellt:private GestureLibrary _gestureLibrary; private ImageView _imageView;
Bearbeiten Sie die
OnCreate
Methode dieser Aktivität so, dass sie dem folgenden Code ähnelt. Lassen Sie uns eine Minute dauern, um zu erläutern, was in diesem Code passiert. Als Erstes instanziieren wir eineGestureOverlayView
Instanziierung und legen sie als Stammansicht der Aktivität fest. Außerdem weisen wir demGesturePerformed
Ereignis einesGestureOverlayView
Ereignisses einen Ereignishandler zu. Als Nächstes wird die zuvor erstellte Layoutdatei aufgeblasen und als untergeordnete Ansicht derGestureOverlayView
Datei hinzugefügt. Der letzte Schritt besteht darin, die Variable_gestureLibrary
zu initialisieren und die Gestendatei aus den Anwendungsressourcen zu laden. Wenn die Gestendatei aus irgendeinem Grund nicht geladen werden kann, gibt es nicht viel dieser Aktivität, sodass sie heruntergefahren wird: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(); } }
Abschließend müssen wir die Methode
GestureOverlayViewOnGesturePerformed
implementieren, wie im folgenden Codeausschnitt gezeigt. Wenn dieGestureOverlayView
Geste erkannt wird, ruft sie diese Methode zurück. Als Erstes versuchen wir, objekteIList<Prediction>
abzurufen, die mit der Geste übereinstimmen, indem wir aufrufen_gestureLibrary.Recognize()
. Wir verwenden ein bisschen LINQ, um diePrediction
höchste Punktzahl für die Geste zu erhalten.Wenn keine Übereinstimmungsgeste mit einer hohen Punktzahl vorhanden ist, wird der Ereignishandler beendet, ohne etwas zu tun. Andernfalls überprüfen wir den Namen der Vorhersage und ändern das angezeigte Bild basierend auf dem Namen der Geste:
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); } }
Führen Sie die Anwendung aus, und starten Sie die Aktivität "Benutzerdefinierte Gestenerkennung". Es sollte ungefähr wie der folgende Screenshot aussehen:
Zeichnen Sie nun ein Häkchen auf dem Bildschirm, und die angezeigte Bitmap sollte ungefähr wie in den nächsten Screenshots dargestellt aussehen:
Zeichnen Sie schließlich einen Skizzenausschnitt auf dem Bildschirm. Das Kontrollkästchen sollte wie in den folgenden Screenshots dargestellt wieder in das ursprüngliche Bild geändert werden:
Sie wissen jetzt, wie Sie Toucheingaben und Gesten in eine Android-Anwendung mit Xamarin.Android integrieren.