Das Zeichnen einer UI ist nur ein Teil des Erstellens einer benutzerdefinierten Ansicht. Außerdem muss Ihre Ansicht so auf Nutzereingaben reagieren, dass sie der realen Handlung ähnelt, die Sie imitieren.
Lassen Sie die Objekte in Ihrer App sich wie echte Objekte verhalten. Achten Sie beispielsweise darauf, dass Bilder in Ihrer App nicht auftauchen und an anderer Stelle wieder erscheinen, weil Objekte in der realen Welt dies nicht tun. Verschieben Sie Ihre Bilder stattdessen von einem Ort an einen anderen.
Nutzer spüren selbst subtiles Verhalten oder Gefühl in einer Benutzeroberfläche und reagieren am besten auf Feinheiten, die die reale Welt nachahmen. Wenn Nutzer beispielsweise ein UI-Objekt bewegen, vermitteln Sie ihnen ein Gefühl der Trägheit am Anfang, die die Bewegung verzögert. Vermitteln Sie ihnen am Ende der Bewegung ein Gefühl von Impuls, der das Objekt über den Ziehpunkt hinaus trägt.
Auf dieser Seite wird gezeigt, wie Sie Funktionen des Android-Frameworks verwenden können, um diese Verhaltensweisen aus der Praxis zu Ihrer benutzerdefinierten Ansicht hinzuzufügen.
Weitere zugehörige Informationen finden Sie unter Eingabeereignisse – Übersicht und Übersicht über die Property-Animation.
Eingabegesten verarbeiten
Wie viele andere UI-Frameworks unterstützt Android ein Eingabeereignismodell. Nutzeraktionen werden zu Ereignissen, die Callbacks auslösen. Sie können die 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 zur Verarbeitung des Ereignisses wie folgt:
Kotlin
override fun onTouchEvent(event: MotionEvent): Boolean { return super.onTouchEvent(event) }
Java
@Override public boolean onTouchEvent(MotionEvent event) { return super.onTouchEvent(event); }
Touch-Ereignisse 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
. Dazu übergeben Sie eine Instanz einer Klasse, die GestureDetector.OnGestureListener
implementiert.
Wenn Sie nur wenige Touch-Gesten verarbeiten möchten, können Sie GestureDetector.SimpleOnGestureListener
erweitern, statt die GestureDetector.OnGestureListener
-Schnittstelle 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, implementieren Sie immer eine onDown()
-Methode, die true
zurückgibt. Das ist erforderlich, da alle Touch-Gesten mit einer onDown()
-Nachricht beginnen. Wenn Sie wie bei GestureDetector.SimpleOnGestureListener
false
von onDown()
zurückgeben, geht das System davon aus, dass Sie den Rest der Touch-Geste ignorieren möchten. Die anderen Methoden von GestureDetector.OnGestureListener
werden nicht aufgerufen. Geben Sie false
nur dann von onDown()
zurück, wenn Sie eine ganze Geste ignorieren möchten.
Nachdem Sie GestureDetector.OnGestureListener
implementiert und eine Instanz von GestureDetector
erstellt haben, können Sie mit GestureDetector
die Touch-Ereignisse interpretieren, die Sie in onTouchEvent()
empfangen.
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 Sie ein Touch-Ereignis an onTouchEvent()
übergeben, das nicht als Teil einer Geste erkannt wird, wird false
zurückgegeben. Sie können dann Ihren eigenen benutzerdefinierten Code zur Gestenerkennung ausführen.
Physikalische Bewegung erstellen
Gesten sind eine wirkungsvolle Möglichkeit, Geräte mit Touchscreen zu steuern. Sie sind jedoch oft konstruktiv und schwer zu merken, es sei denn, sie liefern physisch plausible Ergebnisse.
Angenommen, Sie möchten eine horizontale Ziehbewegung implementieren, bei der sich das in der Ansicht gezeichnete Element um seine vertikale Achse dreht. Diese Geste ist sinnvoll, wenn die Benutzeroberfläche als Reaktion darauf reagiert, indem sie sich schnell in die Richtung des Schleuders bewegt und dann langsamer wird, als ob der Nutzer auf ein Flywheel drückt und es dreht.
In der Dokumentation zum Animieren einer Scrollgeste findest du eine ausführliche Erklärung dazu, wie du dein eigenes Scoll-Verhalten implementieren kannst. Es ist jedoch nicht einfach, das Gefühl eines Schwungrads zu simulieren. Damit ein Schwungradmodell richtig
funktioniert, ist viel Physik und Mathematik erforderlich. Glücklicherweise bietet Android Hilfsklassen, mit denen dieses und andere Verhaltensweisen simuliert werden können. Die Klasse Scroller
ist die Grundlage für den Umgang mit Flywheel-Gesten.
Um einen Schlingvorgang zu starten, rufen Sie fling()
mit der Startgeschwindigkeit und den minimalen und maximalen x- und y-Werten des Schleuders auf. Als 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; }
Mit dem Aufruf von fling()
wird das physikalische Modell für die fling-Geste eingerichtet. Aktualisieren Sie anschließend Scroller
, indem Sie in regelmäßigen Abständen Scroller.computeScrollOffset()
aufrufen. computeScrollOffset()
aktualisiert den internen Zustand des Scroller
-Objekts. Dazu wird die aktuelle Zeit gelesen und das physikalische Modell 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.
In diesem Beispiel wird der Drehwinkel der Ansicht anhand der aktuellen x-Scrollposition festgelegt.
Kotlin
scroller.apply { if (!isFinished) { computeScrollOffset() setItemRotation(currX) } }
Java
if (!scroller.isFinished()) { scroller.computeScrollOffset(); setItemRotation(scroller.getCurrX()); }
Die Klasse Scroller
berechnet die Scrollpositionen für Sie. Diese werden jedoch nicht automatisch auf Ihre Ansicht angewendet. Wenden Sie die neuen Koordinaten so oft an, dass die Scroll-Animation reibungslos wirkt. Dafür gibt es zwei Möglichkeiten:
- Erzwingen Sie eine Neuzeichnung, indem Sie nach dem Aufruf von
fling()
postInvalidate()
aufrufen. Bei diesem Verfahren müssen Sie den Scroll-Offset inonDraw()
berechnen undpostInvalidate()
jedes Mal aufrufen, wenn sich der Scroll-Offset ändert. - Richten Sie ein
ValueAnimator
ein, das für die Dauer des Ergebnisses animiert wird, und fügen Sie durch Aufrufen vonaddUpdateListener()
einen Listener hinzu, um Animationsaktualisierungen zu verarbeiten. Mit dieser Technik lassen sich die Eigenschaften vonView
animieren.
Für einen reibungslosen Übergang
Nutzer erwarten, dass eine moderne Benutzeroberfläche reibungslos zwischen Status wechselt: UI-Elemente werden ein- und ausgeblendet, anstatt ein- und ausgeblendet zu werden, und Bewegungen, die reibungslos beginnen und enden, statt plötzlich zu starten und zu stoppen. Das Android-Framework für Property-Animationen erleichtert den Wechsel.
Wenn Sie das Animationssystem verwenden möchten, sollten Sie die Eigenschaft nicht direkt ändern, wenn sich eine Eigenschaft ändert, die sich auf die Darstellung der 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 Basisattribute vom Typ View
ist, ist die Animation sogar noch einfacher, da Ansichten einen integrierten ViewPropertyAnimator
haben, der für die gleichzeitige Animation mehrerer Eigenschaften optimiert ist, wie im folgenden Beispiel gezeigt:
Kotlin
animate() .rotation(targetAngle) .duration = ANIM_DURATION .start()
Java
animate().rotation(targetAngle).setDuration(ANIM_DURATION).start();