Animuj ruch za pomocą fizyki sprężyny

Wypróbuj sposób tworzenia wiadomości
Jetpack Compose to zalecany zestaw narzędzi UI na Androida. Dowiedz się, jak korzystać z animacji w sekcji Tworzenie

Ruch w zależności od fizyki jest spowodowany siłą. Siła sprężyna to jedna z tych sił, które kierują interaktywność i ruchem. Siła sprężyna ma takie właściwości: tłumienie i sztywność. W animacji opartej na sprężynie wartość i prędkość są obliczane na podstawie siły sprężysty stosowanej do każdej klatki.

Jeśli chcesz, by animacje w aplikacji były zwalniane tylko w jednym kierunku, rozważ użycie animacji przesuwu opartej na tarciu.

Cykl życia animacji sprężyn

W animacji sprężynowej klasa SpringForce pozwala dostosować sztywność sprężyny, jej współczynnik tłumienia i jej ostateczną pozycję. Po rozpoczęciu animacji siła sprężynia aktualizuje wartość animacji i prędkość na każdej klatce. Animacja trwa, dopóki siła sprężyna nie osiągnie równowagi.

Jeśli na przykład przeciągniesz ikonę aplikacji wokół ekranu, a później ją zwolnisz, unosząc palec z ikony, ikona wróci na swoje miejsce dzięki niewidocznej, ale znanej siły.

Podobny efekt sprężyny przedstawia Rysunek 1. Znak plusa (+) w środku okręgu wskazuje siłę działania gestu dotykowego.

Wersja wiosenna
Rysunek 1. Efekt wiosennej premiery

Utwórz animację wiosny

Ogólne czynności, które musisz wykonać, aby utworzyć animację wiosenną w Twojej aplikacji, to:

W sekcjach poniżej szczegółowo omówiono ogólne etapy tworzenia animacji wiosennej.

Dodawanie biblioteki pomocy

Aby korzystać z biblioteki pomocy opartej na fizyce, musisz dodać ją do projektu w ten sposób:

  1. Otwórz plik build.gradle modułu aplikacji.
  2. Dodaj bibliotekę pomocy do sekcji dependencies.

    Odlotowy

            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")
            }
            

    Aby wyświetlić bieżące wersje tej biblioteki, zapoznaj się z informacjami o animacji dynamicznej na stronie wersji.

Utwórz animację wiosny

Klasa SpringAnimation pozwala utworzyć dla obiektu animację sprężyny. Aby utworzyć animację sprężyn, musisz utworzyć instancję klasy SpringAnimation i podać obiekt, właściwość obiektu, którą chcesz animować, oraz opcjonalną końcową pozycję sprężyny, w której ma się zatrzymać animacja.

Uwaga: w momencie tworzenia animacji sprężyny jej końcowe położenie jest opcjonalne. Trzeba jednak go zdefiniować przed rozpoczęciem animacji.

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

Animacja sprężynowa może animować widoki na ekranie, zmieniając ich rzeczywiste właściwości. W systemie dostępne są te widoki:

  • ALPHA: reprezentuje przezroczystość widoku alfa. Domyślna wartość wynosi 1 (nieprzezroczyste), a 0 oznacza pełną przezroczystość (niewidoczną).
  • TRANSLATION_X, TRANSLATION_Y i TRANSLATION_Z: te właściwości określają, gdzie znajduje się widok jako delta od jego lewej współrzędnych, górnej współrzędnej oraz wysokości, które są ustawiane przez kontener układu.
  • ROTATION, ROTATION_X i ROTATION_Y: te właściwości określają obrót w 2D (właściwość rotation) i 3D wokół punktu obrotu.
  • SCROLL_X i SCROLL_Y: te właściwości wskazują odsunięcie strony źródła od lewej i górnej krawędzi w pikselach. Wskazuje też pozycję w postaci stopnia przewinięcia strony.
  • SCALE_X i SCALE_Y: te właściwości określają skalowanie 2D widoku wokół jego punktu obrotu.
  • X, Y i Z: to podstawowe właściwości narzędziowe, które opisują końcową lokalizację widoku w jego kontenerze.

Zarejestruj detektory

Klasa DynamicAnimation udostępnia 2 detektory: OnAnimationUpdateListener i OnAnimationEndListener. Te detektory nasłuchują aktualizacji animacji, np. informacji o zmianie jej wartości lub końca animacji.

Moduł OnAnimationUpdateListener

Jeśli chcesz animować wiele widoków, by utworzyć animację łańcuchową, możesz skonfigurować OnAnimationUpdateListener tak, by odbierał wywołanie zwrotne za każdym razem, gdy nastąpi zmiana właściwości bieżącego widoku. Wywołanie zwrotne powiadamia drugi widok, aby zaktualizował pozycję sprężyny na podstawie zmian wprowadzonych we właściwości bieżącego widoku. Aby zarejestrować odbiornik, wykonaj te czynności:

  1. Wywołaj metodę addUpdateListener() i dołącz do animacji detektor.

    Uwaga: przed rozpoczęciem animacji musisz zarejestrować odbiornik aktualizacji. Detektor aktualizacji powinien być jednak zarejestrowany tylko wtedy, gdy potrzebna jest aktualizacja wartości animacji w przypadku poszczególnych klatek. Detektor aktualizacji uniemożliwia uruchomienie animacji w oddzielnym wątku.

  2. Zastąp metodę onAnimationUpdate(), aby powiadomić element wywołujący o zmianie w bieżącym obiekcie. Poniższy przykładowy kod ilustruje ogólne użycie atrybutu 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);
    }
});

Parametr OnAnimationEndListener

OnAnimationEndListener powiadamia o końcu animacji. Możesz skonfigurować odbiornik, aby odbierał wywołanie zwrotne, gdy animacja osiągnie równowagę lub zostanie anulowana. Aby zarejestrować odbiornik, wykonaj te czynności:

  1. Wywołaj metodę addEndListener() i dołącz do animacji detektor.
  2. Zastąp metodę onAnimationEnd(), aby otrzymywać powiadomienia, gdy animacja osiągnie równowagę lub zostanie anulowana.

Usuń detektory

Aby przestać otrzymywać wywołania zwrotne aktualizacji animacji i wywołania zwrotne animacji, wywołaj odpowiednio metody removeUpdateListener() i removeEndListener().

Ustaw wartość początkową animacji

Aby ustawić wartość początkową animacji, wywołaj metodę setStartValue() i przekaż wartość początkową animacji. Jeśli nie ustawisz wartości początkowej, jako wartości początkowej animacja użyje bieżącej wartości właściwości obiektu.

Ustaw zakres wartości animacji

Jeśli chcesz ograniczyć wartość właściwości do określonego zakresu, możesz ustawić minimalne i maksymalne wartości animacji. Pomaga też kontrolować zakres w przypadku animowanych właściwości o wbudowanym zakresie, np. alfa (od 0 do 1).

  • Aby ustawić wartość minimalną, wywołaj metodę setMinValue() i przekaż minimalną wartość właściwości.
  • Aby ustawić wartość maksymalną, wywołaj metodę setMaxValue() i przekaż maksymalną wartość właściwości.

Obie metody zwracają animację, dla której ustawiana jest wartość.

Uwaga: jeśli masz ustawioną wartość początkową i zdefiniowany zakres wartości animacji, upewnij się, że wartość początkowa mieści się w zakresie wartości minimalnym i maksymalnym.

Ustaw prędkość początkową

Prędkość początkowa określa szybkość, z jaką zmienia się właściwość animacji na początku animacji. Domyślna prędkość początkowa to zero pikseli na sekundę. Prędkość można ustawić szybkością gestów dotykowych lub ustawiając stałą wartość jako prędkość początkową. Jeśli zdecydujesz się podać stałą wartość, najlepiej określ ją w dp na sekundę, a potem przekonwertuj ją na piksele na sekundę. Określenie wartości w dp na sekundę pozwala określić prędkość niezależnie od gęstości i formatów. Więcej informacji o konwertowaniu wartości na piksele na sekundę znajdziesz w sekcji Konwertowanie dp na sekundę na piksele na sekundę.

Aby ustawić prędkość, wywołaj metodę setStartVelocity() i przekaż ją w pikselach na sekundę. Metoda zwraca obiekt siły sprężyny, na który ustawiono prędkość.

Uwaga: użyj metody klas GestureDetector.OnGestureListener lub VelocityTracker do pobierania i obliczania szybkości gestów dotykowych.

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

Konwertuję dp na sekundę na piksele na sekundę

Prędkość sprężyny musi być wyrażona w pikselach na sekundę. Jeśli jako początek prędkości wybierzesz stałą wartość, podaj wartość w dp na sekundę, a potem przekonwertuj ją na piksele na sekundę. Do konwersji użyj metody applyDimension() z klasy TypedValue. Zapoznaj się z tym przykładowym kodem:

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

Określ właściwości sprężyny

Klasa SpringForce określa metody pobierania i ustawiania dla poszczególnych właściwości sprężyny, takich jak współczynnik tłumienia i sztywność. Aby ustawić właściwości sprężyny, musisz pobrać obiekt sił sprężyny lub utworzyć własną siłę sprężyny, według której możesz określić te właściwości. Więcej informacji o tworzeniu niestandardowej siły sprężyny znajdziesz w sekcji Tworzenie niestandardowej siły sprężyny.

Wskazówka: korzystając z metod ustawiających, możesz utworzyć łańcuch metod, ponieważ wszystkie metody ustawiające zwracają obiekt siły sprężysty.

Współczynnik tłumienia

Współczynnik tłumienia oznacza stopniowe zmniejszenie oscylacji sprężyny. Korzystając z współczynnika tłumienia, możesz określić, jak szybko zanikają oscylacje między jednym odbiciem a drugim. Wiosnę można zwilżyć na 4 sposoby:

  • Nadmiarowe tłumienie ma miejsce, gdy współczynnik tłumienia jest większy niż 1. Obiekt delikatnie wróci do pozycji spoczynkowej.
  • Tłumienie krytyczne ma miejsce, gdy współczynnik tłumienia jest równy 1. Pozwala on obiektowi wrócić do pozycji spoczynkowej w najkrótszym czasie.
  • Wyciszanie tłumienia występuje, gdy współczynnik tłumienia jest niższy niż 1. Pozwala on wielokrotnie przekroczyć limit obiektów, przechodząc pozycję spoczynku, a następnie stopniowo osiąga pozycję spoczynku.
  • Brak tłumienia występuje, gdy współczynnik tłumienia wynosi zero. Pozwala on obiektowi oscylować w nieskończoność.

Aby dodać współczynnik tłumienia do sprężyny, wykonaj te czynności:

  1. Wywołaj metodę getSpring(), aby pobrać sprężynę i dodać współczynnik tłumienia.
  2. Wywołaj metodę setDampingRatio() i prześlij współczynnik tłumienia, który chcesz dodać do sprężyny. Metoda zwraca obiekt siły sprężyny, dla którego ustawiono współczynnik tłumienia.

    Uwaga: współczynnik tłumienia musi być liczbą nieujemną. Jeśli ustawisz współczynnik tłumienia na 0, sprężyna nigdy nie dotrze do pozycji spoczynkowej. Oznacza to, że oscyluje w nieskończoność.

W systemie dostępne są takie stałe współczynniki tłumienia:

Rys. 2. Wysokie odrzucenie

Rys. 3. Średnie odbicie

Rys. 4. Niskie odrzucenie

Rys. 5. Brak odrzuceń

Domyślny współczynnik tłumienia ustawiony jest na 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);
…

Sztywność

Sztywność określa stałą sprężynową, która mierzy jej siłę. Sztywna sprężyna działa z większą siłą na przymocowany obiekt, gdy sprężyna nie jest w pozycji spoczynkowej. Aby dodać sztywność sprężyny, wykonaj te czynności:

  1. Wywołaj metodę getSpring(), aby pobrać sprężynę i dodać sztywność.
  2. Wywołaj metodę setStiffness() i przekaż wartość sztywności, którą chcesz dodać do sprężyny. Metoda zwraca obiekt siły sprężyny, dla której ustawiono sztywność.

    Uwaga: sztywność musi być liczbą dodatnią.

W układzie dostępne są takie stałe sztywność:

Rysunek 6. Wysoka sztywność

Rysunek 7. Średnia sztywność

Rysunek 8. Niska sztywność

Rysunek 9. Bardzo niska sztywność

Domyślna sztywność to STIFFNESS_MEDIUM.

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

Utwórz niestandardową siłę sprężyny

Zamiast używać domyślnej siły sprężyny, możesz utworzyć własną siłę sprężyny. Niestandardowa siła sprężynowa pozwala współdzielić tę samą instancję siły sprężystości w wielu animacjach sprężyny. Po utworzeniu siły sprężyny możesz ustawić takie właściwości jak współczynnik tłumienia i sztywność.

  1. Utwórz obiekt SpringForce.

    SpringForce force = new SpringForce();

  2. Przypisz właściwości, wywołując odpowiednie metody. Możesz też utworzyć łańcuch metod.

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

  3. Wywołaj metodę setSpring(), aby ustawić sprężynę na animację.

    setSpring(force);

Rozpocznij animację

Animację wiosenną możesz rozpocząć na 2 sposoby: wywołując start() lub wywołując metodę animateToFinalPosition(). Obie metody muszą być wywoływane w wątku głównym.

Metoda animateToFinalPosition() wykonuje 2 zadania:

  • Określa końcowe położenie sprężyny.
  • Uruchamia animację, jeśli jeszcze się nie rozpoczęła.

Ponieważ metoda aktualizuje ostatnią pozycję sprężyny i w razie potrzeby uruchamia animację, możesz ją wywołać w dowolnym momencie, aby zmienić przebieg animacji. Na przykład w animacji sprężyny łańcuchowej animacja jednego widoku zależy od innego. Do takiej animacji lepiej użyć metody animateToFinalPosition(). Dzięki tej metodzie w animacji sprężyny łańcuchowej nie musisz martwić się, czy animacja, którą chcesz zaktualizować jako następną, jest obecnie uruchomiona.

Rysunek 10 przedstawia animację sprężyny łańcuchowej, w której animacja jednego widoku zależy od innego.

Sprężynowa wersja demonstracyjna sprężyn
Rysunek 10. Sprężynowa wersja demonstracyjna sprężynowego łańcucha

Aby użyć metody animateToFinalPosition(), wywołaj metodę animateToFinalPosition() i przekaż resztę sprężyny. Możesz też ustawić spoczynkową pozycję sprężyny, wywołując metodę setFinalPosition().

Metoda start() nie ustawia od razu wartości właściwości na wartość początkową. Wartość właściwości zmienia się przy każdym pulsie animacji, który ma miejsce przed przejściem przez rysowanie. W efekcie zmiany są odzwierciedlane w następnej ramce, tak jakby wartości były ustawiane natychmiast.

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

Anuluj animację

Możesz anulować lub przejść do końca animacji. Idealną sytuacją, w której trzeba anulować lub przejść do jej końca, jest sytuacja, w której interakcja użytkownika wymaga natychmiastowego zakończenia animacji. Najczęściej dzieje się tak, gdy użytkownik nagle zamknie aplikację lub widok staje się niewidoczny.

Istnieją 2 metody zakończenia animacji. Metoda cancel() kończy animację w miejscu, w którym się znajduje. Metoda skipToEnd() pomija animację do wartości końcowej, a potem ją kończy.

Przed zakończeniem animacji musisz najpierw sprawdzić stan sprężyny. Jeśli stan jest niewyciszony, animacja nie osiągnie pozycji spoczynku. Aby sprawdzić stan sprężyny, wywołaj metodę canSkipToEnd(). Jeśli sprężyna jest tłumiona, metoda zwraca true, w przeciwnym razie false.

Gdy poznasz stan sprężyny, możesz zakończyć animację, korzystając z metody skipToEnd() lub cancel(). Metoda cancel() musi być wywoływana tylko w wątku głównym.

Uwaga: ogólnie metoda skipToEnd() powoduje wizualny skok.