Attributanimation – Übersicht

Schreiben Sie jetzt
Jetpack Compose ist das empfohlene UI-Toolkit für Android. Weitere Informationen zur Verwendung von Animationen in „Compose“

Das Eigenschaftsanimationssystem ist ein robustes Framework, mit dem Sie fast alles animieren können. Sie können eine Animation so definieren, dass jede Objekteigenschaft im Laufe der Zeit geändert wird, unabhängig davon, ob sie auf dem Bildschirm dargestellt wird oder nicht. Eine Eigenschaftsanimation ändert den Wert einer Eigenschaft (eines Felds in einem Objekt) über einen bestimmten Zeitraum. Um etwas zu animieren, geben Sie die Objekteigenschaft an, die animiert werden soll. Dazu gehört beispielsweise die Position eines Objekts auf dem Bildschirm, die Dauer der Animation und die Werte, zwischen denen die Animation erfolgen soll.

Mit dem Eigenschaftsanimationssystem können Sie die folgenden Eigenschaften einer Animation definieren:

  • Dauer: Sie können die Dauer einer Animation festlegen. Die Standardlänge beträgt 300 ms.
  • Zeitinterpolation: Sie können angeben, wie die Werte für die Eigenschaft in Abhängigkeit von der aktuell verstrichenen Zeit der Animation berechnet werden.
  • Wiederholungsanzahl und -verhalten: Sie können angeben, ob eine Animation nach Ablauf einer bestimmten Dauer wiederholt werden soll und wie oft die Animation wiederholt werden soll. Sie können auch angeben, ob die Animation rückwärts wiedergegeben werden soll. Ist sie festgelegt, wird die Animation wiederholt vor- und dann rückwärts abgespielt, bis die Anzahl der Wiederholungen erreicht ist.
  • Animationssets: Sie können Animationen in logischen Sätzen gruppieren, die zusammen oder nacheinander oder nach bestimmten Verzögerungen wiedergegeben werden.
  • Verzögerung für Frame-Aktualisierung: Sie können angeben, wie oft die Frames der Animation aktualisiert werden sollen. Standardmäßig ist eine Aktualisierung alle 10 ms festgelegt. Die Geschwindigkeit, mit der Ihre Anwendung Frames aktualisieren kann, hängt jedoch letztlich davon ab, wie ausgelastet das System insgesamt ist und wie schnell das System den zugrunde liegenden Timer bedienen kann.

Ein vollständiges Beispiel für eine Attributanimation finden Sie in der Klasse ChangeColor im Beispiel CustomTransition auf GitHub.

Funktionsweise von Property-Animationen

Sehen wir uns zunächst anhand eines einfachen Beispiels an, wie eine Animation funktioniert. Abbildung 1 zeigt ein hypothetisches Objekt, das mit seiner x-Eigenschaft animiert ist, die seine horizontale Position auf einem Bildschirm darstellt. Die Dauer der Animation wird auf 40 ms eingestellt und die zurückgelegte Strecke beträgt 40 Pixel. Alle 10 ms (Standard-Frame-Aktualisierungsrate) wird das Objekt um 10 Pixel horizontal verschoben. Nach 40 ms endet die Animation und das Objekt endet an der horizontalen Position 40. Dies ist ein Beispiel für eine Animation mit linearer Interpolation, d. h., das Objekt bewegt sich mit einer konstanten Geschwindigkeit.

Abbildung 1: Beispiel für eine lineare Animation

Sie können Animationen auch so definieren, dass sie eine nicht lineare Interpolation haben. In Abbildung 2 ist ein hypothetisches Objekt dargestellt, das zu Beginn und am Ende der Animation langsamer wird. Das Objekt bewegt sich in 40 ms immer noch um 40 Pixel, aber nicht linear. Am Anfang wird diese Animation bis zur Hälfte beschleunigt und dann ab der Hälfte der Strecke bis zum Ende der Animation langsamer. Wie Abbildung 2 zeigt, ist die zurückgelegte Strecke am Anfang und am Ende der Animation geringer als in der Mitte.

Abbildung 2: Beispiel für eine nicht lineare Animation

Sehen wir uns genauer an, wie die wichtigen Komponenten des Animationssystems für Eigenschaften Animationen wie die oben abgebildeten berechnen. In Abbildung 3 ist dargestellt, wie die Hauptklassen miteinander arbeiten.

Abbildung 3: Berechnung von Animationen

Das ValueAnimator-Objekt verfolgt den zeitlichen Ablauf der Animation, z. B. wie lange die Animation gelaufen ist und welchen Wert die Eigenschaft hat, die animiert wird.

ValueAnimator kapselt ein TimeInterpolator, das die Animationsinterpolation definiert, und ein TypeEvaluator, das definiert, wie die Werte für die animierte Eigenschaft berechnet werden. In Abbildung 2 wird beispielsweise TimeInterpolator als AccelerateDecelerateInterpolator und TypeEvaluator IntEvaluator verwendet.

Wenn Sie eine Animation starten möchten, erstellen Sie eine ValueAnimator und weisen Sie ihr die Start- und Endwerte für die zu animierende Eigenschaft sowie die Dauer der Animation zu. Wenn Sie start() aufrufen, beginnt die Animation. Während der gesamten Animation berechnet ValueAnimator anhand der Dauer der Animation und der verstrichenen Zeit einen verstrichenen Bruch zwischen 0 und 1. Der verstrichene Anteil stellt die Zeit in Prozent dar, die die Animation abgeschlossen ist. Dabei steht 0 für 0 % und 1 für 100%. In Abbildung 1 wäre beispielsweise der verstrichene Bruch bei t = 10 ms 0, 25, da die Gesamtdauer t = 40 ms beträgt.

Wenn der ValueAnimator mit der Berechnung eines verstrichenen Bruchs fertig ist, wird der aktuell festgelegte TimeInterpolator aufgerufen, um einen interpolierten Bruch zu berechnen. Ein interpolierter Anteil ordnet den verstrichenen Bruch einem neuen Bruch zu, der die festgelegte Zeitinterpolation berücksichtigt. Beispiel: Da die Animation in Abbildung 2 langsam beschleunigt wird, ist der interpolierte Bruch von etwa 0,15 kleiner als der verstrichene Bruch, 0,25, bei t = 10 ms. In Abbildung 1 ist der interpolierte Teil immer mit dem verstrichenen Bruch identisch.

Wenn der interpolierte Bruch berechnet wird, ruft ValueAnimator die entsprechende TypeEvaluator auf, um den Wert der Eigenschaft, die Sie animieren, basierend auf dem interpolierten Bruch sowie dem Start- und Endwert der Animation zu berechnen. In Abbildung 2 lag der interpolierte Bruch beispielsweise bei 0,15 bei t = 10 ms, sodass der Wert für die Eigenschaft zu diesem Zeitpunkt 0,15 × (40 - 0) oder 6 wäre.

Unterschied zwischen Eigenschaftsanimation und Ansichtsanimation

Das Ansichtsanimationssystem bietet die Möglichkeit, nur View-Objekte zu animieren. Wenn Sie also Nicht-View-Objekte animieren möchten, müssen Sie dazu Ihren eigenen Code implementieren. Das Ansichtsanimationssystem ist außerdem dadurch eingeschränkt, dass es nur einige Aspekte eines zu animierenden View-Objekts sichtbar macht, z. B. die Skalierung und Drehung einer Ansicht, aber nicht beispielsweise die Hintergrundfarbe.

Ein weiterer Nachteil des Ansichtsanimationssystems besteht darin, dass es nur dort geändert wurde, wo die Ansicht gezeichnet wurde, und nicht die eigentliche Ansicht selbst. Wenn Sie beispielsweise eine Schaltfläche animiert haben, um sich über den Bildschirm zu bewegen, wird die Schaltfläche korrekt gezeichnet. Die tatsächliche Position, an der Sie auf die Schaltfläche klicken können, ändert sich jedoch nicht. Sie müssen also eine eigene Logik dafür implementieren.

Mit dem Eigenschaftsanimationssystem werden diese Einschränkungen vollständig entfernt und Sie können jede Eigenschaft eines beliebigen Objekts (Views und Nicht-Views) animieren. Das Objekt selbst wird dann tatsächlich geändert. Das Eigenschaftsanimationssystem ist außerdem robuster in der Art und Weise, wie es Animationen durchführt. Grundsätzlich weisen Sie Animatoren den zu animierenden Eigenschaften zu, zum Beispiel Farbe, Position oder Größe, und können Aspekte der Animation definieren, z. B. Interpolation und Synchronisierung mehrerer Animatoren.

Das Ansichtsanimationssystem nimmt jedoch weniger Zeit für die Einrichtung in Anspruch und erfordert weniger Code zum Schreiben. Wenn mit der Ansichtsanimation alles erledigt wird oder der vorhandene Code bereits wie gewünscht funktioniert, muss das Property-Animationssystem nicht verwendet werden. Es kann auch sinnvoll sein, bei unterschiedlichen Anwendungsfällen beide Animationssysteme zu verwenden.

API-Übersicht

Die meisten APIs des Property-Animationssystems finden Sie in android.animation. Da das Ansichtsanimationssystem bereits viele Interpolatoren in android.view.animation definiert, können Sie diese Interpolatoren auch im Eigenschaftsanimationssystem verwenden. In den folgenden Tabellen werden die Hauptkomponenten des Eigenschaftsanimationssystems beschrieben.

Die Klasse Animator bietet die Grundstruktur zum Erstellen von Animationen. Sie verwenden diese Klasse normalerweise nicht direkt, da sie nur minimale Funktionalität bietet, die erweitert werden muss, um Animationswerte vollständig zu unterstützen. Die folgenden abgeleiteten Klassen erweitern Animator:

Tabelle 1 Animatoren

Klasse Beschreibung
ValueAnimator Das Haupt-Timing-Modul für die Eigenschaftsanimation, das auch die Werte für die zu animierende Eigenschaft berechnet. Sie verfügt über alle Kernfunktionen zum Berechnen von Animationswerten und enthält die Zeitdetails jeder Animation, Informationen zur Wiederholung einer Animation, Listener, die Aktualisierungsereignisse empfangen, und die Möglichkeit, benutzerdefinierte Typen für die Auswertung festzulegen. Die Animation von Eigenschaften besteht aus zwei Teilen: dem Berechnen der animierten Werte und dem Festlegen dieser Werte für das zu animierende Objekt und die zu animierende Eigenschaft. ValueAnimator übernimmt den zweiten Teil nicht. Sie müssen also auf Aktualisierungen der von ValueAnimator berechneten Werte warten und die Objekte ändern, die mit Ihrer eigenen Logik animiert werden sollen. Weitere Informationen finden Sie unter Animation mit ValueAnimator.
ObjectAnimator Eine abgeleitete Klasse von ValueAnimator, mit der Sie ein Zielobjekt und eine Objekteigenschaft für die Animation festlegen können. Diese Klasse aktualisiert das Attribut entsprechend, wenn ein neuer Wert für die Animation berechnet wird. Sie sollten ObjectAnimator meistens verwenden, da sich dadurch die Animation von Werten in Zielobjekten viel einfacher gestaltet. Manchmal ist es jedoch sinnvoll, ValueAnimator direkt zu verwenden, da für ObjectAnimator einige weitere Einschränkungen gelten, z. B. dass bestimmte Zugriffsmethoden für das Zielobjekt vorhanden sein müssen.
AnimatorSet Bietet einen Mechanismus, um Animationen so zu gruppieren, dass sie im Verhältnis zueinander ausgeführt werden. Sie können Animationen so festlegen, dass sie zusammen, nacheinander oder nach einer bestimmten Verzögerung wiedergegeben werden. Weitere Informationen finden Sie im Abschnitt Choreografen mehrerer Animationen mit Animator-Sets.

Evaluatoren teilen dem Animationssystem von Properties mit, wie die Werte für eine bestimmte Property berechnet werden sollen. Sie verwenden die von einer Animator-Klasse bereitgestellten Zeitdaten sowie den Start- und Endwert der Animation und berechnen die animierten Werte der Eigenschaft anhand dieser Daten. Im Animationssystem für Unterkünfte stehen die folgenden Bewerter zur Verfügung:

Tabelle 2: Evaluierende

Klasse/Schnittstelle Beschreibung
IntEvaluator Der Standard-Evaluator zur Berechnung von Werten für int-Properties.
FloatEvaluator Der Standard-Evaluator zur Berechnung von Werten für float-Properties.
ArgbEvaluator Der Standardauswerter zur Berechnung von Werten für Farbeigenschaften, die als Hexadezimalwerte dargestellt werden.
TypeEvaluator Eine Schnittstelle, mit der Sie Ihren eigenen Evaluator erstellen können. Wenn Sie eine Objekteigenschaft animieren, die kein int, float oder eine Farbe ist, müssen Sie die TypeEvaluator-Schnittstelle implementieren, um anzugeben, wie die animierten Werte der Objekteigenschaft berechnet werden sollen. Sie können auch eine benutzerdefinierte TypeEvaluator für int-, float- und Farbwerte angeben, wenn Sie diese Typen anders als das Standardverhalten verarbeiten möchten. Weitere Informationen zum Schreiben eines benutzerdefinierten Evaluators finden Sie im Abschnitt TypeEvaluator verwenden.

Ein Zeitinterpolator definiert, wie bestimmte Werte in einer Animation als Funktion der Zeit berechnet werden. Sie können z. B. festlegen, dass Animationen linear über die gesamte Animation laufen, d. h. gleichmäßig über die gesamte Zeit hinweg bewegt werden, oder für Animationen eine nicht lineare Zeit festlegen, z. B. eine Beschleunigung am Anfang und eine Verlangsamung am Ende der Animation. In Tabelle 3 werden die Interpolatoren beschrieben, die in android.view.animation enthalten sind. Wenn keiner der verfügbaren Interpolatoren Ihren Anforderungen entspricht, implementieren Sie die TimeInterpolator-Schnittstelle und erstellen Sie eine eigene. Weitere Informationen zum Schreiben eines benutzerdefinierten Interpolators finden Sie unter Interpolatoren verwenden.

Tabelle 3 Interpolatoren

Klasse/Schnittstelle Beschreibung
AccelerateDecelerateInterpolator Interpolator, deren Änderungsrate langsam beginnt und endet, aber durch die Mitte beschleunigt wird.
AccelerateInterpolator Interpolator, dessen Änderungsrate langsam beginnt und dann beschleunigt wird.
AnticipateInterpolator Interpolator, dessen Änderung rückwärts beginnt und dann nach vorne geschoben wird.
AnticipateOvershootInterpolator Ein Interpolator, dessen Änderung rückwärts beginnt, vorwärts wirft, den Zielwert überschreitet und dann schließlich zum Endwert zurückkehrt.
BounceInterpolator Ein Interpolator, dessen Änderung am Ende zurückspringt.
CycleInterpolator Interpolator, deren Animation für eine bestimmte Anzahl von Zyklen wiederholt wird.
DecelerateInterpolator Interpolator, deren Änderungsrate schnell beginnt und dann abnimmt.
LinearInterpolator Ein Interpolator, dessen Änderungsrate konstant ist.
OvershootInterpolator Interpolator, dessen Änderung nach vorne wirft, den letzten Wert überschreitet und dann wieder zurückkehrt.
TimeInterpolator Eine Schnittstelle, mit der Sie Ihren eigenen Interpolator implementieren können.

Animationen mit ValueAnimator

Mit der Klasse ValueAnimator können Sie Werte eines Typs für die Dauer einer Animation animieren. Dazu geben Sie eine Gruppe von int-, float- oder Farbwerten an, durch die animiert werden soll. Sie erhalten ein ValueAnimator, indem Sie eine der Factory-Methoden aufrufen: ofInt(), ofFloat() oder ofObject(). Beispiel:

Kotlin

ValueAnimator.ofFloat(0f, 100f).apply {
    duration = 1000
    start()
}

Java

ValueAnimator animation = ValueAnimator.ofFloat(0f, 100f);
animation.setDuration(1000);
animation.start();

In diesem Code beginnt ValueAnimator mit der Berechnung der Werte der Animation zwischen 0 und 100 für eine Dauer von 1.000 ms, wenn die Methode start() ausgeführt wird.

Sie können auch einen benutzerdefinierten Typ für die Animation angeben. Gehen Sie dazu folgendermaßen vor:

Kotlin

ValueAnimator.ofObject(MyTypeEvaluator(), startPropertyValue, endPropertyValue).apply {
    duration = 1000
    start()
}

Java

ValueAnimator animation = ValueAnimator.ofObject(new MyTypeEvaluator(), startPropertyValue, endPropertyValue);
animation.setDuration(1000);
animation.start();

In diesem Code beginnt ValueAnimator mit der Berechnung der Werte der Animation zwischen startPropertyValue und endPropertyValue. Dazu wird die von MyTypeEvaluator für eine Dauer von 1.000 ms bereitgestellte Logik verwendet, wenn die Methode start() ausgeführt wird.

Sie können die Werte der Animation verwenden, indem Sie dem ValueAnimator-Objekt ein AnimatorUpdateListener-Objekt hinzufügen, wie im folgenden Code gezeigt:

Kotlin

ValueAnimator.ofObject(...).apply {
    ...
    addUpdateListener { updatedAnimation ->
        // You can use the animated value in a property that uses the
        // same type as the animation. In this case, you can use the
        // float value in the translationX property.
        textView.translationX = updatedAnimation.animatedValue as Float
    }
    ...
}

Java

animation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator updatedAnimation) {
        // You can use the animated value in a property that uses the
        // same type as the animation. In this case, you can use the
        // float value in the translationX property.
        float animatedValue = (float)updatedAnimation.getAnimatedValue();
        textView.setTranslationX(animatedValue);
    }
});

Mit der Methode onAnimationUpdate() können Sie auf den aktualisierten Animationswert zugreifen und ihn in einer Property einer Ihrer Ansichten verwenden. Weitere Informationen zu Listenern finden Sie im Abschnitt zu Animations-Listenern.

Mit ObjectAnimator animieren

ObjectAnimator ist eine abgeleitete Klasse von ValueAnimator, die im vorherigen Abschnitt erläutert wurde. Sie kombiniert die Timing-Engine und die Wertberechnung von ValueAnimator mit der Möglichkeit, eine benannte Eigenschaft eines Zielobjekts zu animieren. Dadurch wird das Animieren von Objekten viel einfacher, da ValueAnimator.AnimatorUpdateListener nicht mehr implementiert werden muss, da die animierte Eigenschaft automatisch aktualisiert wird.

Das Instanziieren eines ObjectAnimator ähnelt einem ValueAnimator, Sie geben jedoch zusätzlich das Objekt und den Namen der Eigenschaft dieses Objekts (als String) sowie die Werte an, zwischen denen animiert werden soll:

Kotlin

ObjectAnimator.ofFloat(textView, "translationX", 100f).apply {
    duration = 1000
    start()
}

Java

ObjectAnimator animation = ObjectAnimator.ofFloat(textView, "translationX", 100f);
animation.setDuration(1000);
animation.start();

Damit die ObjectAnimator-Attribute korrekt aktualisiert werden, musst du Folgendes tun:

  • Die Objekteigenschaft, die Sie animieren, muss eine Setter-Funktion (im Camel-Case-Schreibweise) im Format set<PropertyName>() haben. Da ObjectAnimator das Attribut während der Animation automatisch aktualisiert, muss es in der Lage sein, mit dieser Setter-Methode auf die Property zuzugreifen. Lautet der Attributname beispielsweise foo, benötigen Sie die Methode setFoo(). Wenn diese Setter-Methode nicht vorhanden ist, haben Sie drei Möglichkeiten:
    • Fügen Sie die Setter-Methode zur Klasse hinzu, wenn Sie über die entsprechenden Rechte verfügen.
    • Verwenden Sie eine Wrapper-Klasse, zu deren Änderung Sie berechtigt sind, und sorgen Sie dafür, dass dieser Wrapper den Wert mit einer gültigen Setter-Methode empfängt und an das ursprüngliche Objekt weiterleitet.
    • Verwende stattdessen ValueAnimator.
  • Wenn Sie nur einen Wert für den Parameter values... in einer der ObjectAnimator-Factory-Methoden angeben, wird davon ausgegangen, dass es sich um den Endwert der Animation handelt. Daher muss die Objekteigenschaft, die Sie animieren, eine Getter-Funktion haben, mit der der Startwert der Animation abgerufen wird. Die Getter-Funktion muss das Format get<PropertyName>() haben. Beispiel: Wenn der Attributname foo lautet, benötigen Sie eine getFoo()-Methode.
  • Die Getter- und Setter-Methoden des zu animierenden Attributs müssen bei Bedarf auf denselben Typ angewendet werden wie die Start- und Endwerte, die Sie für ObjectAnimator angeben. Beispiel: Sie benötigen targetObject.setPropName(float) und targetObject.getPropName(), wenn Sie die folgende ObjectAnimator erstellen:
    ObjectAnimator.ofFloat(targetObject, "propName", 1f)
    
  • Je nach Eigenschaft oder Objekt, das Sie animieren, müssen Sie möglicherweise die Methode invalidate() für eine Ansicht aufrufen, damit der Bildschirm mit den aktualisierten animierten Werten neu gezeichnet wird. Dazu verwendest du den onAnimationUpdate()-Callback. Beispielsweise führt das Animieren der Farbeigenschaft eines Drawable-Objekts nur dann zu Aktualisierungen des Bildschirms, wenn dieses Objekt sich selbst neu zeichnet. Alle Property-Setter in der Ansicht, z. B. setAlpha() und setTranslationX(), entwerten die Ansicht ordnungsgemäß. Sie müssen die Ansicht also nicht entwerten, wenn Sie diese Methoden mit neuen Werten aufrufen. Weitere Informationen zu Listenern finden Sie im Abschnitt zu Animations-Listenern.

Mit einem AnimatorSet mehrere Animationen choreografieren

In vielen Fällen möchten Sie eine Animation abspielen, die davon abhängt, wann eine andere Animation beginnt oder endet. Im Android-System kannst du Animationen in einer AnimatorSet bündeln, um anzugeben, ob die Animationen gleichzeitig, nacheinander oder nach einer bestimmten Verzögerung gestartet werden sollen. Sie können AnimatorSet-Objekte auch ineinander verschachteln.

Mit dem folgenden Code-Snippet werden die folgenden Animator-Objekte wie folgt wiedergegeben:

  1. bounceAnim wird abgespielt.
  2. squashAnim1, squashAnim2, stretchAnim1 und stretchAnim2 werden gleichzeitig wiedergegeben.
  3. bounceBackAnim wird abgespielt.
  4. fadeAnim wird abgespielt.

Kotlin

val bouncer = AnimatorSet().apply {
    play(bounceAnim).before(squashAnim1)
    play(squashAnim1).with(squashAnim2)
    play(squashAnim1).with(stretchAnim1)
    play(squashAnim1).with(stretchAnim2)
    play(bounceBackAnim).after(stretchAnim2)
}
val fadeAnim = ObjectAnimator.ofFloat(newBall, "alpha", 1f, 0f).apply {
    duration = 250
}
AnimatorSet().apply {
    play(bouncer).before(fadeAnim)
    start()
}

Java

AnimatorSet bouncer = new AnimatorSet();
bouncer.play(bounceAnim).before(squashAnim1);
bouncer.play(squashAnim1).with(squashAnim2);
bouncer.play(squashAnim1).with(stretchAnim1);
bouncer.play(squashAnim1).with(stretchAnim2);
bouncer.play(bounceBackAnim).after(stretchAnim2);
ValueAnimator fadeAnim = ObjectAnimator.ofFloat(newBall, "alpha", 1f, 0f);
fadeAnim.setDuration(250);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.play(bouncer).before(fadeAnim);
animatorSet.start();

Animations-Listener

Mit den unten beschriebenen Listenern können Sie während der Dauer einer Animation auf wichtige Ereignisse warten.

  • Animator.AnimatorListener
  • ValueAnimator.AnimatorUpdateListener
    • onAnimationUpdate() wird bei jedem Frame der Animation aufgerufen. Wenn Sie dieses Ereignis abwarten, werden die von ValueAnimator während einer Animation generierten berechneten Werte verwendet. Wenn Sie den Wert verwenden möchten, fragen Sie das an das Ereignis übergebene ValueAnimator-Objekt ab, um den aktuellen animierten Wert mit der getAnimatedValue()-Methode abzurufen. Die Implementierung dieses Listeners ist erforderlich, wenn Sie ValueAnimator verwenden.

      Je nach Eigenschaft oder Objekt, das Sie animieren, müssen Sie möglicherweise invalidate() für eine Ansicht aufrufen, um den Bildschirmbereich mit den neuen animierten Werten neu zu zeichnen. Beispielsweise führt das Animieren der Farbeigenschaft eines Drawable-Objekts nur dann zu Aktualisierungen, wenn das Objekt von selbst neu gezeichnet wird. Alle Property-Setter in der Ansicht, z. B. setAlpha() und setTranslationX(), entwerten die Ansicht ordnungsgemäß. Sie müssen die Ansicht also nicht entwerten, wenn Sie diese Methoden mit neuen Werten aufrufen.

Wenn Sie nicht alle Methoden der Animator.AnimatorListener-Schnittstelle implementieren möchten, können Sie die Klasse AnimatorListenerAdapter erweitern, anstatt die Schnittstelle Animator.AnimatorListener zu implementieren. Die Klasse AnimatorListenerAdapter bietet leere Implementierungen der Methoden, die Sie überschreiben können.

Mit dem folgenden Code-Snippet wird beispielsweise ein AnimatorListenerAdapter nur für den onAnimationEnd()-Callback erstellt:

Kotlin

ObjectAnimator.ofFloat(newBall, "alpha", 1f, 0f).apply {
    duration = 250
    addListener(object : AnimatorListenerAdapter() {
        override fun onAnimationEnd(animation: Animator) {
            balls.remove((animation as ObjectAnimator).target)
        }
    })
}

Java

ValueAnimator fadeAnim = ObjectAnimator.ofFloat(newBall, "alpha", 1f, 0f);
fadeAnim.setDuration(250);
fadeAnim.addListener(new AnimatorListenerAdapter() {
public void onAnimationEnd(Animator animation) {
    balls.remove(((ObjectAnimator)animation).getTarget());
}

Layoutänderungen an ViewGroup-Objekten animieren

Das Eigenschaftsanimationssystem bietet die Möglichkeit, Änderungen an ViewGroup-Objekten zu animieren. Außerdem bietet es eine einfache Möglichkeit, View-Objekte selbst zu animieren.

Mit der Klasse LayoutTransition können Sie Layoutänderungen innerhalb einer ViewGroup animieren. Ansichten in einer ViewGroup können eine ein- und ausgeblendete Animation durchlaufen, wenn Sie sie einer ViewGroup hinzufügen oder daraus entfernen oder wenn Sie die Methode setVisibility() einer Ansicht mit VISIBLE, INVISIBLE oder GONE aufrufen. Die verbleibenden Ansichten in der ViewGroup können ebenfalls an ihre neue Position animiert werden, wenn Sie Ansichten hinzufügen oder entfernen. Sie können die folgenden Animationen in einem LayoutTransition-Objekt definieren. Dazu rufen Sie setAnimator() auf und übergeben ein Animator-Objekt mit einer der folgenden LayoutTransition-Konstanten:

  • APPEARING: Ein Flag, das die Animation angibt, die für Elemente ausgeführt wird, die im Container angezeigt werden.
  • CHANGE_APPEARING: Dieses Flag gibt die Animation an, die für Elemente ausgeführt wird, die sich aufgrund eines neuen Elements im Container ändern.
  • DISAPPEARING: Ein Flag, das die Animation für Elemente angibt, die aus dem Container verschwinden.
  • CHANGE_DISAPPEARING: Ein Flag, das die Animation für Elemente angibt, die sich ändern, weil ein Element aus dem Container verschwindet.

Sie können Ihre eigenen benutzerdefinierten Animationen für diese vier Ereignistypen definieren, um das Erscheinungsbild Ihrer Layoutübergänge anzupassen, oder das Animationssystem anweisen, die Standardanimationen zu verwenden.

So setzen Sie das Attribut android:animateLayoutchanges für die ViewGroup auf true:

<LinearLayout
    android:orientation="vertical"
    android:layout_width="wrap_content"
    android:layout_height="match_parent"
    android:id="@+id/verticalContainer"
    android:animateLayoutChanges="true" />

Wenn Sie dieses Attribut auf „true“ setzen, werden Ansichten, die der ViewGroup hinzugefügt oder daraus entfernt werden, sowie die verbleibenden Ansichten in der ViewGroup automatisch animiert.

Änderungen des Ansichtsstatus mit StateListAnimator animieren

Mit der Klasse StateListAnimator können Sie Animatoren definieren, die ausgeführt werden, wenn sich der Status einer Ansicht ändert. Dieses Objekt verhält sich wie ein Wrapper für ein Animator-Objekt und ruft diese Animation immer dann auf, wenn sich der angegebene Ansichtsstatus (z. B. „gedrückt“ oder „fokussiert“) ändert.

Das StateListAnimator kann in einer XML-Ressource mit einem Stammelement <selector> und untergeordneten <item>-Elementen definiert werden, die jeweils einen anderen Ansichtsstatus angeben, der durch die Klasse StateListAnimator definiert wird. Jedes <item> enthält die Definition für einen Eigenschaftsanimationssatz.

Die folgende Datei erstellt beispielsweise eine Statuslistenanimation, die die x- und y-Skalierung der Ansicht ändert, wenn sie gedrückt wird:

res/xml/animate_scale.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- the pressed state; increase x and y size to 150% -->
    <item android:state_pressed="true">
        <set>
            <objectAnimator android:propertyName="scaleX"
                android:duration="@android:integer/config_shortAnimTime"
                android:valueTo="1.5"
                android:valueType="floatType"/>
            <objectAnimator android:propertyName="scaleY"
                android:duration="@android:integer/config_shortAnimTime"
                android:valueTo="1.5"
                android:valueType="floatType"/>
        </set>
    </item>
    <!-- the default, non-pressed state; set x and y size to 100% -->
    <item android:state_pressed="false">
        <set>
            <objectAnimator android:propertyName="scaleX"
                android:duration="@android:integer/config_shortAnimTime"
                android:valueTo="1"
                android:valueType="floatType"/>
            <objectAnimator android:propertyName="scaleY"
                android:duration="@android:integer/config_shortAnimTime"
                android:valueTo="1"
                android:valueType="floatType"/>
        </set>
    </item>
</selector>

Wenn Sie den Animator der Liste der Bundesstaaten an eine Ansicht anhängen möchten, fügen Sie das Attribut android:stateListAnimator so hinzu:

<Button android:stateListAnimator="@xml/animate_scale"
        ... />

Wenn sich der Status dieser Schaltfläche ändert, werden jetzt die in animate_scale.xml definierten Animationen verwendet.

Wenn Sie stattdessen einer Ansicht in Ihrem Code einen Animator für Statuslisten zuweisen möchten, verwenden Sie die Methode AnimatorInflater.loadStateListAnimator() und weisen Sie den Animationsfilmer mit der Methode View.setStateListAnimator() der Ansicht zu.

Statt die Eigenschaften der Ansicht zu animieren, können Sie auch eine Drawable-Animation zwischen Statusänderungen mit AnimatedStateListDrawable abspielen. Einige System-Widgets in Android 5.0 verwenden diese Animationen standardmäßig. Das folgende Beispiel zeigt, wie eine AnimatedStateListDrawable als XML-Ressource definiert wird:

<!-- res/drawable/myanimstatedrawable.xml -->
<animated-selector
    xmlns:android="http://schemas.android.com/apk/res/android">

    <!-- provide a different drawable for each state-->
    <item android:id="@+id/pressed" android:drawable="@drawable/drawableP"
        android:state_pressed="true"/>
    <item android:id="@+id/focused" android:drawable="@drawable/drawableF"
        android:state_focused="true"/>
    <item android:id="@id/default"
        android:drawable="@drawable/drawableD"/>

    <!-- specify a transition -->
    <transition android:fromId="@+id/default" android:toId="@+id/pressed">
        <animation-list>
            <item android:duration="15" android:drawable="@drawable/dt1"/>
            <item android:duration="15" android:drawable="@drawable/dt2"/>
            ...
        </animation-list>
    </transition>
    ...
</animated-selector>

TypeEvaluator verwenden

Wenn Sie einen Typ animieren möchten, der dem Android-System nicht bekannt ist, können Sie einen eigenen Evaluator erstellen, indem Sie die TypeEvaluator-Schnittstelle implementieren. Die im Android-System bekannten Typen sind int, float oder eine Farbe, die von den Evaluators IntEvaluator, FloatEvaluator und ArgbEvaluator unterstützt werden.

In der TypeEvaluator-Schnittstelle gibt es nur eine Methode, die implementiert werden kann: die Methode evaluate(). So kann der von Ihnen verwendete Animator zum aktuellen Zeitpunkt der Animation einen geeigneten Wert für Ihre animierte Eigenschaft zurückgeben. Die Klasse FloatEvaluator zeigt, wie das geht:

Kotlin

private class FloatEvaluator : TypeEvaluator<Any> {

    override fun evaluate(fraction: Float, startValue: Any, endValue: Any): Any {
        return (startValue as Number).toFloat().let { startFloat ->
            startFloat + fraction * ((endValue as Number).toFloat() - startFloat)
        }
    }

}

Java

public class FloatEvaluator implements TypeEvaluator {

    public Object evaluate(float fraction, Object startValue, Object endValue) {
        float startFloat = ((Number) startValue).floatValue();
        return startFloat + fraction * (((Number) endValue).floatValue() - startFloat);
    }
}

Hinweis: Wenn ValueAnimator (oder ObjectAnimator) ausgeführt wird, wird ein aktuell verstrichener Teil der Animation berechnet (ein Wert zwischen 0 und 1) und dann je nach verwendetem Interpolator eine interpolierte Version davon berechnet. Der interpolierte Bruch ist das, was Ihr TypeEvaluator über den Parameter fraction empfängt. Daher müssen Sie bei der Berechnung animierter Werte den Interpolator nicht berücksichtigen.

Interpolatoren verwenden

Ein Interpolator definiert, wie bestimmte Werte in einer Animation als Zeitfunktion berechnet werden. Sie können beispielsweise festlegen, dass Animationen linear über die gesamte Animation laufen, d. h. die Animation gleichmäßig über die gesamte Zeit hinweg bewegt wird, oder Sie können Animationen so spezifizieren, dass eine nicht lineare Zeit verwendet wird, z. B. mit Beschleunigung oder Verzögerung zu Beginn oder am Ende der Animation.

Interpolatoren im Animationssystem erhalten einen Anteil von Animatoren, der die verstrichene Zeit der Animation darstellt. Interpolatoren ändern diesen Anteil so, dass er mit der Art der Animation übereinstimmt, die bereitgestellt werden soll. Das Android-System bietet eine Reihe von gemeinsamen Interpolatoren in der android.view.animation package. Wenn keine dieser Funktionen Ihren Anforderungen entspricht, können Sie die TimeInterpolator-Schnittstelle implementieren und eine eigene erstellen.

Im Folgenden wird zum Beispiel verglichen, wie der Standardinterpolator AccelerateDecelerateInterpolator und der LinearInterpolator interpolierte Brüche berechnen. LinearInterpolator hat keine Auswirkungen auf den verstrichenen Bruch. AccelerateDecelerateInterpolator beschleunigt die Animation und verlangsamt aus ihr heraus. Die folgenden Methoden definieren die Logik für diese Interpolatoren:

AccelerateDecelerateInterpolator

Kotlin

override fun getInterpolation(input: Float): Float =
        (Math.cos((input + 1) * Math.PI) / 2.0f).toFloat() + 0.5f

Java

@Override
public float getInterpolation(float input) {
    return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;
}

LinearInterpolator

Kotlin

override fun getInterpolation(input: Float): Float = input

Java

@Override
public float getInterpolation(float input) {
    return input;
}

Die folgende Tabelle enthält die Näherungswerte, die von diesen Interpolatoren für eine Animation mit einer Dauer von 1.000 ms berechnet werden:

ms verstrichen Verstrichener/interpolierter Bruch (linear) Interpolierter Bruch (beschleunigen/verlangsamen)
0 0 0
200 0,2 0,1
400 0,4 0,345
600 0,6 0,8
800 0,8 0,9
1.000 1 1

Wie die Tabelle zeigt, ändert LinearInterpolator die Werte mit der gleichen Geschwindigkeit, also 0,2 für alle 200 ms. AccelerateDecelerateInterpolator ändert die Werte schneller als LinearInterpolator zwischen 200 ms und 600 ms und langsamer zwischen 600 ms und 1.000 ms.

Keyframes festlegen

Ein Keyframe-Objekt besteht aus einem Zeit/Wert-Paar, mit dem du einen bestimmten Zustand zu einem bestimmten Zeitpunkt einer Animation definieren kannst. Jeder Keyframe kann auch einen eigenen Interpolator haben, um das Verhalten der Animation im Intervall zwischen der Zeit des vorherigen Keyframes und dem Zeitpunkt dieses Keyframes zu steuern.

Zum Instanziieren eines Keyframe-Objekts müssen Sie eine der Factory-Methoden ofInt(), ofFloat() oder ofObject() verwenden, um den entsprechenden Keyframe-Typ zu erhalten. Anschließend rufen Sie die ofKeyframe()-Factory-Methode auf, um ein PropertyValuesHolder-Objekt abzurufen. Wenn Sie das Objekt haben, erhalten Sie einen Animationsfilm, indem Sie das PropertyValuesHolder-Objekt und das zu animierende Objekt übergeben. Das folgende Code-Snippet zeigt, wie dies funktioniert:

Kotlin

val kf0 = Keyframe.ofFloat(0f, 0f)
val kf1 = Keyframe.ofFloat(.5f, 360f)
val kf2 = Keyframe.ofFloat(1f, 0f)
val pvhRotation = PropertyValuesHolder.ofKeyframe("rotation", kf0, kf1, kf2)
ObjectAnimator.ofPropertyValuesHolder(target, pvhRotation).apply {
    duration = 5000
}

Java

Keyframe kf0 = Keyframe.ofFloat(0f, 0f);
Keyframe kf1 = Keyframe.ofFloat(.5f, 360f);
Keyframe kf2 = Keyframe.ofFloat(1f, 0f);
PropertyValuesHolder pvhRotation = PropertyValuesHolder.ofKeyframe("rotation", kf0, kf1, kf2);
ObjectAnimator rotationAnim = ObjectAnimator.ofPropertyValuesHolder(target, pvhRotation);
rotationAnim.setDuration(5000);

Ansichten animieren

Das Eigenschaftsanimationssystem ermöglicht eine optimierte Animation von View-Objekten und bietet einige Vorteile gegenüber dem Ansichtsanimationssystem. Das Ansichtsanimationssystem transformierte View-Objekte durch Änderung der Art und Weise, wie sie gezeichnet wurden. Dies wurde im Container jeder Ansicht durchgeführt, da die Ansicht selbst keine Eigenschaften hatte, die geändert werden konnten. Dies führte dazu, dass die View animiert wurde, jedoch keine Änderung im View-Objekt selbst. Dies führte dazu, dass ein Objekt immer noch an seiner ursprünglichen Position existiert, obwohl es an einer anderen Stelle auf dem Bildschirm gezeichnet wurde. In Android 3.0 wurden neue Properties und die entsprechenden Getter- und Setter-Methoden hinzugefügt, um diesen Nachteil zu eliminieren.

Das Animationssystem für Eigenschaften kann Ansichten auf dem Bildschirm animieren, indem die tatsächlichen Eigenschaften in den View-Objekten geändert werden. Darüber hinaus wird in Views automatisch die Methode invalidate() aufgerufen, um den Bildschirm zu aktualisieren, wenn sich seine Eigenschaften ändern. Die neuen Properties in der View-Klasse, die Eigenschaftsanimationen vereinfachen, sind:

  • translationX und translationY: Mit diesen Eigenschaften wird festgelegt, wo sich die Ansicht als Delta von den linken und oberen Koordinaten befindet, die durch ihren Layoutcontainer festgelegt werden.
  • rotation, rotationX und rotationY: Mit diesen Eigenschaften wird die 2D-Drehung um den Drehpunkt in 2D (rotation-Eigenschaft) und 3D-Drehung gesteuert.
  • scaleX und scaleY: Mit diesen Eigenschaften wird die 2D-Skalierung einer Ansicht um ihren Drehpunkt herum festgelegt.
  • pivotX und pivotY: Diese Eigenschaften steuern die Position des Drehpunkts, um den die Transformationen für Rotation und Skalierung stattfinden. Standardmäßig befindet sich der Drehpunkt in der Mitte des Objekts.
  • x und y: Dies sind einfache Diensteigenschaften, mit denen die endgültige Position der Ansicht in ihrem Container beschrieben wird, als Summe der Werte "Links" und "Oben" sowie der Werte "translationX" und "translationY".
  • alpha: Stellt die Alphatransparenz in der Ansicht dar. Dieser Wert ist standardmäßig 1 (undurchsichtig), der Wert 0 steht für vollständige Transparenz (nicht sichtbar).

Zum Animieren einer Eigenschaft eines View-Objekts, etwa der Farbe oder des Rotationswerts, müssen Sie lediglich einen Eigenschaftsanimationer erstellen und die Ansichtseigenschaft angeben, die animiert werden soll. Beispiel:

Kotlin

ObjectAnimator.ofFloat(myView, "rotation", 0f, 360f)

Java

ObjectAnimator.ofFloat(myView, "rotation", 0f, 360f);

Weitere Informationen zum Erstellen von Animatoren finden Sie in den Abschnitten zur Animation mit ValueAnimator und ObjectAnimator.

Mit ViewPropertyAnimator animieren

ViewPropertyAnimator bietet eine einfache Möglichkeit, mehrere Eigenschaften einer View mithilfe eines einzelnen zugrunde liegenden Animator-Objekts gleichzeitig zu animieren. Sie verhält sich ähnlich wie ein ObjectAnimator, weil damit die tatsächlichen Werte der Attribute der Ansicht geändert werden. Bei gleichzeitiger Animation vieler Attribute ist es jedoch effizienter. Darüber hinaus ist der Code zur Verwendung von ViewPropertyAnimator viel kürzer und leichter zu lesen. Die folgenden Code-Snippets verdeutlichen die Unterschiede bei der Verwendung mehrerer ObjectAnimator-Objekte, eines einzelnen ObjectAnimator-Objekts und von ViewPropertyAnimator bei gleichzeitiger Animation der Attribute x und y einer Ansicht.

Mehrere ObjectAnimator-Objekte

Kotlin

val animX = ObjectAnimator.ofFloat(myView, "x", 50f)
val animY = ObjectAnimator.ofFloat(myView, "y", 100f)
AnimatorSet().apply {
    playTogether(animX, animY)
    start()
}

Java

ObjectAnimator animX = ObjectAnimator.ofFloat(myView, "x", 50f);
ObjectAnimator animY = ObjectAnimator.ofFloat(myView, "y", 100f);
AnimatorSet animSetXY = new AnimatorSet();
animSetXY.playTogether(animX, animY);
animSetXY.start();

One ObjectAnimator

Kotlin

val pvhX = PropertyValuesHolder.ofFloat("x", 50f)
val pvhY = PropertyValuesHolder.ofFloat("y", 100f)
ObjectAnimator.ofPropertyValuesHolder(myView, pvhX, pvhY).start()

Java

PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("x", 50f);
PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("y", 100f);
ObjectAnimator.ofPropertyValuesHolder(myView, pvhX, pvhY).start();

ViewPropertyAnimator

Kotlin

myView.animate().x(50f).y(100f)

Java

myView.animate().x(50f).y(100f);

Ausführliche Informationen zu ViewPropertyAnimator findest du im entsprechenden Blogpost für Android-Entwickler.

Animationen in XML deklarieren

Mit dem Eigenschaftsanimationssystem können Sie Eigenschaftsanimationen mit XML deklarieren, anstatt dies programmatisch durchzuführen. Wenn Sie Ihre Animationen in XML definieren, können Sie sie problemlos in mehreren Aktivitäten wiederverwenden und die Animationssequenz einfacher bearbeiten.

Um Animationsdateien, in denen die neuen APIs für Eigenschaftsanimationen verwendet werden, von denen zu unterscheiden, die das alte Framework für Animationen ansehen ab Android 3.1 verwenden, sollten Sie die XML-Dateien für Eigenschaftsanimationen im Verzeichnis res/animator/ speichern.

Für die folgenden Animationsklassen für Eigenschaften wird die XML-Deklaration mit den folgenden XML-Tags unterstützt:

Die Attribute, die Sie in der XML-Deklaration verwenden können, finden Sie unter Animationsressourcen. Im folgenden Beispiel werden die beiden Sätze von Objektanimationen nacheinander wiedergegeben, wobei der erste verschachtelte Satz zwei Objektanimationen zusammen abspielt:

<set android:ordering="sequentially">
    <set>
        <objectAnimator
            android:propertyName="x"
            android:duration="500"
            android:valueTo="400"
            android:valueType="intType"/>
        <objectAnimator
            android:propertyName="y"
            android:duration="500"
            android:valueTo="300"
            android:valueType="intType"/>
    </set>
    <objectAnimator
        android:propertyName="alpha"
        android:duration="500"
        android:valueTo="1f"/>
</set>

Zum Ausführen dieser Animation müssen Sie die XML-Ressourcen in Ihrem Code in ein AnimatorSet-Objekt aufblähen und dann die Zielobjekte für alle Animationen festlegen, bevor Sie den Animationssatz starten. Durch den Aufruf von setTarget() wird der Einfachheit halber ein einzelnes Zielobjekt für alle untergeordneten Elemente von AnimatorSet festgelegt. Der folgende Code zeigt, wie dies funktioniert:

Kotlin

(AnimatorInflater.loadAnimator(myContext, R.animator.property_animator) as AnimatorSet).apply {
    setTarget(myObject)
    start()
}

Java

AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(myContext,
    R.animator.property_animator);
set.setTarget(myObject);
set.start();

Sie können ein ValueAnimator auch in XML deklarieren, wie im folgenden Beispiel gezeigt:

<animator xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="1000"
    android:valueType="floatType"
    android:valueFrom="0f"
    android:valueTo="-100f" />

Wenn Sie das vorherige ValueAnimator-Objekt in Ihrem Code verwenden möchten, müssen Sie das Objekt aufblähen, ein AnimatorUpdateListener hinzufügen, den aktualisierten Animationswert abrufen und ihn in einem Attribut einer Ihrer Ansichten verwenden, wie im folgenden Code gezeigt:

Kotlin

(AnimatorInflater.loadAnimator(this, R.animator.animator) as ValueAnimator).apply {
    addUpdateListener { updatedAnimation ->
        textView.translationX = updatedAnimation.animatedValue as Float
    }

    start()
}

Java

ValueAnimator xmlAnimator = (ValueAnimator) AnimatorInflater.loadAnimator(this,
        R.animator.animator);
xmlAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator updatedAnimation) {
        float animatedValue = (float)updatedAnimation.getAnimatedValue();
        textView.setTranslationX(animatedValue);
    }
});

xmlAnimator.start();

Informationen zur XML-Syntax zum Definieren von Eigenschaftsanimationen finden Sie unter Animationsressourcen .

Mögliche Auswirkungen auf die Leistung der Benutzeroberfläche

Animatoren, die die UI aktualisieren, verursachen zusätzliche Rendering-Arbeiten für jeden Frame, in dem die Animation ausgeführt wird. Aus diesem Grund kann die Verwendung ressourcenintensiver Animationen die Leistung Ihrer Anwendung negativ beeinflussen.

Die zum Animieren der UI erforderliche Arbeit wird im Animationsphase der Rendering-Pipeline hinzugefügt. Sie können herausfinden, ob sich Ihre Animationen auf die Leistung Ihrer Anwendung auswirken, indem Sie das Profil-GPU-Rendering aktivieren und den Animationsstatus überwachen. Weitere Informationen finden Sie unter Schritt-für-Schritt-Anleitung für das Profil-GPU-Rendering.