Attributanimation – Übersicht

Funktion „Schreiben“ ausprobieren
Jetpack Compose ist das empfohlene UI-Toolkit für Android. Hier erfahren Sie, wie Sie Animationen in Compose verwenden.

Das Property-Animationssystem ist ein robustes Framework, mit dem Sie fast alles animieren können. Sie können eine Animation definieren, um eine Objekteigenschaft im Zeitverlauf zu ändern, unabhängig davon, ob sie auf dem Bildschirm dargestellt wird oder nicht. Bei einer Eigenschaftsanimation wird der Wert einer Eigenschaft (ein Feld in einem Objekt) über einen bestimmten Zeitraum geändert. Zum Animieren geben Sie die Objekteigenschaft an, die Sie animieren möchten, z. B. seine Position auf dem Bildschirm, die Dauer der Animation und die Werte, zwischen denen animiert werden 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 als Funktion der aktuell verstrichenen Zeit der Animation berechnet werden.
  • Wiederholungsanzahl und -verhalten: Sie können festlegen, ob eine Animation am Ende einer bestimmten Dauer wiederholt werden soll und wie oft die Animation wiederholt werden soll. Du kannst auch angeben, ob die Animation rückwärts abgespielt werden soll. Wenn Sie sie auf „Zurück“ einstellen, wird die Animation wiederholt vor und zurück wiedergegeben, bis die Anzahl der Wiederholungen erreicht ist.
  • Animator-Sets: Sie können Animationen in logischen Sätzen gruppieren, die zusammen oder nacheinander oder nach einer bestimmten Verzögerung abgespielt werden.
  • Verzögerung für Frame-Aktualisierung: Sie können angeben, wie oft die Frames Ihrer Animation aktualisiert werden sollen. Standardmäßig wird alle 10 ms eine Aktualisierung durchgeführt. Die Geschwindigkeit, mit der Ihre App Frames aktualisieren kann, hängt letztendlich davon ab, wie ausgelastet das System insgesamt ist und wie schnell das System den zugrunde liegenden Timer verarbeiten kann.

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

Funktionsweise der Property-Animation

Sehen wir uns zunächst anhand eines einfachen Beispiels an, wie eine Animation funktioniert. In Abbildung 1 ist ein hypothetisches Objekt zu sehen, das mithilfe der Eigenschaft x animiert wird, die seine horizontale Position auf einem Bildschirm darstellt. Die Dauer der Animation ist auf 40 ms und die zurückzulegende Strecke 40 Pixel eingestellt. Alle 10 ms, was der Standard-Frame-Aktualisierungsrate entspricht, verschiebt sich das Objekt horizontal um 10 Pixel. 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, bei der sich das Objekt mit einer konstanten Geschwindigkeit bewegt.

Abbildung 1: Beispiel für eine lineare Animation

Sie können auch Animationen mit einer nicht linearen Interpolation festlegen. In Abbildung 2 sehen Sie ein hypothetisches Objekt, das zu Beginn der Animation beschleunigt und am Ende der Animation langsamer wird. Das Objekt bewegt sich in 40 ms immer noch um 40 Pixel, aber nicht linear. Zu Beginn wird die Animation bis auf die Hälfte beschleunigt und wird dann von der Hälfte bis zum Ende der Animation verzögert. Wie Abbildung 2 zeigt, sind die zu Beginn und Ende der Animation zurückgelegten Strecken geringer als in der Mitte.

Abbildung 2: Beispiel für eine nicht lineare Animation

Sehen wir uns genau an, wie die wichtigen Komponenten des Property-Animationssystems Animationen wie die oben dargestellten berechnen würden. In Abbildung 3 ist dargestellt, wie die Hauptklassen miteinander funktionieren.

Abbildung 3: So werden Animationen berechnet

Das ValueAnimator-Objekt erfasst das Timing der Animation, z. B. die Dauer der Animation und den aktuellen Wert der Eigenschaft, die animiert wird.

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

Um eine Animation zu starten, erstellen Sie ein ValueAnimator-Objekt und weisen ihm 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 basierend auf der Dauer der Animation und der verstrichenen Zeit einen verstrichenen Anteil zwischen 0 und 1. Der Anteil der verstrichenen Zeit gibt an, wie lange die Animation bereits abgeschlossen ist. 0 bedeutet 0 % und 1 100%. In Abbildung 1 wäre der verstrichene Anteil bei t = 10 ms beispielsweise 0, 25, da die Gesamtdauer t = 40 ms beträgt.

Wenn der ValueAnimator einen verstrichenen Bruchteil berechnet hat, wird der aktuell festgelegte Anteil TimeInterpolator aufgerufen, um einen interpolierten Bruchteil zu berechnen. Bei einem interpolierten Bruch wird der verstrichene Bruch einem neuen Anteil zugeordnet, der die festgelegte Zeitinterpolation berücksichtigt. Da die Animation beispielsweise langsam beschleunigt wird, ist der interpolierte Anteil von etwa 0,15 bei t = 10 ms kleiner als der verstrichene Anteil (0,25). In Abbildung 1 entspricht der interpolierte Anteil immer dem verstrichenen Anteil.

Wenn der interpolierte Bruch berechnet wird, ruft ValueAnimator den entsprechenden TypeEvaluator auf, um den Wert der Eigenschaft, die Sie animieren, anhand des interpolierten Bruchs, des Startwerts und des Endwerts der Animation zu berechnen. In Abbildung 2 betrug der interpolierte Bruch 0,15 bei t = 10 ms. Der Wert für die Eigenschaft zu diesem Zeitpunkt wäre also 0,15 × (40 - 0) oder 6.

Unterschied zwischen Property-Animation 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 Animationssystem für Ansichten ist außerdem dadurch eingeschränkt, dass es nur einige Aspekte eines zu animierenden View-Objekts zur Verfügung stellt, z. B. die Skalierung und Drehung einer Ansicht, aber nicht die Hintergrundfarbe.

Ein weiterer Nachteil des Ansichtsanimationssystems besteht darin, dass es nur an der Stelle geändert wird, an der die Ansicht gezeichnet wurde, und nicht die tatsächliche Ansicht selbst. Wenn du beispielsweise eine Schaltfläche so animiert hast, dass sie sich über den Bildschirm bewegt, wird sie korrekt gezeichnet, aber die tatsächliche Stelle, an der du auf die Schaltfläche klicken kann, ändert sich nicht, sodass du deine eigene Logik implementieren musst, um dies zu handhaben.

Mit dem Eigenschaftsanimationssystem werden diese Einschränkungen vollständig entfernt. Sie können jede Eigenschaft eines beliebigen Objekts (Ansichten und Nicht-Ansichten) animieren, wobei das Objekt selbst tatsächlich geändert wird. Das Animationssystem für Properties ist außerdem robuster in der Art und Weise, wie Animationen ausgeführt werden. Auf übergeordneter Ebene weisen Sie den Eigenschaften, die Sie animieren möchten (z. B. Farbe, Position oder Größe), Animatoren zu. So können Sie Aspekte der Animation definieren, z. B. die Interpolation und Synchronisierung mehrerer Animatoren.

Das Ansichtsanimationssystem ist jedoch schneller eingerichtet und erfordert weniger Code zum Schreiben. Wenn die Ansichtsanimation alles erledigt, was Sie tun müssen, oder wenn Ihr vorhandener Code bereits wie gewünscht funktioniert, müssen Sie das Property-Animationssystem nicht verwenden. Außerdem kann es sinnvoll sein, beide Animationssysteme für verschiedene Situationen zu verwenden, wenn der Anwendungsfall eintritt.

API-Übersicht

Sie finden die meisten APIs des Property-Animationssystems unter 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 Property-Animationssystems beschrieben.

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

Tabelle 1 Animatoren

Klasse Beschreibung
ValueAnimator Die Haupt-Timing-Engine für die Eigenschaftsanimation, die auch die Werte für die zu animierende Eigenschaft berechnet. Sie umfasst alle Hauptfunktionen zum Berechnen von Animationswerten und enthält die Zeitdetails jeder Animation, Informationen darüber, ob sich eine Animation wiederholt, Listener, die Aktualisierungsereignisse empfangen, und die Möglichkeit, benutzerdefinierte Typen zur Auswertung festzulegen. Bei der Animation von Eigenschaften sind zwei Schritte erforderlich: Sie berechnen die animierten Werte und legen diese Werte für das Objekt und die Eigenschaft fest, die animiert werden. ValueAnimator übernimmt den zweiten Teil nicht. Daher müssen Sie auf Aktualisierungen von Werten warten, die von ValueAnimator berechnet wurden, und die Objekte, die Sie animieren möchten, mit Ihrer eigenen Logik ändern. Weitere Informationen finden Sie im Abschnitt zu Animationen mit ValueAnimator.
ObjectAnimator Eine abgeleitete Klasse von ValueAnimator, mit der Sie ein Zielobjekt und eine Objekteigenschaft animieren können. Diese Klasse aktualisiert die Eigenschaft entsprechend, wenn sie einen neuen Wert für die Animation berechnet. In der Regel sollten Sie ObjectAnimator verwenden, da die Animation von Werten in Zielobjekten damit wesentlich einfacher wird. Gelegentlich möchten Sie ValueAnimator jedoch direkt verwenden, da für ObjectAnimator weitere Einschränkungen gelten. So müssen z. B. bestimmte Zugriffsmethoden auf dem Zielobjekt vorhanden sein.
AnimatorSet Bietet einen Mechanismus zur Gruppierung von Animationen, sodass sie im Verhältnis zueinander ausgeführt werden. Sie können festlegen, dass Animationen zusammen, nacheinander oder nach einer bestimmten Verzögerung abgespielt werden sollen. Weitere Informationen finden Sie im Abschnitt Mehrere Animationen mit Animator-Sets choreografieren.

Evaluatoren teilen dem Property-Animationssystem mit, wie die Werte für eine bestimmte Property berechnet werden sollen. Anhand der von einer Animator-Klasse bereitgestellten Zeitdaten sowie des Start- und Endwerts der Animation werden die animierten Werte der Eigenschaft basierend auf diesen Daten berechnet. Das Animationssystem für Eigenschaften bietet die folgenden Bewerter:

Tabelle 2 Evaluierende

Klasse/Benutzeroberfläche Beschreibung
IntEvaluator Standard-Evaluator zum Berechnen von Werten für int-Properties.
FloatEvaluator Standard-Evaluator zum Berechnen von Werten für float-Properties.
ArgbEvaluator Standardauswerter zum Berechnen von Werten für Farbeigenschaften, die als Hexadezimalwerte dargestellt werden.
TypeEvaluator Benutzeroberfläche, über die Sie Ihren eigenen Bewerter erstellen können Wenn Sie eine Objekteigenschaft animieren, die keine int-, float- oder 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 beispielsweise festlegen, dass Animationen linear über die gesamte Animation laufen, sodass sie sich gleichmäßig über die gesamte Zeit hinweg bewegt. Sie können auch Animationen festlegen, die eine nicht lineare Zeit verwenden, wie etwa die Beschleunigung am Anfang und die Verlangsamung am Ende der Animation. In Tabelle 3 werden die in android.view.animation enthaltenen Interpolatoren beschrieben. Wenn keiner der verfügbaren Interpolatoren Ihren Anforderungen entspricht, implementieren Sie die TimeInterpolator-Schnittstelle und erstellen Sie Ihre eigene. Weitere Informationen zum Schreiben eines benutzerdefinierten Interpolators finden Sie unter Interpolatoren verwenden.

Tabelle 3 Interpolatoren

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

Mit ValueAnimator Animationen erstellen

Mit der Klasse ValueAnimator können Sie Werte eines beliebigen Typs für die Dauer einer Animation animieren. Dazu geben Sie eine Reihe von int-, float- oder Farbwerten an, die animiert werden sollen. Um eine ValueAnimator zu erhalten, rufen Sie eine ihrer Factory-Methoden auf: 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 zu animierenden Typ angeben. Gehen Sie dazu so 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 unter Verwendung der von MyTypeEvaluator bereitgestellten Logik für eine Dauer von 1.000 ms, wenn die Methode start() ausgeführt wird.

Sie können die Werte der Animation verwenden, indem Sie dem ValueAnimator-Objekt ein AnimatorUpdateListener 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);
    }
});

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

Mit ObjectAnimator Animationen erstellen

ObjectAnimator ist eine abgeleitete Klasse von ValueAnimator (im vorherigen Abschnitt erläutert) und kombiniert die Timing-Engine und die Wertberechnung von ValueAnimator mit der Möglichkeit, eine benannte Eigenschaft eines Zielobjekts zu animieren. Das macht das Animieren jedes Objekts viel einfacher, da Sie ValueAnimator.AnimatorUpdateListener nicht mehr implementieren müssen, da die animierte Eigenschaft automatisch aktualisiert wird.

Die Instanziierung eines ObjectAnimator ist mit einem ValueAnimator vergleichbar. Allerdings geben Sie auch das Objekt und den Namen seiner Eigenschaft (als String) zusammen mit den Werten 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-Aktualisierungsattribute korrekt sind, müssen Sie Folgendes tun:

  • Die Objekteigenschaft, die Sie animieren, muss eine Setter-Funktion (im Camel-Case-Schreibweise) in der Form set<PropertyName>() haben. Da ObjectAnimator die Eigenschaft während der Animation automatisch aktualisiert, muss sie mit dieser Setter-Methode auf die Eigenschaft zugreifen können. Wenn der Attributname beispielsweise foo lautet, benötigen Sie die Methode setFoo(). Wenn diese Setter-Methode nicht vorhanden ist, haben Sie drei Möglichkeiten:
    • Fügen Sie der Klasse die Setter-Methode hinzu, wenn Sie über die entsprechenden Rechte verfügen.
    • Verwenden Sie eine Wrapper-Klasse, zu deren Änderung Sie berechtigt sind, und lassen Sie diesen Wrapper den Wert mit einer gültigen Setter-Methode empfangen und leiten Sie ihn an das ursprüngliche Objekt weiter.
    • Verwende stattdessen ValueAnimator.
  • Wenn Sie nur einen Wert für den Parameter values... in einer der ObjectAnimator-Factory-Methoden angeben, wird davon ausgegangen, dass dies der Endwert der Animation ist. 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. Lautet der Attributname beispielsweise foo, benötigen Sie die Methode getFoo().
  • Die Getter- und Setter-Methoden der zu animierenden Eigenschaft müssen mit demselben Typ wie die Start- und Endwerte, die Sie für ObjectAnimator angeben, ausgeführt werden. Sie müssen beispielsweise targetObject.setPropName(float) und targetObject.getPropName() haben, wenn Sie den folgenden ObjectAnimator erstellen:
    ObjectAnimator.ofFloat(targetObject, "propName", 1f)
    
  • Je nachdem, welche Eigenschaft oder welches Objekt Sie animieren, müssen Sie möglicherweise die invalidate()-Methode für eine Ansicht aufrufen, damit der Bildschirm sich mit den aktualisierten animierten Werten neu zeichnen muss. Dies kannst du im onAnimationUpdate()-Callback tun. Wenn du beispielsweise die Farbeigenschaft eines Drawable-Objekts animiert, werden nur dann Aktualisierungen am Bildschirm vorgenommen, wenn das Objekt neu gezeichnet wird. Alle Property-Setter für „View“, 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.

Mehrere Animationen mit einem AnimatorSet choreografieren

In vielen Fällen möchten Sie eine Animation abspielen, die davon abhängt, wann eine andere Animation gestartet oder beendet wird. Mit dem Android-System können Sie Animationen in einer AnimatorSet bündeln und so angeben, ob Animationen gleichzeitig, nacheinander oder nach einer bestimmten Verzögerung gestartet werden sollen. Sie können AnimatorSet-Objekte auch ineinander verschachteln.

Im folgenden Code-Snippet werden die folgenden Animator-Objekte so wiedergegeben:

  1. Spielt „bounceAnim“ ab.
  2. squashAnim1, squashAnim2, stretchAnim1 und stretchAnim2 werden gleichzeitig abgespielt.
  3. Spielt „bounceBackAnim“ ab.
  4. Spielt „fadeAnim“ ab.

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 für jeden Frame der Animation aufgerufen. Warten Sie auf dieses Ereignis, um die berechneten Werte zu verwenden, die von ValueAnimator während einer Animation generiert wurden. Wenn Sie den Wert verwenden möchten, fragen Sie das an das Ereignis übergebene ValueAnimator-Objekt ab, um den aktuellen animierten Wert mit der Methode getAnimatedValue() zu erhalten. Die Implementierung dieses Listeners ist erforderlich, wenn Sie ValueAnimator verwenden.

      Je nachdem, welche Eigenschaft oder welches Objekt Sie animieren, müssen Sie möglicherweise invalidate() für eine Ansicht aufrufen, damit dieser Bereich des Bildschirms sich mit den neuen animierten Werten neu gezeichnet wird. Wenn du beispielsweise die Farbeigenschaft eines Drawable-Objekts animiert, wird der Bildschirm nur aktualisiert, wenn das Objekt neu gezeichnet wird. Alle Property-Setter für die Datenansicht, 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, statt die Animator.AnimatorListener-Schnittstelle 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 für ViewGroup-Objekte animieren

Das Animationssystem für Eigenschaften ermöglicht eine Animation von Änderungen an ViewGroup-Objekten sowie eine einfache Möglichkeit, View-Objekte selbst zu animieren.

Layoutänderungen innerhalb einer ViewGroup können Sie mit der Klasse LayoutTransition animieren. Ansichten in einer ViewGroup können eine Animation durchlaufen, die ein- oder ausgeblendet wird, wenn Sie sie einer ViewGroup hinzufügen oder daraus entfernen oder die Methode setVisibility() einer Ansicht mit VISIBLE, INVISIBLE oder GONE aufrufen. Auch die übrigen Ansichten in der Ansichtsgruppe können an ihren neuen Positionen animiert werden, wenn Sie Ansichten hinzufügen oder entfernen. Sie können die folgenden Animationen in einem LayoutTransition-Objekt definieren, indem Sie setAnimator() aufrufen und ein Animator-Objekt mit einer der folgenden LayoutTransition-Konstanten übergeben:

  • APPEARING: Dieses Flag gibt die Animation für Elemente an, die im Container angezeigt werden.
  • CHANGE_APPEARING: Dieses Flag gibt die Animation für Elemente an, die sich aufgrund eines neuen Elements im Container ändern.
  • DISAPPEARING: Dieses Flag gibt die Animation für Elemente an, die aus dem Container verschwinden.
  • CHANGE_DISAPPEARING: Dieses Flag gibt die Animation für Elemente an, 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 einfach das Animationssystem anweisen, die Standardanimationen zu verwenden.

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

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

Wird dieses Attribut auf „true“ gesetzt, werden automatisch Ansichten animiert, die der ViewGroup hinzugefügt oder daraus entfernt werden. Das Gleiche gilt für die restlichen Datenansichten in der ViewGroup.

Ä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.

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

Die folgende Datei erstellt beispielsweise einen Animator für Statuslisten, der beim Drücken die X- und Y-Skalierung der Ansicht ändert:

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>

Um den Animator für die Statusliste an eine Ansicht anzuhängen, fügen Sie das Attribut android:stateListAnimator so hinzu:

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

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

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 Animator Ihrer Ansicht mit der Methode View.setStateListAnimator() zu.

Anstatt die Eigenschaften der Ansicht zu animieren, kannst du auch mit AnimatedStateListDrawable eine Drawable-Animation zwischen Statusänderungen abspielen. Einige System-Widgets in Android 5.0 verwenden diese Animationen standardmäßig. Das folgende Beispiel zeigt, wie ein 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. Dem Android-System sind die Typen int, float oder eine Farbe bekannt, die von den Typauswertern IntEvaluator, FloatEvaluator und ArgbEvaluator unterstützt werden.

In der TypeEvaluator-Schnittstelle kann nur eine Methode implementiert werden: die evaluate()-Methode. So kann der Animator, den Sie verwenden, am aktuellen Zeitpunkt der Animation einen geeigneten Wert für Ihre animierte Eigenschaft zurückgeben. Die Klasse FloatEvaluator veranschaulicht dies:

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 (ein Wert zwischen 0 und 1) berechnet und dann je nach verwendetem Interpolator eine interpolierte Version berechnet. Der interpolierte Anteil ist das, was die TypeEvaluator über den Parameter fraction erhält, sodass Sie den Interpolator bei der Berechnung animierter Werte nicht berücksichtigen müssen.

Interpolatoren verwenden

Ein Interpolator definiert, wie bestimmte Werte in einer Animation als Funktion der Zeit berechnet werden. Sie können beispielsweise festlegen, dass Animationen linear über die gesamte Animation laufen, sodass sie sich gleichmäßig über die gesamte Zeit hinweg bewegt. Sie können auch Animationen festlegen, die eine nicht lineare Zeit verwenden, z. B. durch Beschleunigung oder Verlangsamung am Anfang oder Ende der Animation.

Interpolatoren im Animationssystem erhalten einen Anteil von Animators, 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 gängiger Interpolatoren im android.view.animation package. Wenn keine dieser Optionen Ihren Anforderungen entspricht, können Sie die TimeInterpolator-Schnittstelle implementieren und eine eigene erstellen.

Unten sehen Sie ein Beispiel dafür, wie der Standardinterpolator AccelerateDecelerateInterpolator und der LinearInterpolator interpolierte Brüche berechnen. LinearInterpolator hat keine Auswirkungen auf den verstrichenen Anteil. Der AccelerateDecelerateInterpolator beschleunigt die Animation und wird aus ihr herausgezoomt. 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 ungefähren Werte, die von diesen Interpolatoren für eine 1.000 ms dauernde Animation berechnet werden:

ms verstrichen Verstrichener Anteil/interpolierter Bruch (linear) Interpolierter Bruch (beschleunigen/verzögern)
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 derselben Geschwindigkeit, nämlich 0,2 pro 200 ms. AccelerateDecelerateInterpolator ändert die Werte schneller als LinearInterpolator zwischen 200 ms und 600 ms und langsamer zwischen 600 und 1.000 ms.

Keyframes angeben

Ein Keyframe-Objekt besteht aus einem Zeit/Wert-Paar, mit dem Sie einen bestimmten Zustand zu einem bestimmten Zeitpunkt einer Animation definieren können. 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 Factory-Methode ofKeyframe() auf, um ein PropertyValuesHolder-Objekt zu erhalten. Wenn Sie das Objekt haben, können Sie einen Animator abrufen. Übergeben Sie dazu das PropertyValuesHolder-Objekt und das zu animierende Objekt. 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);

Aufrufe animieren

Das Property-Animationssystem ermöglicht eine optimierte Animation von View-Objekten und bietet einige Vorteile gegenüber dem Ansichtsanimationssystem. Das Ansichtsanimationssystem transformierte View-Objekte, indem es die Art und Weise änderte, in der sie gezeichnet wurden. Diese Änderung wurde im Container jeder Ansicht vorgenommen, da die Ansicht selbst keine Eigenschaften hatte, die bearbeitet werden konnten. Dies führte dazu, dass die Ansicht animiert wurde, aber keine Änderung im View-Objekt selbst verursachte. Dies führte zu einem Verhalten, bei dem ein Objekt weiterhin an seiner ursprünglichen Position vorhanden war, obwohl es an einer anderen Stelle auf dem Bildschirm gezeichnet wurde. In Android 3.0 wurden neue Eigenschaften und die entsprechenden Getter- und Setter-Methoden hinzugefügt, um diesen Nachteil zu beseitigen.

Das Eigenschaftsanimationssystem kann Ansichten auf dem Bildschirm animieren, indem die eigentlichen 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. Dies sind die neuen Eigenschaften in der Klasse View, die Eigenschaftsanimationen erstellen:

  • translationX und translationY: Mit diesen Eigenschaften wird festgelegt, wo sich die Ansicht als Delta zu den linken und oberen Koordinaten befindet, die durch den Layoutcontainer festgelegt werden.
  • rotation, rotationX und rotationY: Mit diesen Eigenschaften wird die 2D-Drehung (rotation-Eigenschaft) und die 3D-Drehung um den Drehpunkt gesteuert.
  • scaleX und scaleY: Mit diesen Eigenschaften wird die 2D-Skalierung einer Ansicht um ihren Drehpunkt festgelegt.
  • pivotX und pivotY: Diese Eigenschaften steuern die Position des Drehpunkts, um den herum die Rotations- und Skalierungstransformationen erfolgen. Standardmäßig befindet sich der Drehpunkt im Zentrum des Objekts.
  • x und y: Dies sind einfache Diensteigenschaften zur Beschreibung der endgültigen Position der Ansicht in ihrem Container, als Summe der Werte für den linken und oberen Rand und die Werte für die Übersetzung X und die Übersetzung Y.
  • alpha: Stellt die Alphatransparenz in der Ansicht dar. Der Standardwert ist 1 (undurchsichtig), wobei der Wert 0 für vollständige Transparenz (nicht sichtbar) steht.

Um eine Eigenschaft eines View-Objekts zu animieren, etwa seine Farbe oder den Rotationswert, müssen Sie lediglich einen Eigenschafts-Animator 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 Animationen erstellen

ViewPropertyAnimator bietet eine einfache Möglichkeit, mehrere Attribute einer View gleichzeitig zu animieren. Dazu wird ein einzelnes zugrunde liegendes Animator-Objekt verwendet. Sie verhält sich ähnlich wie ObjectAnimator, da sie die tatsächlichen Werte der Ansichtsattribute ändert, aber effizienter ist, wenn viele Attribute gleichzeitig animiert werden. Außerdem ist der Code zur Verwendung von ViewPropertyAnimator viel prägnanter und leichter zu lesen. Die folgenden Code-Snippets zeigen die Unterschiede bei der Verwendung mehrerer ObjectAnimator-Objekte, eines einzelnen ObjectAnimator und der ViewPropertyAnimator bei gleichzeitiger Animation der x- und y-Eigenschaften 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();

Ein 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);

Weitere 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 zu tun. Wenn Sie Ihre Animationen in XML definieren, können Sie sie einfach in mehreren Aktivitäten wiederverwenden und die Animationssequenz einfacher bearbeiten.

Damit Sie die Animationsdateien, die die neuen APIs für Eigenschaftsanimationen verwenden, von denen unterscheiden können, die das Legacy-Framework für Ansichtsanimationen ab Android 3.1 verwenden, sollten Sie die XML-Dateien für Eigenschaftsanimationen im Verzeichnis res/animator/ speichern.

Für die folgenden Eigenschaftsanimationsklassen wird die XML-Deklaration durch folgende XML-Tags unterstützt:

Informationen zu den Attributen, die Sie in Ihrer XML-Deklaration verwenden können, finden Sie unter Animationsressourcen. Im folgenden Beispiel werden die beiden Sätze von Objektanimationen nacheinander abgespielt, wobei im ersten verschachtelten Satz zwei Objektanimationen gleichzeitig abgespielt werden:

<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>

Um diese Animation auszuführen, müssen Sie die XML-Ressourcen in Ihrem Code auf ein AnimatorSet-Objekt aufblähen und dann die Zielobjekte für alle Animationen festlegen, bevor Sie die Animation starten. Durch das Aufrufen von setTarget() wird der Einfachheit halber ein einzelnes Zielobjekt für alle untergeordneten Elemente des AnimatorSet-Elements festgelegt. Der folgende Code zeigt, wie das 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 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 die vorherige ValueAnimator in Ihrem Code verwenden möchten, müssen Sie das Objekt aufblasen, ein AnimatorUpdateListener-Element hinzufügen, den aktualisierten Animationswert abrufen und ihn wie im folgenden Code in einer Eigenschaft einer Ihrer Ansichten verwenden:

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 Attributanimationen finden Sie unter Animationsressourcen .

Mögliche Auswirkungen auf die UI-Leistung

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

Die zum Animieren der UI erforderlichen Schritte werden der Animationsphase der Rendering-Pipeline hinzugefügt. Sie können feststellen, ob sich Ihre Animationen auf die Leistung Ihrer App auswirken, indem Sie Profil-GPU-Rendering aktivieren und die Animationsphase überwachen. Weitere Informationen finden Sie unter Schritt-für-Schritt-Anleitung zum Profil-GPU-Rendering.