Advertisement
  1. Code
  2. Android SDK

Android SDK: Einführung in Gesten

Scroll to top
Read Time: 16 min

German (Deutsch) translation by Katharina Grigorovich-Nevolina (you can also view the original English article)

Eine der am weitesten verbreiteten Veränderungen bei der Verwendung von Touchscreen-Geräten in den letzten Jahren war die Einführung von Fingergesten wie Wischen und Schleudern. Sie ermöglichen eine sehr intuitive und natürliche Interaktion zwischen Benutzer und Gerät. In diesem Tutorial erfahren Sie, wie Sie Gesten in Ihren eigenen Android-Anwendungen verwenden.

In diesem Tutorial wird Code verwendet, in einem Open Source-Projekt wird wird. Die Autoren gehen davon aus, dass der Leser Erfahrung mit Android und Java hat. Wenn Sie alle Fragen zu unseren eigenen haben, können Sie diese gerne stellen.

In diesem Tutorial erfahren Sie, wie Sie mit Fingergesten in Ihren Anwendungen umgehen. Dazu verwenden wir eine einfache Canvas-Objektzeichnung für ein benutzerdefiniertes Ansichtsobjekt. Diese Technik kann auf jede grafische Umgebung angewendet werden, die Sie verwenden, sei es eine 2D-Oberfläche oder sogar ein OpenGL ES-Rendering. Wenn Sie an Multitouch-Gesten interessiert sind (ein fortgeschritteneres Thema für die Gestenbehandlung), werden wir dies in einem weiteren Tutorial behandeln.

Schritt 0: Erstellen des Projekts

Fangen wir einfach an. Erstellen Sie ein neues Android-Projekt. Wir haben unser Projekt Gesture Fun benannt und seine einzige Aktivität mit dem Namen GestureFunActivity konfiguriert. Ändern Sie die Standard-Layoutdatei main.xml in das folgende, sehr einfache Layout:

Das wichtige Element in diesem Layout ist die FrameLayout-Definition. Das FrameLayout-Steuerelement wird verwendet, um die benutzerdefinierte Ansicht zu speichern, in der ein Bild gezeichnet wird.

Zuletzt aktualisieren wir die onCreate()-Methode der Activity-Klasse, um das FrameLayout-Steuerelement zu initialisieren und ihm einige Inhalte bereitzustellen:

Zu diesem Zeitpunkt haben wir die PlayAreaView-Klasse noch nicht definiert, sodass das Projekt nicht kompiliert werden kann. Haben Sie Geduld, wir werden im nächsten Schritt darauf zurückkommen.

Schritt 1: Erstellen einer neuen Ansicht: PlayAreaView

Nachdem alle unsere Projekteinrichtungen aus dem Weg geräumt sind, können wir uns jetzt auf den interessanten Teil konzentrieren: das Zeichnen auf dem Canvas-Objekt. Eine einfache Möglichkeit, ein Canvas-Objekt zum Zeichnen zu bringen, besteht darin, die onDraw() -Methode eines View-Objekts zu überschreiben. Praktischerweise hat diese Methode einen einzigen Parameter: das Canvas-Objekt. Das Zeichnen einer Bitmap-Grafik auf einem Canvas-Objekt ist so einfach wie das Aufrufen der drawBitmap() -Methode des Canvas-Objekts. Hier ist ein einfaches Beispiel für eine Implementierung der onDraw() -Methode, wie sie in unserer neuen PlayAreaView-Klasse definiert ist:

Unsere Implementierung der onDraw()-Methode ist ziemlich einfach. Wie üblich müssen Sie Ihre DEBUG_TAG-Protokollierungs-Tag-Variable irgendwo in Ihrer Aktivität definieren. Der größte Teil der onDraw() -Methode ist nur eine Informationsausgabe. Die einzige echte Arbeit, die mit dieser Methode ausgeführt wird, findet im Aufruf drawBitmap() statt, wobei der erste Parameter das zu zeichnende Bild ist. Der zweite Parameter ist ein Matrix-Objekt namens translate, das, wie der Name schon sagt, bestimmt, wo die Bitmap relativ zu der Ansicht gezeichnet wird, in der sich das Canvas-Objekt befindet. Der gesamte Rest des Codes in diesem Tutorial umfasst das Bearbeiten der Übersetzungsmatrix basierend auf bestimmten Benutzerberührungsereignissen. Das ändert sich wiederum, wo das Bitmap-Objekt im Canvas-Bereich und damit auf dem Bildschirm gezeichnet wird.

Schritt 2: Konfigurieren der neuen Ansicht

Die PlayAreaView-Klasse benötigt einen Konstruktor, um eine Ersteinrichtung durchzuführen. Da unsere benutzerdefinierte Ansicht auf Gesten reagieren muss, benötigen wir hier einen GestureDetector. Ein GestureDetector ist eine Android-Klasse, die Bewegungsereignisse aufnehmen, mathematisch zaubern kann, um festzustellen, was sie sind, und dann Aufrufe als bestimmte Geste oder andere Bewegungsrückrufe an ein GestureListener-Objekt delegieren kann. Das GestureListener-Objekt, eine von uns implementierte Klasse, empfängt diese Aufrufe für bestimmte Gesten, die der GestureDetector erkennt, und ermöglicht es uns, nach Belieben darauf zu reagieren (in diesem Fall, um eine Grafik in unserer PlayAreaView zu verschieben). Obwohl der GestureDetector die Erkennung bestimmter Bewegungen übernimmt, führt er weder etwas Spezifisches mit ihnen aus, noch verarbeitet er alle Arten von Gesten. Für die Zwecke dieses Tutirials werden jedoch gerade genug Informationen bereitgestellt. Lassen Sie es uns also verbinden:

Schauen wir uns den PlayAreaView-Konstruktor etwas genauer an. Zuerst initialisieren wir die Übersetzungsmatrix in eine Identitätsmatrix (Standardeinstellung). Denken Sie daran, dass eine Identitätsmatrix keine Änderungen an einer Bitmap vornimmt: Sie wird an ihrer ursprünglichen Position gezeichnet.
Als Nächstes erstellen und initialisieren wir den GestureDetector - einen Standard - und weisen ihm ein gültiges GestureListener-Objekt zu (wir werden gleich mehr darüber sprechen). Schließlich wird die als Droide bezeichnete Bitmap-Zeichnung direkt aus den Projektressourcen geladen. Sie können jedes gewünschte Bild verwenden - einen Baseball, einen Apfel, einen Glückskeks usw. Das ist die Grafik, die Sie auf dem Canvas-Objekt herumwerfen werden.

Schritt 3: Anschließen des GestureDetector

Als nächstes gelangen wir zum GestureListener, da es sich um ein benutzerdefiniertes Objekt handelt. Lassen Sie uns zunächst das GestureDetector-Objekt so verkabeln, dass es die Bewegungsdaten empfängt, die für die Erkennung von Magie erforderlich sind.

Verbinden wir nun das GestureDector-Objekt mit dem Namen "Gesten", um Ereignisse zu empfangen. Überschreiben Sie dazu die onTouchEvent()-Methode des View-Steuerelements in der PlayAreaView-Klasse wie folgt:

Wir haben hier den GestureDetector zum letzten Wort in allen Berührungsereignissen für diese benutzerdefinierte Ansicht gemacht. Der GestureDetector macht jedoch nichts mit Bewegungsereignissen, sondern erkennt sie einfach und ruft die registrierte GestureListener-Klasse auf.

Schritt 4: Implementieren eines Gesten-Listeners

Um auf die von der GestureDetector-Klasse erkannten Ereignisse reagieren zu können, müssen wir die GestureListener-Klasse implementieren. Die Bewegungsereignisse, an denen wir am meisten interessiert sind, sind Doppelklicks und Gesten jeglicher Art. Um auf diese Art von Bewegungsereignissen zu warten, muss unsere GestureListener-Klasse sowohl die OnGestureListener- als auch die OnDoubleTapListener-Schnittstelle implementieren.

Fügen Sie nach dem Hinzufügen dieser Klasse als Unterklasse der Aktivität Standardimplementierungen für jede der erforderlichen Methoden hinzu. Hier ist beispielsweise eine Implementierung für die onDown() -Methode:

Durch die Implementierung dieser Methoden können Sie die verschiedenen Ereignisse untersuchen, die vom GestureDetector-Objekt erkannt werden. Interessanterweise wird die Hauptgeste, an der wir hier interessiert sind - Scrollen (oder Ziehen) - nicht erkannt, wenn die onDown()-Methode nicht true zurückgibt. Sie können jedoch für die anderen erkannten Ereignisse, an denen Sie nicht interessiert sind, false zurückgeben.

Das MotionEvent-Objekt, das als Parameter an jede Rückrufmethode übergeben wird, repräsentiert manchmal das Berührungsereignis, das die Gestenerkennung gestartet hat, und manchmal das letzte Ereignis, das die Gestenerkennung abgeschlossen hat. Für unsere Zwecke lassen wir die GestureDetector-Klasse alle Details der Entschlüsselung verarbeiten, welche Art von Bewegung das MotionEvent darstellt.

Hinweis: Das Android-Framework bietet auch eine Convenience-Klasse namens SimpleOnGestureListener, die die beiden Schnittstellen (OnGestureListener & OnDoubleTapListener) zu einer einzigen Klasse mit Standardimplementierungen für alle Methoden kombiniert. Die Standardimplementierungen geben false zurück.

Schritt 5: Behandeln einfacher Bewegungsereignisse

Das erste Ereignis, das wir behandeln möchten, ist das Bildlaufereignis. Ein Bildlaufereignis tritt auf, wenn der Benutzer den Bildschirm berührt und dann seinen Finger darüber bewegt. Diese Geste wird auch als Drag-Ereignis bezeichnet. Dieses Ereignis tritt über die onScroll() -Methode der OnGestureListener-Schnittstelle ein.

Hier ist die Implementierung der onScroll()-Methode:

Verwenden Sie das Bildlaufereignis, um eine Verschiebungsanforderung an das PlayAreaView-Objekt weiterzuleiten. Die Implementierung dieser Methode ist ein wichtiger erster Schritt bei der Abbildung, wie ein Fingerbewegungsereignis die grafische Bewegung auffordert. Wir werden in Kürze darauf zurückkommen. In der Zwischenzeit haben Sie Ihre erste Geste ausgeführt!

Die Grafik wird über den gesamten Bildschirm verschoben - und manchmal sogar davon entfernt. Per Definition ist das Bild nur sichtbar, wenn es innerhalb der Grenzen des View-Objekts gezeichnet wird. Wenn die Koordinaten der Grafiken außerhalb der Grenzen des Ansichtsobjekts landen, wird die Grafik abgeschnitten (nicht sichtbar). Sie können die Kantenerkennung und verschiedene andere logische Elemente einfügen (wir befürchten, dass dies nicht in diesem Tutorial enthalten ist) oder einfach die Erkennung für doppeltes Tippen hinzufügen und die Position der Grafik zurücksetzen. Hier ist die Beispielimplementierung der onDoubleTap()-Methode (über die OnDoubleTapListener-Schnittstelle):

Wie bei den möglichen Methodenimplementierung werden wir diese diese Bewegung, ein doppeltes Tippen, um eine Änderung in unserer Ansichtssteuerung auszulösen. In diesem Herbst setzen wir einfach den Speicherort der Ansicht zurück.

Schritt 6: Schleudern

Eine Schleudergeste hinterlässt im Wesentlichen die Geschwindigkeit eines Objekts, das über einen Bildschirm gezogen wurde. Das in Bewegung befindliche Element wird normalerweise allmählich verlangsamt. Dieses Verhalten wird jedoch von der Implementierung des Entwicklers bestimmt. In einem Spiel könnte die Geschwindigkeit beispielsweise der Physik der Spielwelt unterliegen. In anderen Anwendungen könnte die Geschwindigkeit auf der Formel basieren, die sich für die Aktion, die sie darstellt, als richtig anfühlt. Testen ist der beste Weg, um eine Vorstellung davon zu bekommen, wie es sich anfühlt. Nach unserer Erfahrung sind einige Versuche und Irrtümer erforderlich, um sich auf etwas zu einigen, das sich genau richtig anfühlt - und aussieht.

In unserer Implementierung werden wir die Zeitspanne variieren, bevor die durch das Schleudern verursachte Bewegung stoppt, und dann einfach eine Animation des Bildes zum endgültigen Ziel starten, basierend auf der Geschwindigkeit, die uns von der onFling() -Methode und übergeben wurde die Zeit, die wir einstellen. Denken Sie daran, dass die Schleudergeste erst erkannt wird, nachdem der Finger des Benutzers das Display nicht mehr berührt. Stellen Sie sich vor, Sie werfen einen Stein - der Stein bewegt sich weiter, wenn Sie ihn loslassen - das ist der Teil, den wir animieren möchten, sobald der Benutzer „loslässt“.

Klingt komplex? Hier ist der Code:

Wir müssen nicht einmal die beiden MotionEvent-Parameter untersuchen, die Geschwindigkeitsdaten sind für unsere Zwecke ausreichend. Die Geschwindigkeitseinheiten sind in Pixel pro Sekunde angegeben. Wir können also die Geschwindigkeitsdaten verwenden, um einen Skalierungsfaktor zu bestimmen, der verwendet wird, um die endgültige Zeitdauer zu bestimmen, bevor das Bild vollständig zur Ruhe kommt. In unserem Fall verwenden wir 40% einer Sekunde (400 ms). Wenn wir also die Hälfte der beiden Geschwindigkeitswerte mit 40% multiplizieren (auch bekannt als die Variable distanceTimeFactor), erhalten wir die Gesamtbewegung, die nach diesem Sekundenbruchteil erreicht wird. Schließlich geben wir diese Informationen an unsere benutzerdefinierte onAnimateMove()-Methode des View-Objekts weiter, wodurch unsere Grafik mithilfe der vom fling-Bewegungsereignis bereitgestellten Informationen tatsächlich über den Bildschirm verschoben wird.

Warum die Hälfte der Anfangsgeschwindigkeit? Wenn wir beispielsweise mit Geschwindigkeit A beginnen und über einen beliebigen Zeitraum mit Geschwindigkeit B enden, beträgt die Durchschnittsgeschwindigkeit (A+B)/2. In diesem Fall ist die Endgeschwindigkeit 0. Daher halbieren wir die Geschwindigkeit hier, damit das Bild nicht versehentlich so aussieht, als würde es schneller von unserem Finger wegspringen als vor dem Loslassen.

Warum 400 ms? Kein Grund, aber auf den meisten Geräten sieht es einigermaßen gut aus. Es ist nicht unähnlich, Ihre Mausbewegungen zu kalibrieren - zu schnell und es fühlt sich nervös und schwer zu sehen an, zu langsam und Sie warten darauf, dass Ihr träger Mauszeiger Ihr Gehirn einholt. Dieser Wert ist die Hauptvariable, die Sie an das „Gefühl“ Ihres Flings anpassen können. Je höher der Wert, desto weniger „Reibung“ scheint das Bild zu haben, wenn es auf dem Bildschirm zum Stillstand kommt. Wenn Sie echte Oberflächenvariationen haben, müssen Sie regelmäßige physikalische Berechnungen anwenden. Hier machen wir nur eine feste Funktion, die ohne echte Physik langsamer wird.

Schritt 7: Verschieben des Bildes

Nachdem alle Gesten, die uns interessieren, behandelt wurden, ist es an der Zeit, die tatsächliche Bewegung der zugrunde liegenden Grafik zu implementieren. Fügen Sie in der PlayAreaView-Klasse die onMove()-Methode hinzu:

Diese Methode macht zwei Dinge. Erstens übersetzt es (translate = Grafikbegriff für die Bewegung von Punkt A nach Punkt B) unsere eigene Matrix um die Entfernung, um die sich der Finger bewegt hat. Dann macht es die Ansicht ungültig, so dass sie neu gezeichnet wird. Beim Zeichnen wird das Bild an einer anderen Stelle in der Ansicht gezeichnet. Wenn wir das gesamte View-Objekt schwenken wollten, könnten wir die translate()-Methode des View-Objekts verwenden, um die interne Matrix zu aktualisieren, die von Canvas zum Ausführen der gesamten Zeichnung verwendet wird. Dies mag für einige Dinge gut funktionieren, aber wenn wir statische (womit feste, stationäre, unbewegliche, wie Berge) Dinge in der Ansicht hätten, wäre dies nicht der Fall. Stattdessen aktualisieren wir in diesem Fall einfach unsere eigene Matrix, übersetzen, die wir jedes Mal verwenden, wenn wir die Grafik zeichnen.

Jetzt fügen wir auch die onResetLocation()-Methode hinzu:

Diese Methode setzt die Matrix einfach auf die Identitätsmatrix zurück und bewirkt, dass die Ansicht über die invalidate()-Methode erneut gezeichnet wird. Wenn die Ansicht neu gezeichnet wird, befindet sich die Grafik wieder in ihrer ursprünglichen Position.

Schritt 8: Reibungsloses Verschieben des Bildes

Für die Schleuderbewegung haben wir etwas mehr zu tun, als sie nur an einer neuen Stelle zu zeichnen. Wir möchten, dass es diese Position reibungslos animiert. Eine reibungslose Bewegung kann durch Animation erreicht werden, dh das Bild wird sehr schnell an einer anderen Stelle gezeichnet. Android verfügt über integrierte Animationsklassen, die jedoch für ganze Ansichten gelten. Wir animieren kein Ansichtsobjekt. Stattdessen verschieben wir ein Bild auf der Leinwand, das von einer Ansicht gesteuert wird. Also müssen wir unsere eigene Animation implementieren. Verflixt. ☺

Android bietet verschiedene Interpolatoren, mit denen Sie die Position eines Objekts zu einem bestimmten Zeitpunkt während Animationen mithilfe der integrierten Animationsklassen optimieren können. Wir können diese verschiedenen Interpolatoren in unserer eigenen Animation nutzen, um ein wenig Arbeit zu sparen - und einige lustige Effekte anwenden. Das ist möglich, da die bereitgestellten Interpolatoren sehr allgemein gehalten sind und nicht an die Besonderheiten der integrierten View-Animationen gebunden sind.

Beginnen wir mit der onAnimateMove()-Methode:

Bei dieser Methode verfolgen wir den Startort, die Startzeit und die Endzeiten.

Hier initialisieren wir unsere Animation mit der OvershootInterpolator-Klasse. Da dieser Interpolator bewirkt, dass sich das Bild insgesamt etwas weiter bewegt als von uns berechnet, beginnt das Bild technisch etwas schneller. Der Unterschied ist so gering, dass er nicht bemerkt werden sollte. Wenn Sie jedoch Präzisionsgenauigkeit anstreben, müssen Sie sich darauf einstellen (was bedeuten würde, dass Sie Ihren eigenen Interpolator schreiben.
über den Rahmen dieses Tutorials hinaus - und über eine Methode zur Berechnung der zurückgelegten Gesamtstrecke verfügen).

Alle diese Informationen werden verwendet, um zu bestimmen, wann (rechtzeitig) wir uns entlang der Gesamtdauer der Animation befinden. Wir verwenden diese Daten, um zu bestimmen, wann (in Prozent der Zeit) diese Informationen an den Interpolator übergeben werden sollen. Der Interpolator benötigt dies, damit er uns sagen kann, wo (in Prozent Entfernung) wir vom Startpunkt bis zum Endpunkt unserer Bewegung sind. Wir verwenden dies wiederum, um zu bestimmen, wo (in Pixel) wir vom Ausgangspunkt sind.

Diese Berechnung erfolgt in der unten gezeigten Methode onAnimateStep(). Wir rufen die onAnimationStep() -Methode über einen Beitrag in der Nachrichtenwarteschlange auf. Wir möchten nicht in eine enge Schleife geraten - wir würden dazu führen, dass das System nicht mehr reagiert. Ein einfacher Weg ist es also, einfach die Nachrichten zu posten. Auf diese Weise kann das System durch asynchrones Verhalten reaktionsschnell bleiben, ohne sich mit Threads befassen zu müssen. Da wir ohnehin auf dem UI-Thread zeichnen müssen, hat ein Thread in diesem einfachen Beispiel wenig Sinn.

Implementieren wir nun die onAnimateStep()-Methode:

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.