Bewegung mit Federphysik animieren

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

Physikbasierte Bewegungen werden durch Kraft gesteuert. Eine solche Kraft ist die Federkraft, die Interaktivität und Bewegungen leitet. Eine Federkraft hat folgende Eigenschaften: Dämpfung und Steifheit. In einer federbasierten Animation werden der Wert und die Geschwindigkeit basierend auf der Federkraft berechnet, die auf jeden Frame angewendet wird.

Wenn du die Animationen deiner App nur in eine Richtung verlangsamen möchtest, kannst du stattdessen eine reibungsbasierte Fling-Animation verwenden.

Lebenszyklus einer Frühlingsanimation

In einer federbasierten Animation können Sie mit der Klasse SpringForce die Steifheit der Feder, ihr Dämpfungsverhältnis und ihre endgültige Position anpassen. Sobald die Animation beginnt, aktualisiert die Federkraft den Animationswert und die Geschwindigkeit in jedem Frame. Die Animation wird fortgesetzt, bis die Federkraft ein Gleichgewicht erreicht hat.

Wenn du beispielsweise ein App-Symbol um den Bildschirm ziehst und später loslässt, indem du den Finger vom Symbol hebst, zieht es durch eine unsichtbare, aber vertraute Kraft an seine ursprüngliche Stelle zurück.

Abbildung 1 zeigt einen ähnlichen Federeffekt. Das Pluszeichen (+) in der Mitte des Kreises zeigt die Kraft an, die durch eine Touch-Geste aufgebracht wird.

Frühlings-Release
Abbildung 1: Frühlings-Release-Effekt

Baue eine Frühlingsanimation

Die allgemeinen Schritte zum Erstellen einer Frühlingsanimation für Ihre Anwendung sind folgende:

In den folgenden Abschnitten werden die allgemeinen Schritte zum Erstellen einer Frühlingsanimation im Detail erläutert.

Supportbibliothek hinzufügen

Wenn Sie die physikbasierte Supportbibliothek verwenden möchten, müssen Sie die Supportbibliothek wie folgt zu Ihrem Projekt hinzufügen:

  1. Öffnen Sie die Datei build.gradle für Ihr App-Modul.
  2. Fügen Sie die Supportbibliothek zum Abschnitt dependencies hinzu.

    Groovig

            dependencies {
                def dynamicanimation_version = '1.0.0'
                implementation "androidx.dynamicanimation:dynamicanimation:$dynamicanimation_version"
            }
            

    Kotlin

            dependencies {
                val dynamicanimation_version = "1.0.0"
                implementation("androidx.dynamicanimation:dynamicanimation:$dynamicanimation_version")
            }
            

    Die aktuellen Versionen dieser Bibliothek finden Sie in den Informationen zu Dynamic Animation auf der Seite Versionen.

Frühlingsanimation erstellen

Mit der Klasse SpringAnimation können Sie eine Frühlingsanimation für ein Objekt erstellen. Um eine Frühlingsanimation zu erstellen, müssen Sie eine Instanz der SpringAnimation-Klasse erstellen und ein Objekt, die Eigenschaft eines Objekts, die Sie animieren möchten, sowie eine optionale endgültige Federposition angeben, an der die Animation angehalten werden soll.

Hinweis:Beim Erstellen einer Federanimation ist die endgültige Position der Feder optional. Sie muss jedoch definiert werden, bevor die Animation gestartet wird.

Kotlin

val springAnim = findViewById<View>(R.id.imageView).let { img ->
    // Setting up a spring animation to animate the view’s translationY property with the final
    // spring position at 0.
    SpringAnimation(img, DynamicAnimation.TRANSLATION_Y, 0f)
}

Java

final View img = findViewById(R.id.imageView);
// Setting up a spring animation to animate the view’s translationY property with the final
// spring position at 0.
final SpringAnimation springAnim = new SpringAnimation(img, DynamicAnimation.TRANSLATION_Y, 0);

Durch die federbasierte Animation können Ansichten auf dem Bildschirm animiert werden, indem die tatsächlichen Eigenschaften in den Ansichtsobjekten geändert werden. Die folgenden Ansichten sind im System verfügbar:

  • ALPHA: Stellt die Alphatransparenz in der Ansicht dar. Der Wert ist standardmäßig 1 (undurchsichtig), wobei der Wert 0 für vollständige Transparenz steht (nicht sichtbar).
  • TRANSLATION_X, TRANSLATION_Y und TRANSLATION_Z: Mit diesen Eigenschaften wird die Position der Ansicht als Delta der linken Koordinaten, der obersten Koordinate und der Höhe festgelegt, die durch den Layoutcontainer festgelegt werden.
  • ROTATION, ROTATION_X und ROTATION_Y: Diese Eigenschaften steuern die 2D-Drehung (rotation-Eigenschaft) und die 3D-Drehung um den Drehpunkt.
  • SCROLL_X und SCROLL_Y: Diese Eigenschaften geben den Scrollversatz der Quelle links und des oberen Rands in Pixeln an. Er gibt auch an, wo auf der Seite gescrollt wird.
  • SCALE_X und SCALE_Y: Mit diesen Eigenschaften wird die 2D-Skalierung einer Ansicht um ihren Drehpunkt herum gesteuert.
  • X, Y und Z: Dies sind grundlegende Diensteigenschaften zur Beschreibung der endgültigen Position der Ansicht in ihrem Container.

Listener registrieren

Die Klasse DynamicAnimation bietet zwei Listener: OnAnimationUpdateListener und OnAnimationEndListener. Diese Listener erfassen Aktualisierungen in der Animation, beispielsweise wenn sich der Wert für die Animation ändert oder wenn die Animation endet.

OnAnimationUpdateListener

Wenn Sie mehrere Ansichten animieren möchten, um eine verkettete Animation zu erstellen, können Sie OnAnimationUpdateListener so einrichten, dass jedes Mal ein Callback empfangen wird, wenn sich die Property der aktuellen Ansicht ändert. Der Callback informiert die andere Datenansicht darüber, dass ihre Federposition anhand der Änderung an der Property der aktuellen Ansicht aktualisiert werden soll. Registrieren Sie den Listener mit den folgenden Schritten:

  1. Rufen Sie die Methode addUpdateListener() auf und hängen Sie den Listener an die Animation an.

    Hinweis:Der Update-Listener muss vor Beginn der Animation registriert werden. Der Update-Listener sollte jedoch nur registriert werden, wenn der Animationswert pro Frame aktualisiert werden muss. Ein Update-Listener verhindert, dass die Animation potenziell in einem separaten Thread ausgeführt wird.

  2. Überschreiben Sie die Methode onAnimationUpdate(), um den Aufrufer über die Änderung im aktuellen Objekt zu informieren. Der folgende Beispielcode veranschaulicht die allgemeine Verwendung von OnAnimationUpdateListener.

Kotlin

// Setting up a spring animation to animate the view1 and view2 translationX and translationY properties
val (anim1X, anim1Y) = findViewById<View>(R.id.view1).let { view1 ->
    SpringAnimation(view1, DynamicAnimation.TRANSLATION_X) to
            SpringAnimation(view1, DynamicAnimation.TRANSLATION_Y)
}
val (anim2X, anim2Y) = findViewById<View>(R.id.view2).let { view2 ->
    SpringAnimation(view2, DynamicAnimation.TRANSLATION_X) to
            SpringAnimation(view2, DynamicAnimation.TRANSLATION_Y)
}

// Registering the update listener
anim1X.addUpdateListener { _, value, _ ->
    // Overriding the method to notify view2 about the change in the view1’s property.
    anim2X.animateToFinalPosition(value)
}

anim1Y.addUpdateListener { _, value, _ -> anim2Y.animateToFinalPosition(value) }

Java

// Creating two views to demonstrate the registration of the update listener.
final View view1 = findViewById(R.id.view1);
final View view2 = findViewById(R.id.view2);

// Setting up a spring animation to animate the view1 and view2 translationX and translationY properties
final SpringAnimation anim1X = new SpringAnimation(view1,
        DynamicAnimation.TRANSLATION_X);
final SpringAnimation anim1Y = new SpringAnimation(view1,
    DynamicAnimation.TRANSLATION_Y);
final SpringAnimation anim2X = new SpringAnimation(view2,
        DynamicAnimation.TRANSLATION_X);
final SpringAnimation anim2Y = new SpringAnimation(view2,
        DynamicAnimation.TRANSLATION_Y);

// Registering the update listener
anim1X.addUpdateListener(new DynamicAnimation.OnAnimationUpdateListener() {

// Overriding the method to notify view2 about the change in the view1’s property.
    @Override
    public void onAnimationUpdate(DynamicAnimation dynamicAnimation, float value,
                                  float velocity) {
        anim2X.animateToFinalPosition(value);
    }
});

anim1Y.addUpdateListener(new DynamicAnimation.OnAnimationUpdateListener() {

  @Override
    public void onAnimationUpdate(DynamicAnimation dynamicAnimation, float value,
                                  float velocity) {
        anim2Y.animateToFinalPosition(value);
    }
});

OnAnimationEndListener

OnAnimationEndListener benachrichtigt das Ende einer Animation. Sie können den Listener so einrichten, dass er einen Callback empfängt, sobald die Animation das Gleichgewicht erreicht oder abgebrochen wird. Registrieren Sie den Listener mit den folgenden Schritten:

  1. Rufen Sie die Methode addEndListener() auf und hängen Sie den Listener an die Animation an.
  2. Überschreiben Sie die Methode onAnimationEnd(), damit Sie immer dann benachrichtigt werden, wenn eine Animation das Gleichgewicht erreicht oder abgebrochen wird.

Listener entfernen

Wenn Sie keine Callbacks für Animationsaktualisierung und Animationsende mehr erhalten möchten, rufen Sie die Methoden removeUpdateListener() bzw. removeEndListener() auf.

Startwert der Animation festlegen

Rufen Sie die Methode setStartValue() auf und übergeben Sie den Startwert der Animation, um den Startwert der Animation festzulegen. Wenn Sie keinen Startwert festlegen, wird in der Animation der aktuelle Wert der Objekteigenschaft als Startwert verwendet.

Wertebereich für Animation festlegen

Sie können die Mindest- und Höchstwerte für die Animation festlegen, wenn Sie den Eigenschaftswert auf einen bestimmten Bereich beschränken möchten. Außerdem ist es hilfreich, den Bereich zu steuern, falls Sie Eigenschaften animieren, die einen intrinsischen Bereich haben, z. B. Alpha (von 0 bis 1).

  • Wenn Sie den Mindestwert festlegen möchten, rufen Sie die Methode setMinValue() auf und übergeben Sie den Mindestwert der Eigenschaft.
  • Wenn Sie den Maximalwert festlegen möchten, rufen Sie die Methode setMaxValue() auf und übergeben Sie den Maximalwert der Property.

Beide Methoden geben die Animation zurück, für die der Wert festgelegt wird.

Hinweis: Wenn Sie den Startwert festgelegt und einen Animationswertbereich definiert haben, muss der Startwert innerhalb des Mindest- und Höchstwertbereichs liegen.

Startgeschwindigkeit einstellen

Die Startgeschwindigkeit definiert die Geschwindigkeit, mit der sich die Animationseigenschaft zu Beginn der Animation ändert. Die standardmäßige Startgeschwindigkeit ist auf null Pixel pro Sekunde festgelegt. Du kannst die Geschwindigkeit entweder über die Geschwindigkeit von Touch-Gesten oder über einen festen Wert als Startgeschwindigkeit festlegen. Wenn Sie einen festen Wert angeben möchten, sollten Sie den Wert in dp pro Sekunde definieren und ihn dann in Pixel pro Sekunde konvertieren. Wenn der Wert in dp pro Sekunde definiert wird, ist die Geschwindigkeit unabhängig von Dichte und Formfaktoren. Weitere Informationen zum Konvertieren von Werten in Pixel pro Sekunde finden Sie im Abschnitt dp pro Sekunde in Pixel pro Sekunde umwandeln.

Um die Geschwindigkeit festzulegen, rufen Sie die Methode setStartVelocity() auf und übergeben Sie die Geschwindigkeit in Pixeln pro Sekunde. Die Methode gibt das Federkraftobjekt zurück, auf das die Geschwindigkeit eingestellt ist.

Hinweis: Verwenden Sie die Klassenmethoden GestureDetector.OnGestureListener oder VelocityTracker, um die Geschwindigkeit von Touch-Gesten abzurufen und zu berechnen.

Kotlin

findViewById<View>(R.id.imageView).also { img ->
    SpringAnimation(img, DynamicAnimation.TRANSLATION_Y).apply {
        …
        // Compute velocity in the unit pixel/second
        vt.computeCurrentVelocity(1000)
        val velocity = vt.yVelocity
        setStartVelocity(velocity)
    }
}

Java

final View img = findViewById(R.id.imageView);
final SpringAnimation anim = new SpringAnimation(img, DynamicAnimation.TRANSLATION_Y);
…
// Compute velocity in the unit pixel/second
vt.computeCurrentVelocity(1000);
float velocity = vt.getYVelocity();
anim.setStartVelocity(velocity);

dp pro Sekunde in Pixel pro Sekunde umwandeln

Die Geschwindigkeit einer Feder muss in Pixeln pro Sekunde angegeben werden. Wenn Sie einen festen Wert als Anfang der Geschwindigkeit angeben möchten, geben Sie den Wert in dp pro Sekunde an und wandeln ihn dann in Pixel pro Sekunde um. Verwenden Sie für die Konvertierung die Methode applyDimension() aus der Klasse TypedValue. Sehen Sie sich den folgenden Beispielcode an:

Kotlin

val pixelPerSecond: Float =
    TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpPerSecond, resources.displayMetrics)

Java

float pixelPerSecond = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpPerSecond, getResources().getDisplayMetrics());

Frühlingseigenschaften festlegen

Die Klasse SpringForce definiert den Getter und die Setter-Methoden für jede der Federeigenschaften, z. B. das Dämpfungsverhältnis und die Steifheit. Zum Einstellen der Federeigenschaften muss entweder das Federkraftobjekt abgerufen oder eine benutzerdefinierte Federkraft erstellt werden, für die sich die Eigenschaften festlegen lassen. Weitere Informationen zum Erstellen einer benutzerdefinierten Federkraft finden Sie im Abschnitt Benutzerdefinierte Federkraft erstellen.

Tipp: Bei Verwendung der Setter-Methoden können Sie eine Methodenkette erstellen, da alle Setter-Methoden das Federkraftobjekt zurückgeben.

Dämpfungsverhältnis

Das Dämpfungsverhältnis beschreibt eine allmähliche Reduzierung einer Federschwingung. Mithilfe des Dämpfungsverhältnisses können Sie definieren, wie schnell die Schwingungen von einem Sprung zum nächsten abfallen. Es gibt vier Möglichkeiten, eine Feder zu feuchten:

  • Eine Überdämmung tritt auf, wenn das Dämpfungsverhältnis größer als eins ist. Das Objekt kehrt sanft in seine Ruheposition zurück.
  • Eine kritische Dämpfung tritt auf, wenn das Dämpfungsverhältnis gleich eins ist. So kann das Objekt innerhalb kürzester Zeit an seine Ruheposition zurückkehren.
  • Eine Unterdämpfung tritt auf, wenn das Dämpfungsverhältnis kleiner als eins ist. Das Objekt kann mehrfach überschritten werden, indem die Ruheposition übergeben wird, bevor sie die Ruheposition nach und nach erreicht.
  • Der Wert „ungeampelt“ tritt auf, wenn das Dämpfungsverhältnis gleich null ist. Das Objekt ändert sich dadurch unbegrenzt.

Führe die folgenden Schritte aus, um das Dämpfungsverhältnis zur Feder hinzuzufügen:

  1. Rufen Sie die Methode getSpring() auf, um die Feder abzurufen und das Dämpfungsverhältnis hinzuzufügen.
  2. Rufen Sie die Methode setDampingRatio() auf und übergeben Sie das Dämpfungsverhältnis, das Sie der Feder hinzufügen möchten. Die Methode gibt das Federkraftobjekt zurück, auf das das Dämpfungsverhältnis eingestellt ist.

    Hinweis: Das Dämpfungsverhältnis muss eine nicht negative Zahl sein. Wenn Sie das Dämpfungsverhältnis auf null setzen, erreicht die Feder nie die Ruheposition. Mit anderen Worten, sie schwankt für immer.

Die folgenden Konstanten des Dämpfungsverhältnisses sind im System verfügbar:

Abbildung 2: Hohes Absprung

Abbildung 3: Mittlere Absprünge

Abbildung 4: Niedriger Absprung

Abbildung 5: Kein Absprung

Das Standarddämpfungsverhältnis ist auf DAMPING_RATIO_MEDIUM_BOUNCY eingestellt.

Kotlin

findViewById<View>(R.id.imageView).also { img ->
    SpringAnimation(img, DynamicAnimation.TRANSLATION_Y).apply {
        …
        // Setting the damping ratio to create a low bouncing effect.
        spring.dampingRatio = SpringForce.DAMPING_RATIO_LOW_BOUNCY
        …
    }
}

Java

final View img = findViewById(R.id.imageView);
final SpringAnimation anim = new SpringAnimation(img, DynamicAnimation.TRANSLATION_Y);
…
// Setting the damping ratio to create a low bouncing effect.
anim.getSpring().setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY);
…

Steifheit

Steifheit definiert die Federkonstante, die die Stärke der Feder misst. Eine steife Feder weist auf das befestigte Objekt mehr Kraft auf, wenn sich die Feder nicht in der Ruheposition befindet. Gehen Sie so vor, um der Feder die Steifheit hinzuzufügen:

  1. Rufen Sie die Methode getSpring() auf, um die Feder abzurufen und die Steifheit hinzuzufügen.
  2. Rufen Sie die Methode setStiffness() auf und übergeben Sie den Wert für die Steifheit, den Sie der Feder hinzufügen möchten. Die Methode gibt das Federkraftobjekt zurück, auf das die Steifheit eingestellt ist.

    Hinweis: Die Steifheit muss eine positive Zahl sein.

Die folgenden Steifheitskonstanten sind im System verfügbar:

Abbildung 6: Hohe Steifheit

Abbildung 7: Mittlere Steifheit

Abbildung 8: Geringe Steifheit

Abbildung 9: Sehr geringe Steifheit

Die standardmäßige Steifheit ist auf STIFFNESS_MEDIUM eingestellt.

Kotlin

findViewById<View>(R.id.imageView).also { img ->
    SpringAnimation(img, DynamicAnimation.TRANSLATION_Y).apply {
        …
        // Setting the spring with a low stiffness.
        spring.stiffness = SpringForce.STIFFNESS_LOW
        …
    }
}

Java

final View img = findViewById(R.id.imageView);
final SpringAnimation anim = new SpringAnimation(img, DynamicAnimation.TRANSLATION_Y);
…
// Setting the spring with a low stiffness.
anim.getSpring().setStiffness(SpringForce.STIFFNESS_LOW);
…

Benutzerdefinierte Federkraft erstellen

Alternativ können Sie eine benutzerdefinierte Federkraft erstellen. Mit der benutzerdefinierten Federkraft können Sie dieselbe Federkraft in mehreren Federanimationen teilen. Nachdem Sie die Federkraft festgelegt haben, können Sie Eigenschaften wie das Dämpfungsverhältnis und die Steifheit festlegen.

  1. Erstellen Sie ein SpringForce-Objekt.

    SpringForce force = new SpringForce();

  2. Weisen Sie die Eigenschaften zu, indem Sie die entsprechenden Methoden aufrufen. Sie können auch eine Methodenkette erstellen.

    force.setDampingRatio(DAMPING_RATIO_LOW_BOUNCY).setStiffness(STIFFNESS_LOW);

  3. Rufen Sie die Methode setSpring() auf, um die Quelle auf die Animation festzulegen.

    setSpring(force);

Animation starten

Es gibt zwei Möglichkeiten, eine Frühlingsanimation zu starten: durch Aufrufen von start() oder durch Aufrufen der Methode animateToFinalPosition(). Beide Methoden müssen im Hauptthread aufgerufen werden.

animateToFinalPosition() führt zwei Aufgaben aus:

  • Legt die endgültige Position der Feder fest.
  • Startet die Animation, falls sie noch nicht gestartet wurde.

Da die Methode die endgültige Position der Feder aktualisiert und die Animation bei Bedarf startet, können Sie diese Methode jederzeit aufrufen, um den Verlauf einer Animation zu ändern. Bei einer verketteten Frühlingsanimation hängt die Animation einer Ansicht beispielsweise von einer anderen ab. Für eine solche Animation bietet es sich an, die Methode animateToFinalPosition() zu verwenden. Wenn Sie diese Methode in einer verketteten Frühlingsanimation verwenden, müssen Sie sich keine Gedanken darüber machen, ob die Animation, die Sie als Nächstes aktualisieren möchten, derzeit ausgeführt wird.

In Abbildung 10 ist eine verkettete Federanimation dargestellt, bei der die Animation einer Ansicht von einer anderen Ansicht abhängt.

Frühlingsdemo verkettet
Abbildung 10. Ketten-Frühlingsdemo

Wenn Sie die Methode animateToFinalPosition() verwenden möchten, rufen Sie die Methode animateToFinalPosition() auf und übergeben Sie die Ruheposition der Feder. Sie können auch die Ruheposition der Feder festlegen, indem Sie die Methode setFinalPosition() aufrufen.

Bei der Methode start() wird der Attributwert nicht sofort auf den Startwert gesetzt. Der Eigenschaftswert ändert sich bei jedem Animationspuls, der vor dem Zeichnen erfolgt. Die Änderungen sind dann im nächsten Frame sichtbar, als ob die Werte sofort festgelegt werden würden.

Kotlin

findViewById<View>(R.id.imageView).also { img ->
    SpringAnimation(img, DynamicAnimation.TRANSLATION_Y).apply {
        …
        // Starting the animation
        start()
        …
    }
}

Java

final View img = findViewById(R.id.imageView);
final SpringAnimation anim = new SpringAnimation(img, DynamicAnimation.TRANSLATION_Y);
…
// Starting the animation
anim.start();
…

Animation abbrechen

Sie können die Animation abbrechen oder zum Ende springen. Im Idealfall musst du die Aktivierung abbrechen oder zum Ende springen, wenn eine Nutzerinteraktion erfordert, dass die Animation sofort beendet wird. Das passiert vor allem dann, wenn ein Nutzer eine App abrupt schließt oder die Ansicht unsichtbar wird.

Es gibt zwei Methoden, um die Animation zu beenden. Die Methode cancel() beendet die Animation an dem Wert, an dem sie sich befindet. Die Methode skipToEnd() überspringt die Animation bis zum endgültigen Wert und beendet sie dann.

Bevor Sie die Animation beenden können, ist es wichtig, zuerst den Zustand der Feder zu prüfen. Ist der Zustand ungelastet, kann die Animation die restliche Position nie erreichen. Um den Zustand der Feder zu prüfen, rufen Sie die Methode canSkipToEnd() auf. Wenn die Feder gedämpft ist, gibt die Methode true zurück, andernfalls false.

Sobald Sie den Zustand der Feder kennen, können Sie eine Animation mit der Methode skipToEnd() oder cancel() beenden. Die Methode cancel() muss nur im Hauptthread aufgerufen werden.

Hinweis: Im Allgemeinen führt die Methode skipToEnd() zu einem visuellen Sprung.