System animacji właściwości to stabilna platforma, która pozwala animować niemal wszystko. Możesz zdefiniować animację, która zmienia z czasem dowolną właściwość obiektu niezależnie od tego, czy jest wyświetlana na ekranie czy nie. Animacja właściwości zmienia wartość właściwości (pole w obiekcie) przez określony czas. Aby coś animować, określ właściwość obiektu, którą chcesz animować, np. położenie obiektu na ekranie, czas trwania animacji i wartości, pomiędzy którymi chcesz animować obiekt.
System animacji właściwości umożliwia definiowanie tych właściwości animacji:
- Czas trwania: możesz określić czas trwania animacji. Domyślna długość to 300 ms.
- Interpolacja czasowa: możesz określić sposób obliczania wartości właściwości jako funkcji bieżącego czasu trwania animacji.
- Liczba powtórzeń i zachowanie: możesz określić, czy animacja ma być powtarzana na końcu czasu trwania, oraz ile razy ma być powtarzana. Możesz też określić, czy animacja ma być odtwarzana wstecz. Włączenie tej opcji powoduje odtwarzanie animacji do przodu, a potem do tyłu, aż zostanie osiągnięta liczba powtórzeń.
- Zestawy animatorów: możesz grupować animacje w zbiory logiczne, które odtwarzają się razem lub po kolei lub po określonych opóźnieniach.
- Opóźnienie odświeżania klatki: możesz określić, jak często mają być odświeżane klatki animacji. Domyślnie ustawione jest odświeżanie co 10 ms, ale szybkość, z jaką aplikacja może odświeżać klatki, zależy od ogólnego obciążenia systemu oraz od szybkości, z jaką system może obsługiwać bazowy licznik czasu.
Aby zobaczyć pełny przykład animacji właściwości, zapoznaj się z klasą ChangeColor
w przykładzie z CustomMove na GitHubie.
Jak działa animacja właściwości
Zobaczmy, jak działa animacja, korzystając z prostego przykładu. Rysunek 1 przedstawia hipotetyczny obiekt animowany za pomocą właściwości x
, która reprezentuje jego poziome położenie na ekranie. Czas trwania animacji jest ustawiony na 40 ms, a odległość – 40 pikseli. Co 10 ms, co jest domyślną częstotliwością odświeżania klatek, obiekt przesuwa się w poziomie o 10 pikseli. Po upływie 40 ms animacja zatrzymuje się, a obiekt kończy się w pozycji poziomej 40. To przykład animacji z interpolacją liniową, co oznacza, że obiekt porusza się ze stałą prędkością.
Możesz także określić animacje z interpolacją nieliniową. Rysunek 2 ilustruje hipotetyczny obiekt, który przyspiesza na początku animacji i spowalnia na końcu. Obiekt przesuwa się o 40 pikseli w czasie 40 ms, ale nieliniowo. Na początku animacja przyspiesza do połowy, a następnie zwalnia od połowy do końca. Jak pokazano na Rysunku 2, odległość pokonana na początku i na końcu animacji jest mniejsza niż w środku.
Przyjrzyjmy się, jak ważne komponenty systemu animacji właściwości obliczają animacje podobne do przedstawionych powyżej. Rysunek 3 przedstawia wzajemne współdziałanie klas głównych.
Obiekt ValueAnimator
śledzi czas trwania animacji, np. czas jej trwania i aktualną wartość właściwości, którą tworzy.
W elemencie ValueAnimator
zawarty jest element TimeInterpolator
, który określa interpolację animacji, oraz element TypeEvaluator
, który określa sposób obliczania wartości animowanej właściwości. Na przykład na Rysunku 2 użyto wartości TimeInterpolator
jako AccelerateDecelerateInterpolator
, a TypeEvaluator
– IntEvaluator
.
Aby rozpocząć animację, utwórz obiekt ValueAnimator
, nadaj mu wartości początkowe i końcowe właściwości, którą chcesz animować, oraz czas trwania animacji. Gdy wywołasz start()
, rozpocznie się animacja. W trakcie całej animacji funkcja ValueAnimator
oblicza upływ czasu od 0 do 1 na podstawie czasu trwania animacji i czasu, który upłynął. Ułamek upływu to procent czasu zakończenia animacji. 0 oznacza 0%, a 1 oznacza 100%. Na przykład na Rysunku 1 ułamek upływu czasu w przypadku t = 10 ms wyniesie 0, 25, ponieważ łączny czas trwania wynosi t = 40 ms.
Gdy funkcja ValueAnimator
oblicza ułamek, wywołuje obecnie ustawioną wartość TimeInterpolator
, aby obliczyć ułamek interpolowany. Ułamek interpolowany mapuje upływ czasu na nowy ułamek z uwzględnieniem ustawionej interpolacji czasowej. Na przykład na Rysunku 2, ponieważ animacja przyspiesza, ułamek interpolowany, czyli około 0, 15, jest mniejszy niż ułamek, który upłynął (0, 25) w czasie t = 10 ms. Na Rysunku 1 ułamek interpolowany jest zawsze taki sam jak ułamek, który upłynął.
Podczas obliczania ułamka interpolowanego ValueAnimator
wywołuje odpowiednią wartość TypeEvaluator
, aby obliczyć wartość animowanej właściwości na podstawie ułamka interpolowanego, wartości początkowej i końcowej wartości animacji. Na przykład na Rysunku 2 ułamek interpolowany wynosił 0, 15 w czasie t = 10 ms, więc wartość dla właściwości w tym czasie będzie wynosić 0, 15 × (40 – 0), czyli 6.
Czym różni się animacja właściwości od animacji widoku
System animacji widoku umożliwia animowanie tylko obiektów View
. Jeśli chcesz animować obiekty inne niż View
, musisz wdrożyć własny kod. Ogranicza on także tym, że naraża on tylko niektóre aspekty obiektu View
do animowania, takie jak na przykład skalowanie i obrót widoku, ale nie na przykład kolor tła.
Inną wadą systemu animacji widoku jest to, że modyfikuje się on tylko w miejscu, w którym został narysowany widok, a nie sam widok. Jeśli na przykład animowany przycisk przesuwa się po ekranie, przycisk jest rysowany poprawnie, ale rzeczywista lokalizacja, w której można go kliknąć, nie zmienia się, więc musisz zaimplementować własną logikę.
Dzięki systemowi animacji właściwości te ograniczenia są całkowicie usuwane, możesz animować dowolną właściwość dowolnych obiektów (View i innych obiektów), a sam obiekt jest w rzeczywistości modyfikowany. System animacji właściwości jest też bardziej wydajny w sposobie przeprowadzania animacji. Ogólnie rzecz biorąc, przypisujesz animatorów do właściwości, które chcesz animować, takich jak kolor, położenie lub rozmiar, i możesz definiować takie aspekty animacji, jak interpolacja i synchronizacja wielu animatorów.
Konfiguracja systemu wyświetlania animacji zajmuje jednak mniej czasu i wymaga mniej kodu. Jeśli animacje widoku danych spełniają wszystkie Twoje oczekiwania lub jeśli Twój kod działa już zgodnie z oczekiwaniami, nie musisz korzystać z systemu animacji właściwości. Wykorzystanie obu systemów animacji może też być przydatne w różnych sytuacjach, jeśli tylko zaistnieje taka potrzeba.
Omówienie interfejsu API
Większość interfejsów API systemu animacji właściwości znajdziesz w tym pliku: android.animation
. System animacji widoków definiuje wiele interpolatorów w android.view.animation
, więc możesz ich też używać w systemie animacji właściwości. W tabelach poniżej opisujemy główne komponenty systemu animacji właściwości.
Klasa Animator
określa podstawową strukturę do tworzenia animacji. Zwykle nie używasz tej klasy bezpośrednio, ponieważ zapewnia ona tylko nieznaczne funkcje, które trzeba rozszerzyć, aby w pełni obsługiwać wartości animowane. Te podklasy są rozszerzone o Animator
:
Kategoria | Opis |
---|---|
ValueAnimator |
Główny mechanizm czasowy animacji właściwości, który oblicza również wartości właściwości do animacji. Obejmuje wszystkie główne funkcje, które pozwalają obliczać wartości animacji, szczegółowe dane o czasie trwania każdej animacji, informacje o tym, czy animacja się powtarza, detektory odbierające zdarzenia aktualizacji oraz możliwość ustawiania niestandardowych typów do oceny. Właściwości animowania składają się z 2 elementów: obliczania wartości animowanych i ustawiania ich dla animowanego obiektu i właściwości. ValueAnimator nie wykonuje drugiego utworu, dlatego musisz nasłuchiwać aktualizacji wartości obliczonych przez ValueAnimator i zmodyfikować obiekty, które chcesz animować zgodnie z własną logiką. Więcej informacji znajdziesz w sekcji dotyczącej animowania za pomocą ValueAnimator. |
ObjectAnimator |
Podklasa klasy ValueAnimator , która pozwala ustawić animowany obiekt docelowy i właściwość obiektu. Ta klasa odpowiednio aktualizuje właściwość podczas obliczania nowej wartości animacji. Najlepiej używać ObjectAnimator , ponieważ ułatwia to animowanie wartości w obiektach docelowych. Czasami warto jednak używać metody ValueAnimator bezpośrednio, ponieważ ObjectAnimator ma więcej ograniczeń, na przykład wymaganie obecności określonych metod akcesora w obiekcie docelowym. |
AnimatorSet |
Udostępnia mechanizm grupowania animacji, tak aby uruchamiały się względem siebie. Możesz ustawić animacje do odtwarzania razem, sekwencyjnego lub po określonym opóźnieniu. Więcej informacji znajdziesz w sekcji na temat tworzenia układu wielu animacji za pomocą zestawów animacji. |
Oceniający informują system animacji właściwości, jak obliczać wartości danej właściwości. Biorą pod uwagę dane o czasie dostarczane przez klasę Animator
, wartość początkową i końcową animacji, a następnie na podstawie tych danych obliczają animowane wartości właściwości. System animacji właściwości udostępnia następujące weryfikatory:
Klasa/interfejs | Opis |
---|---|
IntEvaluator |
Domyślny wskaźnik do obliczania wartości właściwości int . |
FloatEvaluator |
Domyślny wskaźnik do obliczania wartości właściwości float . |
ArgbEvaluator |
Domyślny narzędzie do obliczania wartości właściwości koloru, które są przedstawiane jako wartości szesnastkowe. |
TypeEvaluator |
Interfejs, który umożliwia utworzenie własnego narzędzia do oceny. Jeśli animujesz właściwość obiektu, która nie jest właściwością int , float ani kolorem, musisz zaimplementować interfejs TypeEvaluator , aby określić sposób obliczania animowanych wartości właściwości obiektu. Możesz też określić niestandardowy TypeEvaluator dla wartości int , float i kolorów, jeśli chcesz, aby były przetwarzane inaczej niż domyślne.
Więcej informacji o tworzeniu niestandardowego oceniającego znajdziesz w sekcji poświęconej korzystaniu z narzędzia TypeEvaluator. |
Interpolator czasu definiuje sposób obliczania konkretnych wartości w animacji jako funkcja czasu. Na przykład możesz określić animacje, które mają się odtwarzać liniowo w obrębie całej animacji, co oznacza, że porusza się ona równomiernie w trakcie jej trwania, albo określić, by wykorzystywała czas nieliniowy, np. przyspieszać na początku i spowalniać na końcu. Tabela 3 opisuje interpolatory zawarte w komponencie android.view.animation
. Jeśli żaden z dostępnych interpolatorów nie odpowiada Twoim potrzebom, zaimplementuj interfejs TimeInterpolator
i utwórz własny. Więcej informacji o tworzeniu interpolatora niestandardowego znajdziesz w sekcji Korzystanie z interpolatorów.
Klasa/interfejs | Opis |
---|---|
AccelerateDecelerateInterpolator |
Interpolator, którego tempo zmian powoli się rozpoczyna i kończy, a przyspiesza w środku. |
AccelerateInterpolator |
Interpolator, którego tempo zmian zaczyna się powoli, a następnie przyspiesza. |
AnticipateInterpolator |
Interpolator, którego zmiana rozpoczyna się wstecz, a potem przesuwa do przodu. |
AnticipateOvershootInterpolator |
Interpolator, którego zmiana rozpoczyna się wstecz, przesuwa do przodu i przekracza wartość docelową, a następnie wraca do ostatecznej wartości. |
BounceInterpolator |
Interpolator, którego zmiana zostaje odbita na końcu. |
CycleInterpolator |
Interpolator, którego animacja powtarza się przez określoną liczbę cykli. |
DecelerateInterpolator |
Interpolator, którego szybkość zmian zaczyna się szybko, a następnie zwalnia. |
LinearInterpolator |
Interpolator, którego szybkość zmian jest stała. |
OvershootInterpolator |
Interpolator, którego zmiana zostaje odwrócona do przodu i przekracza ostatnią wartość, a następnie zwraca wartość. |
TimeInterpolator |
Interfejs umożliwiający wdrożenie własnego interpolatora. |
Animacja za pomocą ValueAnimator
Klasa ValueAnimator
umożliwia animowanie wartości określonego typu przez czas trwania animacji przez określenie zbioru wartości int
, float
lub kolorów, które mają być animowane. Otrzymasz urządzenie ValueAnimator
, wywołując jedną z metod fabrycznych: ofInt()
, ofFloat()
lub ofObject()
. Na przykład:
Kotlin
ValueAnimator.ofFloat(0f, 100f).apply { duration = 1000 start() }
Java
ValueAnimator animation = ValueAnimator.ofFloat(0f, 100f); animation.setDuration(1000); animation.start();
W tym kodzie ValueAnimator
zaczyna obliczać wartości animacji (od 0 do 100) dla okresu 1000 ms, gdy działa metoda start()
.
Możesz też określić niestandardowy typ animacji, wykonując te czynności:
Kotlin
ValueAnimator.ofObject(MyTypeEvaluator(), startPropertyValue, endPropertyValue).apply { duration = 1000 start() }
Java
ValueAnimator animation = ValueAnimator.ofObject(new MyTypeEvaluator(), startPropertyValue, endPropertyValue); animation.setDuration(1000); animation.start();
W tym kodzie ValueAnimator
zaczyna obliczać wartości animacji od startPropertyValue
do endPropertyValue
, korzystając z logiki dostarczonej przez MyTypeEvaluator
, przez okres 1000 ms, gdy działa metoda start()
.
Aby użyć wartości animacji, dodaj AnimatorUpdateListener
do obiektu ValueAnimator
, jak pokazano w tym kodzie:
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); } });
W metodzie onAnimationUpdate()
możesz uzyskać dostęp do zaktualizowanej wartości animacji i użyć jej we właściwości jednego z widoków. Więcej informacji o detektorach znajdziesz w sekcji o detektorach animacji.
Tworzenie animacji z użyciem narzędzia ObjectAnimator
ObjectAnimator
jest podklasą klasy ValueAnimator
(omówioną w poprzedniej sekcji) i łączy mechanizm pomiaru czasu i obliczanie wartości ValueAnimator
z możliwością animowania właściwości nazwanej obiektu docelowego. Bardzo ułatwia to animowanie obiektów, ponieważ nie trzeba już implementować ValueAnimator.AnimatorUpdateListener
, ponieważ animowana właściwość aktualizuje się automatycznie.
Tworzenie wystąpienia obiektu ObjectAnimator
jest podobne do ValueAnimator
, ale musisz też określić obiekt, nazwę właściwości tego obiektu (w postaci ciągu znaków) oraz wartości, które mają być animowane między:
Kotlin
ObjectAnimator.ofFloat(textView, "translationX", 100f).apply { duration = 1000 start() }
Java
ObjectAnimator animation = ObjectAnimator.ofFloat(textView, "translationX", 100f); animation.setDuration(1000); animation.start();
Aby właściwości się aktualizowały ObjectAnimator
, musisz wykonać te czynności:
- Animowana właściwość obiektu musi mieć funkcję seter (wielbłąd) w postaci
set<PropertyName>()
. Ponieważ właściwośćObjectAnimator
automatycznie aktualizuje właściwość podczas animacji, musi mieć dostęp do niej za pomocą tej metody ustawiania. Jeśli nazwa właściwości to np.foo
, potrzebujesz metodysetFoo()
. Jeśli ta metoda ustawiająca nie istnieje, masz 3 możliwości:- Dodaj do klasy metodę ustawiającą, jeśli masz do tego uprawnienia.
- Użyj klasy otoki, którą masz uprawnienia do zmieniania, która powinna otrzymywać wartość za pomocą prawidłowej metody ustawiającej i przekazywać ją do pierwotnego obiektu.
- Użyj w zamian zasady
ValueAnimator
.
- Jeśli określisz tylko jedną wartość parametru
values...
w jednej z metod fabrycznychObjectAnimator
, zostanie ona uznana za wartość końcową animacji. Dlatego właściwość obiektu, którą animujesz, musi mieć funkcję pobierania, która służy do uzyskania wartości początkowej animacji. Funkcja pobierająca musi mieć postaćget<PropertyName>()
. Jeśli np. nazwa właściwości tofoo
, potrzebujesz metodygetFoo()
. - Metody pobierające (w razie potrzeby) i ustawiające dla animowanej właściwości muszą działać na tym samym typie co wartości początkowa i końcowa, które podajesz w polu
ObjectAnimator
. Jeśli na przykład tworzysz takąObjectAnimator
, musisz miećtargetObject.setPropName(float)
itargetObject.getPropName()
:ObjectAnimator.ofFloat(targetObject, "propName", 1f)
- W zależności od animowanych właściwości lub obiektów może być konieczne wywołanie w widoku metody
invalidate()
, aby wymusić ponowne rysowanie ekranu ze zaktualizowanymi animowanymi wartościami. Możesz to zrobić w wywołaniu zwrotnymonAnimationUpdate()
. Na przykład animowanie właściwości koloru obiektu rysowalnego powoduje zaktualizowanie ekranu tylko wtedy, gdy ten obiekt jest ponownie rysowany. Wszystkie ustawienia właściwości w widoku danych, takie jaksetAlpha()
isetTranslationX()
, prawidłowo unieważniają widok, więc nie musisz unieważniać widoku podczas wywoływania tych metod z nowymi wartościami. Więcej informacji o detektorach znajdziesz w sekcji o detektorach animacji.
Utwórz choreografię wielu animacji za pomocą AnimatorSet
Często chcesz odtworzyć animację zależną od momentu rozpoczęcia lub zakończenia innej animacji. System Android umożliwia łączenie animacji w element AnimatorSet
, dzięki czemu możesz określić, czy animacje mają być uruchamiane jednocześnie, sekwencyjnie czy z określonym opóźnieniem. Obiekty AnimatorSet
możesz też zagnieżdżać wewnątrz siebie.
Ten fragment kodu odtwarza obiekty Animator
w taki sposób:
- Odtwarza:
bounceAnim
. - Wyświetla jednocześnie
squashAnim1
,squashAnim2
,stretchAnim1
istretchAnim2
. - Odtwarza:
bounceBackAnim
. - Odtwarza:
fadeAnim
.
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();
Detektory animacji
W trakcie trwania animacji możesz nasłuchiwać ważnych zdarzeń, korzystając z opisanych poniżej detektorów.
Animator.AnimatorListener
onAnimationStart()
– wywoływane po rozpoczęciu animacji.onAnimationEnd()
– wywoływane po zakończeniu animacji.onAnimationRepeat()
– wywoływane, gdy animacja się powtarza.onAnimationCancel()
– wywoływane po anulowaniu animacji. Anulowana animacja wywołuje też metodęonAnimationEnd()
niezależnie od tego, jak została zakończona.
ValueAnimator.AnimatorUpdateListener
-
onAnimationUpdate()
– wywoływane w każdej klatce animacji. Odsłuchaj to zdarzenie, aby użyć obliczonych wartości wygenerowanych przez funkcjęValueAnimator
podczas animacji. Aby użyć tej wartości, wyślij zapytanie do obiektuValueAnimator
przekazanego do zdarzenia, aby uzyskać bieżącą animowaną wartość za pomocą metodygetAnimatedValue()
. Jeśli używaszValueAnimator
, musisz wdrożyć ten detektor.W zależności od animowanych właściwości lub obiektów może być konieczne wywołanie w widoku
invalidate()
wywołaniainvalidate()
, aby wymusić ponowne rysowanie tego obszaru na ekranie z nowymi animowanymi wartościami. Na przykład animowanie właściwości koloru obiektu rysowalnego powoduje zaktualizowanie ekranu tylko wtedy, gdy ten obiekt jest ponownie rysowany. Wszystkie ustawienia właściwości w widoku danych, np.setAlpha()
isetTranslationX()
, prawidłowo unieważniają widok, więc nie musisz unieważniać widoku podczas wywoływania tych metod z nowymi wartościami.
-
Zamiast implementować interfejs Animator.AnimatorListener
, możesz rozszerzyć klasę AnimatorListenerAdapter
, jeśli nie chcesz implementować wszystkich metod interfejsu Animator.AnimatorListener
. Klasa AnimatorListenerAdapter
udostępnia puste implementacje metod, które możesz zastąpić.
Na przykład ten fragment kodu tworzy AnimatorListenerAdapter
tylko dla wywołania zwrotnego onAnimationEnd()
:
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()); }
Animuj zmiany układu obiektów ViewGroup
System animacji właściwości umożliwia animowanie zmian w obiektach ViewGroup, a także zapewnia prosty sposób animowania samych obiektów View.
Zmiany układu w grupie widoków możesz animować za pomocą klasy LayoutTransition
. Wyświetlenia w grupie widoków mogą przechodzić przez animację pojawiania się i znikania po dodaniu do niej lub jej usunięciu albo po wywołaniu metody setVisibility()
widoku danych za pomocą VISIBLE
, INVISIBLE
lub GONE
. Pozostałe widoki w grupie wyświetleń
mogą się pojawić na nowych pozycjach po dodaniu lub usunięciu widoków. Te animacje możesz zdefiniować w obiekcie LayoutTransition
, wywołując setAnimator()
i przekazując obiekt Animator
z jedną z tych stałych LayoutTransition
:
APPEARING
– flaga wskazująca animację wyświetlaną na elementach widocznych w kontenerze.CHANGE_APPEARING
– flaga wskazująca animację uruchamianą na elementach, które zmieniają się w wyniku pojawienia się w kontenerze nowego elementu.DISAPPEARING
– flaga wskazująca animację uruchamianą na elementach, które znikają z kontenera.CHANGE_DISAPPEARING
– flaga wskazująca animację uruchamianą na elementach, które zmieniają się w wyniku znikania elementu z kontenera.
Możesz definiować własne animacje niestandardowe dla tych 4 typów zdarzeń, aby dostosować wygląd przejść układu, lub po prostu wskazać systemowi animacji, by używał animacji domyślnych.
Aby ustawić atrybut android:animateLayoutchanges
na true
dla grupy widoków danych, wykonaj te czynności:
<LinearLayout android:orientation="vertical" android:layout_width="wrap_content" android:layout_height="match_parent" android:id="@+id/verticalContainer" android:animateLayoutChanges="true" />
Ustawienie tego atrybutu na wartość Prawda automatycznie powoduje animowanie widoków danych, które zostały dodane do grupy widoków danych lub z niej usunięte, a także pozostałe widoki w tej grupie.
Animacja zmian stanu widoku za pomocą StateListAnimator
Klasa StateListAnimator
umożliwia definiowanie animatorów uruchamianych po zmianie stanu widoku. Ten obiekt działa jako otoka obiektu Animator
, wywołując tę animację po każdej zmianie określonego stanu widoku (np. „naciśnięcie” lub „zaznaczenie”).
Element StateListAnimator
może być zdefiniowany w zasobie XML z elementem głównym <selector>
i podrzędnymi elementami <item>
. Każdy z nich określa inny stan widoku zdefiniowany przez klasę StateListAnimator
. Każdy element <item>
zawiera definicję zestawu animacji właściwości.
Na przykład ten plik tworzy animator listy stanów, który po naciśnięciu zmienia skalę X i Y widoku:
<?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>
Aby dołączyć do widoku animatora listy stanów, dodaj atrybut
android:stateListAnimator
w ten sposób:
<Button android:stateListAnimator="@xml/animate_scale" ... />
Teraz w przypadku zmiany stanu przycisku używane są animacje zdefiniowane w elemencie animate_scale.xml
.
Aby zamiast tego przypisać do widoku w kodzie animatora listy stanów, użyj metody AnimatorInflater.loadStateListAnimator()
i przypisz animatora do widoku za pomocą metody View.setStateListAnimator()
.
Zamiast animować właściwości widoku, możesz też odtworzyć rysowalną animację między zmianami stanu za pomocą funkcji AnimatedStateListDrawable
.
Niektóre widżety systemowe
w Androidzie 5.0 domyślnie używają tych animacji. Poniższy przykład pokazuje, jak zdefiniować AnimatedStateListDrawable
jako zasób XML:
<!-- 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>
Użyj narzędzia do oceny typów
Jeśli chcesz animować typ, który jest nieznany w systemie Android, możesz utworzyć własny ocenianie, implementując interfejs TypeEvaluator
. Typy znane w systemie Android to int
, float
lub kolor. Są one obsługiwane przez weryfikatorów typów IntEvaluator
, FloatEvaluator
i ArgbEvaluator
.
W interfejsie TypeEvaluator
można wdrożyć tylko jedną metodę – evaluate()
. Dzięki temu używane animatorzy mogą zwrócić odpowiednią wartość właściwości animacji w bieżącym punkcie animacji. Klasa FloatEvaluator
pokazuje, jak to zrobić:
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); } }
Uwaga: gdy jest uruchamiany ValueAnimator
(lub ObjectAnimator
), oblicza on bieżący ułamek animacji (wartość od 0 do 1), a następnie oblicza jego wersję interpolowaną w zależności od używanego interpolatora. Ułamek interpolowany to wartość, którą TypeEvaluator
otrzymuje dzięki parametrowi fraction
. Dlatego przy obliczaniu animowanych wartości nie musisz uwzględniać interpolatora.
Użyj interpolatorów
Interpolator określa sposób obliczania konkretnych wartości w animacji jako funkcji czasu. Na przykład możesz ustawić animacje, które będą przebiegały liniowo w całej animacji, czyli będzie ona przesuwała się równomiernie przez cały czas, albo możesz wskazać animacje do korzystania z czasu nieliniowego – np. użyć przyspieszenia lub zwolnienia na początku lub na końcu animacji.
Interpolatory w systemie animacji otrzymują od animacji ułamek reprezentujący czas trwania animacji. Interpolatory modyfikują ten ułamek tak, aby zbiegł się z typem animacji, który ma przedstawiać. System Android udostępnia w elemencie android.view.animation package
zestaw typowych interpolatorów. Jeśli żaden z nich nie odpowiada Twoim potrzebom, możesz wdrożyć interfejs TimeInterpolator
i utworzyć własny.
Poniżej przedstawiamy przykład porównanie ułamków interpolowanych przez domyślny interpolator AccelerateDecelerateInterpolator
i LinearInterpolator
.
LinearInterpolator
nie ma wpływu na ułamek, który upłynął. AccelerateDecelerateInterpolator
przyspiesza do animacji i zwalnia z niej. Logika tych interpolatorów definiuje się w następujących metodach:
Przyspieszenie DecelerateInterpolator
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; }
Interpolator liniowy
Kotlin
override fun getInterpolation(input: Float): Float = input
Java
@Override public float getInterpolation(float input) { return input; }
W tabeli poniżej znajdziesz przybliżone wartości obliczone przez te interpolatory dla animacji trwającej 1000 ms:
upłynęło: ms | Upływ ułamka/ułamek interpolowany (liniowy) | Ułamek zinterpolowany (przyspieszenie/obniżenie poziomu) |
---|---|---|
0 | 0 | 0 |
200 | 0,2 | 0,1 |
400 | 0,4 | 0,345 |
600 | 0,6 | 0,8 |
800 | 0,8 | 0,9 |
1000 | 1 | 1 |
Jak widać w tabeli, LinearInterpolator
zmienia wartości z tą samą szybkością, czyli 0,2 na każde 200 ms. AccelerateDecelerateInterpolator
zmienia wartości szybciej niż LinearInterpolator
między 200 ms a 600 ms i wolniej od 600 ms do 1000 ms.
Określanie klatek kluczowych
Obiekt Keyframe
składa się z pary czas/wartość, który umożliwia zdefiniowanie określonego stanu w konkretnym momencie animacji. Każda klatka kluczowa może też mieć własny interpolator kontrolujący zachowanie animacji w odstępie między czasem poprzedniej klatki kluczowej a czasem tej klatki kluczowej.
Aby utworzyć instancję obiektu Keyframe
, musisz użyć jednej z metod fabrycznych: ofInt()
, ofFloat()
lub ofObject()
, aby uzyskać odpowiedni typ Keyframe
. Następnie wywołaj metodę fabryczną ofKeyframe()
, aby uzyskać obiekt PropertyValuesHolder
. Gdy już będziesz mieć obiekt, możesz uzyskać animator, przesyłając do niego obiekt PropertyValuesHolder
i obiekt do animacji. Ten fragment kodu pokazuje, jak to zrobić:
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);
Animacja widoków
System animacji właściwości umożliwia uproszczoną animację obiektów View i ma kilka zalet w porównaniu z systemem animacji widoku. System animacji widoku przekształcił obiekty widoku, zmieniając sposób ich rysowania. Ta czynność została przeprowadzona w kontenerze każdego widoku danych, ponieważ widok ten nie miał żadnych właściwości do manipulowania. W rezultacie widok był animowany, ale nie spowodował zmian w samym obiekcie widoku. W efekcie obiekt nadal istniał w pierwotnej lokalizacji, mimo że został narysowany w innej lokalizacji na ekranie. Aby wyeliminować tę wadę, w Androidzie 3.0 dodano nowe właściwości oraz odpowiadające im metody getter i seter.
System animacji właściwości
może animować widoki na ekranie, zmieniając ich rzeczywiste właściwości. Widoki automatycznie wywołują metodę invalidate()
, aby odświeżyć ekran po każdej zmianie jego właściwości. Nowe właściwości klasy View
ułatwiające animacje właściwości to:
translationX
itranslationY
: te właściwości określają, gdzie znajduje się widok, w zależności od jego współrzędnych od lewej i od góry, które są ustawiane przez kontener układu.rotation
,rotationX
irotationY
: te właściwości określają obrót w 2D (właściwośćrotation
) i 3D wokół punktu obrotu.scaleX
iscaleY
: te właściwości sterują skalowaniem 2D widoku wokół jego punktu obrotu.pivotX
ipivotY
: te właściwości określają lokalizację punktu obrotu, wokół którego odbywają się przekształcenia obrotu i skalowania. Domyślnie punkt przestawny znajduje się na środku obiektu.x
iy
: to proste właściwości narzędziowe, które opisują końcową lokalizację widoku w jego kontenerze, stanowiącą sumę wartości lewo i góry oraz wartości translacjiX i przesunięciaY.alpha
: reprezentuje przezroczystość widoku alfa w widoku alfa. Domyślna wartość to 1 (nieprzezroczyste), a 0 oznacza pełną przezroczystość (niewidoczną).
Aby animować właściwość obiektu widoku, np. jej kolor lub wartość obrotu, musisz tylko utworzyć animatora właściwości i określić właściwość widoku, którą chcesz animować. Na przykład:
Kotlin
ObjectAnimator.ofFloat(myView, "rotation", 0f, 360f)
Java
ObjectAnimator.ofFloat(myView, "rotation", 0f, 360f);
Więcej informacji o tworzeniu animatorów znajdziesz w sekcjach poświęconych animowaniu za pomocą usług ValueAnimator i ObjectAnimator.
Tworzenie animacji za pomocą ViewPropertyAnimator
Obiekt ViewPropertyAnimator
pozwala w prosty sposób animować kilka właściwości elementu View
równolegle za pomocą pojedynczego obiektu Animator
. Zachowuje się podobnie do elementu ObjectAnimator
, bo zmienia rzeczywiste wartości jego właściwości, ale jest bardziej wydajny, jeśli animuje wiele właściwości naraz. Poza tym kod ViewPropertyAnimator
jest znacznie bardziej zwięzły i czytelny. Fragmenty kodu poniżej pokazują różnice w używaniu kilku obiektów ObjectAnimator
, 1 obiektu ObjectAnimator
i ViewPropertyAnimator
podczas jednoczesnej animowania właściwości x
i y
widoku.
Wiele obiektów ObjectAnimator
Kotlin
val animX = ObjectAnimator.ofFloat(myView, "x", 50f) val animY = ObjectAnimator.ofFloat(myView, "y", 100f) AnimatorSet().apply { playTogether(animX, animY) start() }
Java
ObjectAnimator animX = ObjectAnimator.ofFloat(myView, "x", 50f); ObjectAnimator animY = ObjectAnimator.ofFloat(myView, "y", 100f); AnimatorSet animSetXY = new AnimatorSet(); animSetXY.playTogether(animX, animY); animSetXY.start();
One ObjectAnimator
Kotlin
val pvhX = PropertyValuesHolder.ofFloat("x", 50f) val pvhY = PropertyValuesHolder.ofFloat("y", 100f) ObjectAnimator.ofPropertyValuesHolder(myView, pvhX, pvhY).start()
Java
PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("x", 50f); PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("y", 100f); ObjectAnimator.ofPropertyValuesHolder(myView, pvhX, pvhY).start();
ViewPropertyAnimator
Kotlin
myView.animate().x(50f).y(100f)
Java
myView.animate().x(50f).y(100f);
Więcej informacji na temat właściwości ViewPropertyAnimator
znajdziesz w odpowiednim poście na blogu dla deweloperów aplikacji na Androida.
Deklarowanie animacji w pliku XML
System animacji właściwości umożliwia deklarowanie animacji właściwości za pomocą kodu XML, zamiast robić to automatycznie. Dzięki zdefiniowaniu animacji w języku XML możesz ich łatwo używać ponownie w wielu działaniach i łatwiej edytować sekwencję animacji.
Aby odróżnić pliki animacji korzystające z nowych interfejsów API animacji właściwości od tych, które korzystają ze starszej platformy animacji widoku danych (od Androida 3.1), zapisz pliki XML animacji właściwości w katalogu res/animator/
.
Poniższe klasy animacji właściwości obsługują deklarację XML z tymi tagami XML:
ValueAnimator
–<animator>
ObjectAnimator
–<objectAnimator>
AnimatorSet
–<set>
Atrybuty, których możesz używać w deklaracji XML, znajdziesz w sekcji Zasoby animacji. W przykładzie poniżej dwa zestawy animacji obiektów są odtwarzane sekwencyjnie, a pierwszy zagnieżdżony zestaw odtwarza razem 2 animacje obiektów:
<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>
Aby uruchomić tę animację, musisz uzupełnić zasoby XML w swoim kodzie do obiektu AnimatorSet
, a następnie ustawić obiekty docelowe dla wszystkich animacji przed rozpoczęciem zestawu animacji. Wywołanie setTarget()
ustawia jako wygodę jeden obiekt docelowy dla wszystkich elementów podrzędnych obiektu AnimatorSet
. Poniższy kod pokazuje, jak to zrobić:
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();
ValueAnimator
możesz też zadeklarować w pliku XML, tak jak w tym przykładzie:
<animator xmlns:android="http://schemas.android.com/apk/res/android" android:duration="1000" android:valueType="floatType" android:valueFrom="0f" android:valueTo="-100f" />
Aby użyć poprzedniej wartości ValueAnimator
w kodzie, musisz powiększyć obiekt, dodać atrybut AnimatorUpdateListener
, uzyskać zaktualizowaną wartość animacji i użyć jej we właściwości jednego z widoków danych, jak w tym kodzie:
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();
Informacje o składni XML służącej do definiowania animacji właściwości znajdziesz w sekcji Zasoby animacji .
Potencjalny wpływ na wydajność interfejsu
Animatory aktualizujące interfejs użytkownika wykonują dodatkową pracę związaną z renderowaniem w przypadku każdej klatki, w której wyświetla się animacja. Z tego powodu korzystanie z animacji zużywających dużo zasobów może niekorzystnie wpłynąć na działanie aplikacji.
Na etap animacji w potoku renderowania dodawany jest czas potrzebny do animowania interfejsu użytkownika. Aby sprawdzić, czy Twoje animacje wpływają na wydajność aplikacji, włącz Renderowanie GPU w profilu i monitoruj etap animacji. Więcej informacji znajdziesz w artykule Przewodnik po renderowaniu GPU.