Bewegung mit Federphysik animieren

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

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

Wenn die Animationen Ihrer App nur in einer Richtung verlangsamt werden sollen, können Sie stattdessen eine Reibungsanimation verwenden, die auf Reibungspunkten basiert.

Lebenszyklus einer Federanimation

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 auf jedem Frame. Die Animation wird fortgesetzt, bis die Federkraft ein Gleichgewicht erreicht.

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

In Abbildung 1 ist ein ähnlicher Federeffekt dargestellt. Das Pluszeichen (+) in der Mitte des Kreises gibt die Kraft an, die durch eine Berührung ausgeübt wird.

Frühlingsrelease
Abbildung 1. Effekt „Frühlingsrelease“

Frühlingsanimation erstellen

So erstellen Sie eine Federanimation für Ihre Anwendung:

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

Supportbibliothek hinzufügen

Wenn Sie die physikbasierte Supportbibliothek verwenden möchten, müssen Sie sie Ihrem Projekt so hinzufügen:

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

    Cool

            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 auf der Seite Versionen in den Informationen zu Dynamic Animation.

Frühlingsanimation erstellen

Mit der Klasse SpringAnimation können Sie eine Federanimation für ein Objekt erstellen. Wenn Sie eine Federanimation erstellen möchten, müssen Sie eine Instanz der Klasse SpringAnimation erstellen und ein Objekt, eine Objekteigenschaft, die animiert werden soll, und eine optionale endgültige Federposition für die Animation angeben.

Hinweis:Beim Erstellen einer Federanimation ist die endgültige Position der Feder optional. Er muss jedoch vor dem Start der Animation definiert werden.

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

Mit dieser 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), der Wert 0 steht für vollständige Transparenz (nicht sichtbar).
  • TRANSLATION_X, TRANSLATION_Y und TRANSLATION_Z: Mit diesen Eigenschaften wird festgelegt, wo sich die Ansicht als Delta von der linken Koordinate, der oberen Koordinate und der Höhe befindet, die durch den jeweiligen Layoutcontainer festgelegt werden.
  • ROTATION, ROTATION_X und ROTATION_Y: Mit diesen Eigenschaften wird die 2D-Drehung (rotation-Eigenschaft) und die 3D-Drehung um den Drehpunkt festgelegt.
  • SCROLL_X und SCROLL_Y: Diese Eigenschaften geben den Scroll-Offset der Quelle links und des oberen Rands in Pixeln an. Außerdem wird angegeben, wie weit die Seite gescrollt wird.
  • SCALE_X und SCALE_Y: Mit diesen Eigenschaften wird die 2D-Skalierung einer Ansicht um ihren Drehpunkt herum festgelegt.
  • X, Y und Z: Dies sind grundlegende Dienstprogrammattribute, die den endgültigen Standort der Ansicht in ihrem Container beschreiben.

Listener registrieren

Die Klasse DynamicAnimation bietet zwei Listener: OnAnimationUpdateListener und OnAnimationEndListener. Diese Listener hören auf Aktualisierungen in der Animation, z. B. wenn sich der Animationswert ändert oder die Animation zu Ende ist.

OnAnimationUpdateListener

Wenn Sie mehrere Ansichten animieren möchten, um eine verkettete Animation zu erstellen, können Sie festlegen, dass OnAnimationUpdateListener jedes Mal einen Callback erhält, wenn sich die Eigenschaft der aktuellen Ansicht ändert. Der Callback benachrichtigt die andere Ansicht, um ihre Federposition basierend auf der Änderung an der Property der aktuellen Ansicht zu aktualisieren. So registrieren Sie den Listener:

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

    Hinweis: Sie müssen den Update-Listener registrieren, bevor die Animation beginnt. Der Update-Listener sollte jedoch nur registriert werden, wenn Sie den Animationswert pro Frame aktualisieren müssen. Ein Update-Listener verhindert, dass die Animation möglicherweise 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 erhält, wenn die Animation ein Gleichgewicht erreicht oder abgebrochen wird. Führen Sie die folgenden Schritte aus, um den Listener zu registrieren:

  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 benachrichtigt werden, wenn eine Animation das Gleichgewicht erreicht oder abgebrochen wird.

Hörer entfernen

Wenn Sie keine Callbacks für Animationsupdates und keine Callbacks zum Ende der Animation mehr erhalten möchten, rufen Sie die Methoden removeUpdateListener() bzw. removeEndListener() auf.

Startwert der Animation festlegen

Um den Startwert der Animation festzulegen, rufen Sie die Methode setStartValue() auf und übergeben Sie den Startwert der Animation. Wenn Sie keinen Startwert festlegen, verwendet die Animation den aktuellen Wert der Eigenschaft des Objekts als Startwert.

Wertebereich für Animation festlegen

Sie können die minimalen und maximalen Animationswerte festlegen, wenn Sie den Attributwert auf einen bestimmten Bereich beschränken möchten. Es ist auch hilfreich, den Bereich zu steuern, wenn du Properties mit einem intrinsischen Bereich animiert, 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 des Attributs.
  • Rufen Sie zum Festlegen des Maximalwerts die Methode setMaxValue() auf und übergeben Sie den Maximalwert des Attributs.

Bei beiden Methoden wird die Animation zurückgegeben, für die der Wert festgelegt wird.

Hinweis:Wenn Sie den Startwert festgelegt und einen Wertebereich für die Animation definiert haben, muss der Startwert innerhalb des Mindest- und Höchstwerts liegen.

Startgeschwindigkeit festlegen

Die Startgeschwindigkeit definiert die Geschwindigkeit, mit der sich die Animationseigenschaft zu Beginn der Animation ändert. Die Standardstartgeschwindigkeit ist auf null Pixel pro Sekunde eingestellt. Sie können die Geschwindigkeit entweder über die Geschwindigkeit der Berührung oder einen festen Wert als Startgeschwindigkeit festlegen. Wenn Sie einen festen Wert angeben möchten, empfehlen wir, den Wert in dp pro Sekunde zu definieren und ihn dann in Pixel pro Sekunde umzuwandeln. Wenn Sie den Wert in dp pro Sekunde definieren, kann die Geschwindigkeit unabhängig von Dichte und Formfaktoren sein. Weitere Informationen zum Umwandeln von Werten in Pixel pro Sekunde finden Sie im Abschnitt dp pro Sekunde in Pixel pro Sekunde konvertieren.

Rufen Sie zum Festlegen der Geschwindigkeit die Methode setStartVelocity() auf und übergeben Sie die Geschwindigkeit in Pixeln pro Sekunde. Die Methode gibt das Federkraftobjekt zurück, auf dem die Geschwindigkeit festgelegt wird.

Hinweis:Verwende 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 Pixel pro Sekunde angegeben werden. Wenn Sie einen festen Wert als Anfang der Geschwindigkeit festlegen möchten, geben Sie den Wert in dp pro Sekunde an und rechnen ihn dann in Pixel pro Sekunde um. Verwenden Sie zur Konvertierung die Methode applyDimension() aus der Klasse TypedValue. Sehen Sie sich dazu 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());

Federeigenschaften festlegen

Die Klasse SpringForce definiert den Getter und die Setter-Methoden für die einzelnen Federeigenschaften, z. B. das Dämpfungsverhältnis und die Steifheit. Zum Festlegen der Federeigenschaften muss entweder das Federkraftobjekt abgerufen oder eine benutzerdefinierte Federkraft erstellt werden, für die Sie die Eigenschaften festlegen können. 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 Verringerung der Schwingung einer Feder. Mit dem Dämpfungsverhältnis können Sie festlegen, wie schnell die Schwingungen von einem Sprung zum nächsten abklingen. Es gibt vier Möglichkeiten, eine Feder zu befeuchten:

  • Eine Überdämpfung tritt auf, wenn das Dämpfungsverhältnis größer als eins ist. Auf diese Weise kehrt das Objekt sanft in die Ruheposition zurück.
  • Eine kritische Dämpfung tritt auf, wenn das Dämpfungsverhältnis gleich 1 ist. Auf diese Weise kann das Objekt innerhalb kürzester Zeit zu seiner Ruheposition zurückkehren.
  • Eine Unterdämpfung tritt auf, wenn das Dämpfungsverhältnis kleiner als 1 ist. Dabei wird die Ruheposition übergeben, damit ein Objekt mehrmals übersteigen kann. Anschließend wird nach und nach die Ruheposition erreicht.
  • Wenn das Dämpfungsverhältnis gleich null ist, erfolgt die Dämpfung als „nicht gedämpft“. Es lässt das Objekt für immer schwingen.

So fügen Sie der Feder das Dämpfungsverhältnis hinzu:

  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 dem das Dämpfungsverhältnis festgelegt ist.

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

Im System sind folgende Konstanten für das Dämpfungsverhältnis verfügbar:

Abbildung 2: Hohe Absprünge

Abbildung 3: Mittlere Absprünge

Abbildung 4: Niedrige Absprünge

Abbildung 5: Kein Absprung

Das standardmäßige Dämpfungsverhältnis ist DAMPING_RATIO_MEDIUM_BOUNCY.

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, mit der die Stärke der Feder gemessen wird. Eine steife Feder wirkt stärker auf das befestigte Objekt, wenn sich die Feder nicht in der Ruheposition befindet. So fügen Sie der Feder die Steifheit hinzu:

  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 Steifheitswert, den Sie der Feder hinzufügen möchten. Die Methode gibt das Federkraftobjekt zurück, auf dem die Steifheit festgelegt 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: Niedrige Steifheit

Abbildung 9: Sehr niedrige Steifheit

Die Standardsteifheit ist auf STIFFNESS_MEDIUM festgelegt.

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 zur Standardfederkraft können Sie eine benutzerdefinierte Federkraft erstellen. Durch die benutzerdefinierte Federkraft können Sie dieselbe Federkraft auf mehrere Federanimationen verteilen. Nachdem Sie die Federkraft erstellt haben, können Sie Eigenschaften wie Dämpfungsverhältnis und Steifheit festlegen.

  1. Erstellen Sie ein SpringForce-Objekt.

    SpringForce force = new SpringForce();

  2. Weisen Sie die Attribute durch Aufrufen der entsprechenden Methoden zu. 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 Feder auf die Animation festzulegen.

    setSpring(force);

Animation starten

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

animateToFinalPosition() werden zwei Aufgaben ausgeführt:

  • 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 bei Bedarf die Animation startet, können Sie diese Methode jederzeit aufrufen, um den Verlauf einer Animation zu ändern. Bei einer verketteten Federanimation hängt die Animation einer Ansicht beispielsweise von einer anderen Ansicht ab. Für solche Animationen ist die Methode animateToFinalPosition() praktischer. Wenn Sie diese Methode in einer verketteten Federanimation verwenden, müssen Sie sich keine Sorgen machen, ob die Animation, die Sie als Nächstes aktualisieren möchten, gerade ausgeführt wird.

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

Demo für verkettete Federn
Abbildung 10. Demo zu verketteten Federn

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

Die Methode start() legt den Attributwert nicht sofort auf den Startwert fest. Der Attributwert ändert sich bei jedem Animationspuls vor dem Zeichendurchlauf. Daher spiegeln sich die Änderungen im nächsten Frame wider, als ob die Werte sofort festgelegt 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. Eine ideale Situation, in der Sie die Unterhaltung abbrechen oder zum Ende springen müssen, wenn eine Nutzerinteraktion erfordert, dass die Animation sofort beendet wird. Das passiert meistens, wenn ein Nutzer die App abrupt beendet oder die Ansicht unsichtbar wird.

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

Bevor Sie die Animation beenden können, müssen Sie zuerst den Zustand der Feder prüfen. Wenn der Zustand ungedampft ist, kann die Animation niemals die Ruheposition erreichen. Um den Zustand der Feder zu prüfen, rufen Sie die Methode canSkipToEnd() auf. Ist die Feder gedämpft, 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 verursacht die Methode skipToEnd() einen visuellen Sprung.