Das Zeichnen einer Benutzeroberfläche ist nur ein Teil der Erstellung einer benutzerdefinierten Ansicht. Außerdem müssen Sie dafür sorgen, dass Ihre Ansicht auf Nutzereingaben so reagiert, dass sie der realen Aktion, die Sie nachahmen, sehr ähnlich ist.
Achten Sie darauf, dass sich die Objekte in Ihrer App wie echte Objekte verhalten. Achten Sie beispielsweise darauf, dass Bilder in Ihrer App nicht aus der App hervorgehen und an anderer Stelle wieder angezeigt werden. Dies ist bei Objekten in der realen Welt nicht der Fall. Verschieben Sie Ihre Bilder stattdessen von einem Ort an einen anderen.
Nutzer wahrnehmen sogar subtiles Verhalten oder Gefühl in einer Benutzeroberfläche und reagieren am besten auf Feinheiten, die die reale Welt imitieren. Wenn Nutzer beispielsweise mit einem UI-Objekt losfliegen, vermitteln Sie ihnen zu Beginn ein Gefühl der Trägheit, die die Bewegung verzögert. Vermitteln Sie ihnen am Ende der Bewegung einen Impuls, durch den das Objekt über den Schlauch hinaus transportiert wird.
Auf dieser Seite wird beschrieben, wie Sie mithilfe der Funktionen des Android-Frameworks diese realen Verhaltensweisen zu Ihrer benutzerdefinierten Ansicht hinzufügen können.
Weitere Informationen finden Sie unter Eingabeereignisse und Übersicht über Property-Animation.
Touch-Gesten zur Eingabe
Wie viele andere UI-Frameworks unterstützt Android ein Eingabeereignismodell. Nutzeraktionen werden zu Ereignissen, die Callbacks auslösen. Sie können diese Callbacks überschreiben, um anzupassen, wie Ihre App auf den Nutzer reagiert. Das häufigste Eingabeereignis im Android-System ist touch, das onTouchEvent(android.view.MotionEvent)
auslöst.
Überschreiben Sie diese Methode, um das Ereignis so zu verarbeiten:
Kotlin
override fun onTouchEvent(event: MotionEvent): Boolean { return super.onTouchEvent(event) }
Java
@Override public boolean onTouchEvent(MotionEvent event) { return super.onTouchEvent(event); }
Berührungsereignisse an sich sind nicht besonders nützlich. Moderne Touch-UIs definieren Interaktionen in Form von Gesten wie Tippen, Ziehen, Drücken, Ziehen und Zoomen. Zum Umwandeln von unbearbeiteten Touch-Ereignissen in Gesten bietet Android GestureDetector
.
Erstellen Sie ein GestureDetector
, indem Sie eine Instanz einer Klasse übergeben, die GestureDetector.OnGestureListener
implementiert.
Wenn Sie nur wenige Gesten verarbeiten möchten, können Sie GestureDetector.SimpleOnGestureListener
erweitern, anstatt die GestureDetector.OnGestureListener
-Oberfläche zu implementieren. Mit diesem Code wird beispielsweise eine Klasse erstellt, die GestureDetector.SimpleOnGestureListener
erweitert und onDown(MotionEvent)
überschreibt.
Kotlin
private val myListener = object : GestureDetector.SimpleOnGestureListener() { override fun onDown(e: MotionEvent): Boolean { return true } } private val detector: GestureDetector = GestureDetector(context, myListener)
Java
class MyListener extends GestureDetector.SimpleOnGestureListener { @Override public boolean onDown(MotionEvent e) { return true; } } detector = new GestureDetector(getContext(), new MyListener());
Unabhängig davon, ob Sie GestureDetector.SimpleOnGestureListener
verwenden oder nicht, müssen Sie immer eine onDown()
-Methode implementieren, die true
zurückgibt. Das ist erforderlich, da alle Touch-Gesten mit einer onDown()
-Nachricht beginnen. Wenn du false
von onDown()
zurückgibst, wie es bei GestureDetector.SimpleOnGestureListener
der Fall ist, geht das System davon aus, dass du den Rest der Geste ignorieren möchtest. Die anderen Methoden von GestureDetector.OnGestureListener
werden dann nicht aufgerufen. Geben Sie false
nur dann von onDown()
zurück, wenn Sie eine ganze Geste ignorieren möchten.
Nachdem du GestureDetector.OnGestureListener
implementiert und eine Instanz von GestureDetector
erstellt hast, kannst du mit GestureDetector
die Touch-Ereignisse interpretieren, die du in onTouchEvent()
erhältst.
Kotlin
override fun onTouchEvent(event: MotionEvent): Boolean { return detector.onTouchEvent(event).let { result -> if (!result) { if (event.action == MotionEvent.ACTION_UP) { stopScrolling() true } else false } else true } }
Java
@Override public boolean onTouchEvent(MotionEvent event) { boolean result = detector.onTouchEvent(event); if (!result) { if (event.getAction() == MotionEvent.ACTION_UP) { stopScrolling(); result = true; } } return result; }
Wenn du onTouchEvent()
ein Touch-Ereignis übergibst, das nicht als Teil einer Geste erkannt wird, wird false
zurückgegeben. Anschließend können Sie Ihren eigenen benutzerdefinierten Code für die Gestenerkennung ausführen.
Physisch plausible Bewegungen erzeugen
Gesten sind eine leistungsstarke Möglichkeit, Geräte mit Touchscreen zu steuern. Sie sind jedoch unlogisch und schwer zu merken, sofern sie nicht zu physisch plausiblen Ergebnissen führen.
Angenommen, Sie möchten eine horizontale Fingerbewegung implementieren, mit der sich das gezeichnete Element in der Ansicht um seine vertikale Achse dreht. Diese Geste ist sinnvoll, wenn die UI reagiert, indem sie sich schnell in Richtung des Schlägers bewegt und dann langsamer wird, als ob der Nutzer auf einen Schwung drückt und ihn dreht.
In der Dokumentation zum Animieren einer Scrollbewegung wird ausführlich erläutert, wie Sie Ihr eigenes Scoll-Verhalten implementieren können. Den Eindruck eines Schwungrads zu simulieren, ist jedoch nicht ganz einfach. Damit ein Schwungradmodell richtig funktioniert,
ist eine Menge Physik und Mathematik erforderlich. Zum Glück bietet Android Hilfsklassen, mit denen dieses und andere Verhaltensweisen simuliert werden können. Die Klasse Scroller
bildet die Grundlage für die Verarbeitung von Fliege-Gesten im Flywheel-Stil.
Um einen Fling zu starten, rufen Sie fling()
mit der Startgeschwindigkeit und den Mindest- und Höchstwerten x und y auf. Für den Geschwindigkeitswert können Sie den von GestureDetector
berechneten Wert verwenden.
Kotlin
fun onFling(e1: MotionEvent, e2: MotionEvent, velocityX: Float, velocityY: Float): Boolean { scroller.fling( currentX, currentY, (velocityX / SCALE).toInt(), (velocityY / SCALE).toInt(), minX, minY, maxX, maxY ) postInvalidate() return true }
Java
@Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { scroller.fling(currentX, currentY, velocityX / SCALE, velocityY / SCALE, minX, minY, maxX, maxY); postInvalidate(); return true; }
Durch den Aufruf von fling()
wird das Physikmodell für die Touch-Geste eingerichtet. Aktualisieren Sie danach Scroller
, indem Sie in regelmäßigen Abständen Scroller.computeScrollOffset()
aufrufen. computeScrollOffset()
aktualisiert den internen Status des Scroller
-Objekts. Dazu wird die aktuelle Zeit gelesen und das Physikmodell verwendet, um die x- und y-Position zu diesem Zeitpunkt zu berechnen. Rufen Sie getCurrX()
und getCurrY()
auf, um diese Werte abzurufen.
Bei den meisten Ansichten werden die x- und y-Positionen des Scroller
-Objekts direkt an scrollTo()
übergeben.
Dieses Beispiel ist etwas anders: Es wird die aktuelle Scroll-x-Position verwendet, um den Drehwinkel der Ansicht festzulegen.
Kotlin
scroller.apply { if (!isFinished) { computeScrollOffset() setItemRotation(currX) } }
Java
if (!scroller.isFinished()) { scroller.computeScrollOffset(); setItemRotation(scroller.getCurrX()); }
Die Klasse Scroller
berechnet Scrollpositionen für Sie, wendet diese Positionen jedoch nicht automatisch auf Ihre Ansicht an. Wenden Sie oft so viele neue Koordinaten an, dass die Scrollanimation flüssiger aussieht. Dafür gibt es zwei Möglichkeiten:
- Erzwingen Sie das erneute Zeichnen, indem Sie nach dem Aufrufen von
fling()
postInvalidate()
aufrufen. Bei dieser Technik müssen Sie den Scroll-Offset inonDraw()
berechnen und jedes MalpostInvalidate()
aufrufen, wenn sich der Scroll-Offset ändert. - Richten Sie eine
ValueAnimator
ein, die für die Dauer des Fling-Vorgangs animiert wird, und fügen Sie einen Listener hinzu, um Animationsaktualisierungen zu verarbeiten. Rufen Sie dazuaddUpdateListener()
auf. Mit diesem Verfahren lassen sich die Eigenschaften einerView
animieren.
Reibungslose Übergänge
Nutzer erwarten, dass eine moderne Benutzeroberfläche reibungslos zwischen den Zuständen wechselt: UI-Elemente werden ein- und ausgeblendet, anstatt zu erscheinen und zu verschwinden. Bewegungen beginnen und enden reibungslos, statt abrupt zu starten und anzuhalten. Mit dem Android-Framework für Animationen lassen sich reibungslose Übergänge einfacher gestalten.
Wenn Sie das Animationssystem verwenden möchten, sollten Sie die Eigenschaft nicht direkt ändern, wenn sich eine Eigenschaft ändert, was sich auf die Darstellung Ihrer Ansicht auswirkt. Verwenden Sie stattdessen ValueAnimator
, um die Änderung vorzunehmen. Im folgenden Beispiel wird durch das Ändern der ausgewählten untergeordneten Komponente in der Ansicht die gesamte gerenderte Ansicht so gedreht, dass der Auswahlzeiger zentriert wird.
ValueAnimator
ändert die Rotation über einen Zeitraum von mehreren Hundert Millisekunden, anstatt den neuen Rotationswert sofort festzulegen.
Kotlin
autoCenterAnimator = ObjectAnimator.ofInt(this, "Rotation", 0).apply { setIntValues(targetAngle) duration = AUTOCENTER_ANIM_DURATION start() }
Java
autoCenterAnimator = ObjectAnimator.ofInt(this, "Rotation", 0); autoCenterAnimator.setIntValues(targetAngle); autoCenterAnimator.setDuration(AUTOCENTER_ANIM_DURATION); autoCenterAnimator.start();
Wenn der Wert, den Sie ändern möchten, eines der View
-Basiseigenschaften ist, ist die Animation noch einfacher, da Ansichten eine integrierte ViewPropertyAnimator
haben, die für die gleichzeitige Animation mehrerer Attribute optimiert ist, wie im folgenden Beispiel:
Kotlin
animate() .rotation(targetAngle) .duration = ANIM_DURATION .start()
Java
animate().rotation(targetAngle).setDuration(ANIM_DURATION).start();