System animacji właściwości to solidna platforma, która umożliwia animowanie niemal wszystkiego. Możesz zdefiniować animację, która będzie zmieniać dowolną właściwość obiektu w czasie, niezależnie od tego, czy jest on rysowany na ekranie. Animacja właściwości zmienia wartość właściwości (pola w obiekcie) w określonym czasie. Aby coś animować, musisz określić właściwość obiektu, którą chcesz animować, np. jego położenie na ekranie, czas trwania animacji i wartości, między którymi ma się ona zmieniać.
System animacji właściwości umożliwia określenie tych cech animacji:
- Czas trwania: możesz określić czas trwania animacji. Domyślna długość to 300 ms.
- Interpolacja czasu: możesz określić, jak wartości właściwości są obliczane jako funkcja bieżącego czasu trwania animacji.
- Liczba powtórzeń i zachowanie: możesz określić, czy animacja ma być powtarzana po zakończeniu i ile razy ma być powtarzana. Możesz też określić, czy animacja ma być odtwarzana od końca. Ustawienie „reverse” powoduje odtwarzanie animacji do przodu, a następnie do tyłu, aż do osiągnięcia liczby powtórzeń.
- Zestawy animatora: możesz grupować animacje w zestawy logiczne, które są odtwarzane razem, sekwencyjnie lub po określonych opóźnieniach.
- Opóźnienie odświeżania klatek: możesz określić, jak często mają być odświeżane klatki animacji. Domyślnie odświeżanie jest ustawione na 10 ms, ale szybkość, z jaką aplikacja może odświeżać klatki, zależy ostatecznie od ogólnego obciążenia systemu i szybkości, z jaką system może obsługiwać bazowy licznik czasu.
Jak działa animacja właściwości
Najpierw wyjaśnimy, jak działa animacja, na prostym przykładzie. Rysunek 1 przedstawia hipotetyczny obiekt animowany za pomocą właściwości x, która reprezentuje jego położenie poziome na ekranie. Czas trwania animacji jest ustawiony na 40 ms, a odległość do pokonania wynosi 40 pikseli. Co 10 ms, czyli domyślna częstotliwość odświeżania klatek, obiekt przesuwa się w poziomie o 10 pikseli. Po 40 ms animacja zatrzymuje się, a obiekt znajduje się w położeniu poziomym 40. To przykład animacji z interpolacją liniową, co oznacza, że obiekt porusza się ze stałą szybkością.
Rysunek 1. Przykład animacji liniowej
Możesz też określić, że animacje mają mieć interpolację nieliniową. Ilustracja 2 przedstawia hipotetyczny obiekt, który przyspiesza na początku animacji i zwalnia na jej końcu. Obiekt nadal przesuwa się o 40 pikseli w 40 ms, ale nieliniowo. Na początku animacja przyspiesza do połowy, a potem zwalnia od połowy do końca. Jak widać na rysunku 2, odległość pokonana na początku i na końcu animacji jest mniejsza niż w środku.
Rysunek 2. Przykład animacji nielinearnej
Przyjrzyjmy się szczegółowo, jak ważne komponenty systemu animacji właściwości obliczają animacje takie jak te przedstawione powyżej. Ilustracja 3 pokazuje, jak działają ze sobą główne klasy.
Rysunek 3. Jak obliczane są animacje
Obiekt ValueAnimator śledzi czas trwania animacji, np. jak długo trwa animacja i jaka jest bieżąca wartość animowanej właściwości.
Element ValueAnimator zawiera element TimeInterpolator, który definiuje interpolację animacji, oraz element TypeEvaluator, który określa sposób obliczania wartości animowanej właściwości. Na przykład na ilustracji 2 użyty identyfikator TimeInterpolator to AccelerateDecelerateInterpolator, a identyfikator TypeEvaluator to IntEvaluator.
Aby rozpocząć animację, utwórz ValueAnimator i podaj wartości początkową i końcową właściwości, którą chcesz animować, oraz czas trwania animacji. Gdy zadzwonisz pod numer start(), rozpocznie się animacja. Podczas całej animacji ValueAnimator oblicza ułamek czasu
od 0 do 1 na podstawie czasu trwania animacji i czasu, który upłynął. Ułamek czasu, który upłynął, to procent czasu, który upłynął od rozpoczęcia animacji. 0 oznacza 0%, a 1 – 100%. Na przykład na rysunku 1 ułamek czasu, który upłynął w momencie t = 10 ms, wynosi 0,25, ponieważ łączny czas trwania to t = 40 ms.
Gdy funkcja ValueAnimator obliczy ułamek czasu, który upłynął, wywołuje aktualnie ustawioną funkcję TimeInterpolator, aby obliczyć interpolowany ułamek. Interpolowany ułamek mapuje ułamek czasu, który upłynął, na nowy ułamek uwzględniający ustawioną interpolację czasu. Na przykład na rysunku 2, ponieważ animacja powoli przyspiesza, interpolowany ułamek, czyli około 0, 15, jest mniejszy niż ułamek czasu, czyli 0, 25, w momencie t = 10 ms. Na rysunku 1 interpolowany ułamek jest zawsze taki sam jak ułamek czasu.
Po obliczeniu interpolowanej wartości ułamkowej funkcja ValueAnimator wywołuje odpowiednią funkcję TypeEvaluator, aby obliczyć wartość animowanej właściwości na podstawie interpolowanej wartości ułamkowej, wartości początkowej i wartości końcowej animacji. Na przykład na rysunku 2 interpolowany ułamek wynosił 0, 15 przy t = 10 ms, więc wartość właściwości w tym czasie wynosiła 0, 15 × (40 – 0), czyli 6.
Różnice między animacją właściwości a animacją widoku
System animacji widoku umożliwia animowanie tylko obiektów View, więc jeśli chcesz animować obiekty inne niż View, musisz zaimplementować własny kod. System animacji widoku jest też ograniczony tym, że udostępnia do animacji tylko kilka aspektów obiektu View, takich jak skalowanie i obracanie widoku, ale nie kolor tła.View
Kolejną wadą systemu animacji widoku jest to, że modyfikował on tylko miejsce rysowania widoku, a nie sam widok. Jeśli na przykład animujesz przycisk, aby przesuwał się po ekranie, przycisk jest rysowany prawidłowo, ale rzeczywiste miejsce, w którym można go kliknąć, nie zmienia się, więc musisz zaimplementować własną logikę, aby to obsłużyć.
Dzięki systemowi animacji właściwości te ograniczenia są całkowicie usuwane i możesz animować dowolną właściwość dowolnego obiektu (widoków i innych elementów) oraz sam obiekt jest faktycznie modyfikowany. System animacji właściwości jest też bardziej niezawodny w sposobie przeprowadzania animacji. Ogólnie rzecz biorąc, przypisujesz animatory do właściwości, które chcesz animować, takich jak kolor, pozycja lub rozmiar, i możesz określać aspekty animacji, takie jak interpolacja i synchronizacja wielu animatorów.
System animacji widoku wymaga jednak mniej czasu na konfigurację i mniej kodu do napisania. Jeśli animacja widoku spełnia wszystkie Twoje potrzeby lub jeśli istniejący kod działa już tak, jak chcesz, nie musisz używać systemu animacji właściwości. W różnych sytuacjach może też być przydatne używanie obu systemów animacji.
Omówienie interfejsu API
Większość interfejsów API systemu animacji właściwości znajdziesz w android.animation. System animacji widoku definiuje już wiele interpolatorów w android.view.animation, więc możesz ich używać również w systemie animacji właściwości. W tabelach poniżej opisano główne komponenty systemu animacji właściwości.
Klasa Animator zapewnia podstawową strukturę do tworzenia animacji. Zwykle nie używasz tej klasy bezpośrednio, ponieważ zapewnia ona tylko minimalną funkcjonalność, którą należy rozszerzyć, aby w pełni obsługiwać animowanie wartości. Te podklasy rozszerzają klasę Animator:
Tabela 1. Animatorzy
| Klasa | Opis |
|---|---|
ValueAnimator |
Główny mechanizm czasowy animacji właściwości, który oblicza też wartości animowanej właściwości. Zawiera wszystkie główne funkcjonalności, które obliczają wartości animacji, a także szczegóły dotyczące czasu trwania każdej animacji, informacje o tym, czy animacja się powtarza, detektory, które otrzymują zdarzenia aktualizacji, oraz możliwość ustawiania niestandardowych typów do oceny. Animowanie właściwości składa się z 2 części: obliczania animowanych wartości i ustawiania tych wartości w obiekcie i właściwości, które są animowane. ValueAnimator nie wykonuje drugiej części, więc musisz nasłuchiwać aktualizacji wartości obliczanych przez ValueAnimator i modyfikować obiekty, które chcesz animować, za pomocą własnej logiki. Więcej informacji znajdziesz w sekcji Animowanie za pomocą klasy ValueAnimator. |
ObjectAnimator |
Podklasa klasy ValueAnimator, która umożliwia ustawienie obiektu docelowego i właściwości obiektu do animacji. Ta klasa aktualizuje właściwość odpowiednio, gdy oblicza nową wartość animacji. ObjectAnimator jest najczęściej używaną funkcją, ponieważ znacznie ułatwia animowanie wartości w obiektach docelowych. Czasami jednak warto użyć bezpośrednio ValueAnimator, ponieważ ObjectAnimator ma kilka dodatkowych ograniczeń, np. wymaga, aby w obiekcie docelowym były obecne określone metody dostępu. |
AnimatorSet |
Umożliwia grupowanie animacji, aby były one uruchamiane względem siebie. Animacje możesz ustawić tak, aby były odtwarzane razem, kolejno lub po określonym opóźnieniu. Więcej informacji znajdziesz w sekcji Tworzenie choreografii wielu animacji za pomocą zestawów animatorów. |
Obiekty Evaluator informują system animacji właściwości, jak obliczać wartości danej właściwości. Korzystają one z danych o czasie dostarczanych przez klasę Animator, wartości początkowej i końcowej animacji oraz obliczają na podstawie tych danych animowane wartości właściwości. System animacji właściwości udostępnia te funkcje oceniające:
Tabela 2. Osoby oceniające
| Klasa/interfejs | Opis |
|---|---|
IntEvaluator |
Domyślny moduł obliczający wartości właściwości int. |
FloatEvaluator |
Domyślny moduł obliczający wartości właściwości float. |
ArgbEvaluator |
Domyślny moduł obliczający wartości właściwości koloru, które są reprezentowane jako wartości szesnastkowe. |
TypeEvaluator |
Interfejs, który umożliwia utworzenie własnego oceniającego. Jeśli animujesz właściwość obiektu, która nie jest 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 koloru, jeśli chcesz przetwarzać te typy inaczej niż domyślnie.
Więcej informacji o pisaniu niestandardowego narzędzia do oceny znajdziesz w sekcji Korzystanie z narzędzia TypeEvaluator. |
Interpolator czasu określa, jak konkretne wartości w animacji są obliczane jako funkcja czasu. Możesz na przykład określić, że animacje mają przebiegać liniowo w całym czasie trwania animacji, co oznacza, że animacja będzie się poruszać równomiernie przez cały czas. Możesz też określić, że animacje mają korzystać z nieliniowego czasu, np. przyspieszając na początku i zwalniając na końcu animacji. W tabeli 3 opisujemy interpolatory zawarte w android.view.animation. Jeśli żaden z podanych interpolatorów nie odpowiada Twoim potrzebom, zaimplementuj interfejs TimeInterpolator i utwórz własny. Więcej informacji o pisaniu niestandardowego interpolatora znajdziesz w artykule Korzystanie z interpolatorów.
Tabela 3. Interpolatory
| Klasa/interfejs | Opis |
|---|---|
AccelerateDecelerateInterpolator |
Interpolator, którego szybkość zmian jest powolna na początku i na końcu, ale przyspiesza w środku. |
AccelerateInterpolator |
Interpolator, którego szybkość zmian początkowo jest mała, a potem rośnie. |
AnticipateInterpolator |
Interpolator, którego zmiana zaczyna się od cofania, a potem gwałtownie przyspiesza. |
AnticipateOvershootInterpolator |
Interpolator, którego zmiana zaczyna się od tyłu, gwałtownie przesuwa się do przodu i przekracza wartość docelową, a następnie wraca do wartości końcowej. |
BounceInterpolator |
Interpolator, którego zmiana odbija się na końcu. |
CycleInterpolator |
Interpolator, którego animacja powtarza się przez określoną liczbę cykli. |
DecelerateInterpolator |
Interpolator, którego szybkość zmian początkowo jest duża, a potem maleje. |
LinearInterpolator |
Interpolator, którego szybkość zmiany jest stała. |
OvershootInterpolator |
Interpolator, którego zmiana gwałtownie rośnie i przekracza ostatnią wartość, a następnie wraca. |
TimeInterpolator |
Interfejs, który umożliwia implementację własnego interpolatora. |
Animowanie za pomocą klasy ValueAnimator
Klasa ValueAnimator umożliwia animowanie wartości określonego typu przez czas trwania animacji poprzez podanie zestawu wartości int, float lub kolorów, które mają być animowane. Obiekt ValueAnimator uzyskujesz, wywołując jedną z jego metod fabrycznych: ofInt(), ofFloat() lub ofObject(). 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 rozpoczyna obliczanie wartości animacji w zakresie od 0 do 100 przez 1000 ms, gdy uruchamiana jest 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 między startPropertyValue a endPropertyValue, korzystając z logiki dostarczonej przez MyTypeEvaluator przez 1000 ms, gdy uruchamia się metoda start().
Możesz użyć wartości animacji, dodając 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 słuchaczach znajdziesz w sekcji Słuchacze animacji.
Animowanie za pomocą klasy ObjectAnimator
ObjectAnimator jest podklasą klasy ValueAnimator (omówionej w poprzedniej sekcji) i łączy silnik czasowy oraz obliczanie wartości klasy ValueAnimator z możliwością animowania nazwanej właściwości obiektu docelowego. Dzięki temu animowanie dowolnego obiektu jest znacznie łatwiejsze, ponieważ nie musisz już implementować ValueAnimator.AnimatorUpdateListener, ponieważ animowana właściwość jest aktualizowana automatycznie.
Utworzenie instancji ObjectAnimator jest podobne do utworzenia instancji ValueAnimator, ale musisz też określić obiekt i nazwę właściwości tego obiektu (jako ciąg znaków) wraz z wartościami, między którymi ma się odbywać animacja:
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 aktualizacji ObjectAnimator były prawidłowe, musisz wykonać te czynności:
- Właściwość obiektu, którą animujesz, musi mieć funkcję ustawiającą (w formacie camel case) w postaci
set<PropertyName>(). PonieważObjectAnimatorautomatycznie aktualizuje właściwość podczas animacji, musi mieć do niej dostęp za pomocą tej metody ustawiającej. Jeśli na przykład nazwa właściwości tofoo, musisz mieć metodęsetFoo(). Jeśli ta metoda ustawiająca nie istnieje, masz 3 możliwości:- Jeśli masz do tego prawo, dodaj do klasy metodę ustawiającą.
- Użyj klasy opakowującej, którą możesz zmieniać, i spraw, aby ta klasa otrzymywała wartość za pomocą prawidłowej metody ustawiającej i przekazywała ją do oryginalnego obiektu.
- Użyj w zamian zasady
ValueAnimator.
- Jeśli w jednej z metod fabrycznych
ObjectAnimatorpodasz tylko jedną wartość parametruvalues..., zostanie ona uznana za wartość końcową animacji. Dlatego właściwość obiektu, którą animujesz, musi mieć funkcję pobierającą, która służy do uzyskiwania wartości początkowej animacji. Funkcja pobierająca musi mieć postaćget<PropertyName>(). Jeśli na przykład nazwa właściwości tofoo, musisz mieć metodęgetFoo(). - Metody pobierania (jeśli są potrzebne) i ustawiania właściwości, którą animujesz, muszą działać na tym samym typie co wartości początkowe i końcowe, które określasz w
ObjectAnimator. Jeśli na przykład tworzysz tenObjectAnimator, musisz miećtargetObject.setPropName(float)itargetObject.getPropName():ObjectAnimator.ofFloat(targetObject, "propName", 1f)
- W zależności od tego, jaką właściwość lub obiekt animujesz, może być konieczne wywołanie metody
invalidate()w obiekcie View, aby wymusić ponowne narysowanie ekranu z zaktualizowanymi wartościami animacji. Możesz to zrobić w funkcji zwrotnejonAnimationUpdate(). Na przykład animowanie właściwości koloru obiektu Drawable powoduje aktualizacje ekranu tylko wtedy, gdy obiekt jest ponownie rysowany. Wszystkie metody ustawiające właściwości w klasie View, takie jaksetAlpha()isetTranslationX(), prawidłowo unieważniają widok, więc nie musisz go unieważniać, gdy wywołujesz te metody z nowymi wartościami. Więcej informacji o słuchaczach znajdziesz w sekcji Słuchacze animacji.
Choreografia wielu animacji za pomocą AnimatorSet
W wielu przypadkach chcesz odtworzyć animację, która zależy od tego, kiedy rozpocznie się lub zakończy inna animacja. System Android umożliwia łączenie animacji w AnimatorSet, dzięki czemu możesz określić, czy animacje mają się rozpoczynać jednocześnie, sekwencyjnie czy po określonym opóźnieniu. Możesz też zagnieżdżać AnimatorSet obiekty w sobie.
Poniższy fragment kodu odtwarza obiekty Animator w ten sposób:
- Odtwarza
bounceAnim. - Odtwarza jednocześnie
squashAnim1,squashAnim2,stretchAnim1istretchAnim2. - 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();
Słuchacze animacji
Podczas animacji możesz nasłuchiwać ważnych zdarzeń za pomocą opisanych poniżej detektorów.
Animator.AnimatorListeneronAnimationStart()– wywoływana, gdy animacja się rozpoczyna.onAnimationEnd()- Wywoływana po zakończeniu animacji.onAnimationRepeat()– wywoływana, gdy animacja się powtarza.onAnimationCancel()– wywoływana, gdy animacja zostanie anulowana. Anulowana animacja również wywołujeonAnimationEnd(), niezależnie od sposobu jej zakończenia.
ValueAnimator.AnimatorUpdateListener-
onAnimationUpdate()– wywoływana w każdej klatce animacji. Nasłuchuj tego zdarzenia, aby używać obliczonych wartości generowanych przezValueAnimatorpodczas animacji. Aby użyć wartości, wyślij zapytanie do obiektuValueAnimatorprzekazanego do zdarzenia, aby uzyskać bieżącą wartość animowaną za pomocą metodygetAnimatedValue(). Wdrożenie tego odbiornika jest wymagane, jeśli używaszValueAnimator.W zależności od tego, jaką usługę lub obiekt animujesz, może być konieczne wywołanie funkcji
invalidate()w obiekcie View, aby wymusić ponowne narysowanie tego obszaru ekranu z nowymi animowanymi wartościami. Na przykład animowanie właściwości koloru obiektu Drawable powoduje aktualizacje ekranu tylko wtedy, gdy obiekt jest ponownie rysowany. Wszystkie metody ustawiające właściwości w klasie View, takie jaksetAlpha()isetTranslationX(), prawidłowo unieważniają widok, więc nie musisz go unieważniać, gdy wywołujesz te metody z nowymi wartościami.
-
Możesz rozszerzyć klasę AnimatorListenerAdapter zamiast implementować interfejs Animator.AnimatorListener, jeśli nie chcesz implementować wszystkich metod interfejsu Animator.AnimatorListener. Klasa AnimatorListenerAdapter zawiera 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()); }
Animowanie zmian układu w obiektach ViewGroup
System animacji właściwości umożliwia animowanie zmian w obiektach ViewGroup, a także łatwe animowanie samych obiektów View.
Zmiany układu w obiekcie ViewGroup możesz animować za pomocą klasy
LayoutTransition. Widoki w grupie widoków mogą przechodzić animację pojawiania się i znikania, gdy dodajesz je do grupy widoków lub usuwasz z niej, albo gdy wywołujesz metodę setVisibility() widoku z wartościami VISIBLE, INVISIBLE lub GONE. Pozostałe widoki w grupie widoków mogą też animować się do nowych pozycji po dodaniu lub usunięciu widoków. W obiekcie LayoutTransition możesz zdefiniować te animacje, wywołując funkcję setAnimator() i przekazując obiekt Animator z jedną z tych stałych LayoutTransition:
APPEARING– flaga wskazująca animację, która jest uruchamiana w przypadku elementów pojawiających się w kontenerze.CHANGE_APPEARING– flaga wskazująca animację, która jest uruchamiana w przypadku elementów zmieniających się z powodu pojawienia się w kontenerze nowego elementu.DISAPPEARING– flaga wskazująca animację, która jest uruchamiana w przypadku elementów znikających z kontenera.CHANGE_DISAPPEARING– flaga wskazująca animację, która jest uruchamiana w przypadku elementów zmieniających się z powodu zniknięcia elementu z kontenera.
Możesz zdefiniować własne animacje niestandardowe dla tych 4 rodzajów zdarzeń, aby dostosować wygląd przejść układu lub po prostu nakazać systemowi animacji używanie animacji domyślnych.
Aby ustawić atrybut android:animateLayoutchanges na true w przypadku elementu ViewGroup:
<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ść „true” powoduje automatyczne animowanie obiektów View dodawanych do obiektu ViewGroup lub z niego usuwanych, a także pozostałych obiektów View w obiekcie ViewGroup.
Animowanie zmian stanu widoku za pomocą klasy StateListAnimator
Klasa StateListAnimator umożliwia definiowanie animatorów, którzy są uruchamiani, gdy zmienia się stan widoku. Ten obiekt działa jako otoczka dla obiektu Animator, wywołując tę animację za każdym razem, gdy zmieni się określony stan widoku (np. „naciśnięty” lub „skupiony”).
Element StateListAnimator można zdefiniować w zasobie XML z elementem głównym <selector> i elementami podrzędnymi <item>, z których każdy 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 zmienia skalę x i y widoku po jego naciśnięciu:
res/xml/animate_scale.xml
<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <!-- the pressed state; increase x and y size to 150% --> <item android:state_pressed="true"> <set> <objectAnimator android:propertyName="scaleX" android:duration="@android:integer/config_shortAnimTime" android:valueTo="1.5" android:valueType="floatType"/> <objectAnimator android:propertyName="scaleY" android:duration="@android:integer/config_shortAnimTime" android:valueTo="1.5" android:valueType="floatType"/> </set> </item> <!-- the default, non-pressed state; set x and y size to 100% --> <item android:state_pressed="false"> <set> <objectAnimator android:propertyName="scaleX" android:duration="@android:integer/config_shortAnimTime" android:valueTo="1" android:valueType="floatType"/> <objectAnimator android:propertyName="scaleY" android:duration="@android:integer/config_shortAnimTime" android:valueTo="1" android:valueType="floatType"/> </set> </item> </selector>
Aby dołączyć animator listy stanów do widoku, dodaj atrybut
android:stateListAnimator w ten sposób:
<Button android:stateListAnimator="@xml/animate_scale" ... />
Teraz animacje zdefiniowane w animate_scale.xml są używane, gdy zmienia się stan tego przycisku.
Możesz też przypisać animator listy stanów do widoku w kodzie, używając metody
AnimatorInflater.loadStateListAnimator(), i przypisać animatora do widoku za pomocą metody View.setStateListAnimator().
Zamiast animować właściwości widoku, możesz odtwarzać animację rysowalną między zmianami stanu za pomocą AnimatedStateListDrawable.
Niektóre widżety systemowe w Androidzie 5.0 domyślnie korzystają z 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żywanie klasy TypeEvaluator
Jeśli chcesz animować typ, który jest nieznany systemowi Android, możesz utworzyć własny moduł obliczający, implementując interfejs TypeEvaluator. Typy znane systemowi Android to int, float lub kolor, które są obsługiwane przez moduły oceniające typu IntEvaluator, FloatEvaluator i ArgbEvaluator.
W interfejsie TypeEvaluator jest tylko jedna metoda implementacji – metoda evaluate(). Dzięki temu animator, którego używasz, może zwrócić odpowiednią wartość animowanej właściwości 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 funkcja ValueAnimator (lub ObjectAnimator) jest uruchamiana, oblicza bieżący ułamek czasu trwania animacji (wartość z przedziału od 0 do 1), a następnie oblicza interpolowaną wersję tej wartości w zależności od użytego interpolatora. Interpolowany ułamek to wartość, którą TypeEvaluator otrzymuje za pomocą parametru fraction, więc podczas obliczania wartości animowanych nie musisz uwzględniać interpolatora.
Używanie interpolatorów
Interpolator określa, jak konkretne wartości w animacji są obliczane jako funkcja czasu. Możesz na przykład określić, że animacje mają przebiegać liniowo w całej animacji, co oznacza, że animacja będzie się poruszać równomiernie przez cały czas. Możesz też określić, że animacje mają wykorzystywać czas nieliniowy, np. przyspieszenie lub zwalnianie na początku lub na końcu animacji.
Interpolatory w systemie animacji otrzymują od Animators ułamek reprezentujący czas, który upłynął od rozpoczęcia animacji. Interpolatory modyfikują ten ułamek, aby odpowiadał typowi animacji, którą mają zapewnić. System Android udostępnia zestaw typowych interpolatorów w klasie android.view.animation package. Jeśli żaden z nich nie odpowiada Twoim potrzebom, możesz zaimplementować interfejs TimeInterpolator i utworzyć własny.
Poniżej porównujemy sposób obliczania ułamków interpolowanych przez domyślny interpolator AccelerateDecelerateInterpolator i interpolator LinearInterpolator.
Wartość LinearInterpolator nie ma wpływu na upłyniętą część. AccelerateDecelerateInterpolator przyspiesza w animacji i zwalnia podczas wychodzenia z niej. Logikę tych interpolatorów definiują te metody:
AccelerateDecelerateInterpolator
Kotlin
override fun getInterpolation(input: Float): Float = (Math.cos((input + 1) * Math.PI) / 2.0f).toFloat() + 0.5f
Java
@Override public float getInterpolation(float input) { return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f; }
LinearInterpolator
Kotlin
override fun getInterpolation(input: Float): Float = input
Java
@Override public float getInterpolation(float input) { return input; }
W tabeli poniżej znajdziesz przybliżone wartości obliczane przez te interpolatory w przypadku animacji trwającej 1000 ms:
| ms elapsed | Ułamek czasu, który upłynął/Ułamek interpolowany (liniowy) | Interpolowany ułamek (przyspieszenie/zwalnianie) |
|---|---|---|
| 0 | 0 | 0 |
| 200 | 0,2 | .1 |
| 400 | .4 | 0,345 |
| 600 | .6 | .654 |
| 800 | .8 | .9 |
| 1000 | 1 | 1 |
Jak widać w tabeli, LinearInterpolator zmienia wartości z tą samą prędkością – o 0,2 co 200 ms. W zakresie od 200 ms do 600 ms funkcja AccelerateDecelerateInterpolator zmienia wartości szybciej niż LinearInterpolator, a w zakresie od 600 ms do 1000 ms – wolniej.
Określanie klatek kluczowych
Obiekt Keyframe składa się z pary czas/wartość, która umożliwia zdefiniowanie określonego stanu w określonym czasie animacji. Każda klatka kluczowa może też mieć własny interpolator, który kontroluje zachowanie animacji w przedziale czasu między poprzednią klatką kluczową a bieżącą.
Aby utworzyć instancję obiektu Keyframe, musisz użyć jednej z metod fabrycznych: ofInt(), ofFloat() lub ofObject(), aby uzyskać odpowiedni typ Keyframe. Następnie wywołujesz metodę fabryczną ofKeyframe(), aby uzyskać obiekt PropertyValuesHolder. Gdy masz już obiekt, możesz uzyskać animatora, przekazując PropertyValuesHolder obiekt i obiekt do animacji. Poniższy 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);
Animowanie widoków
System animacji właściwości umożliwia usprawnione animowanie obiektów View i ma kilka zalet w porównaniu z systemem animacji widoku. System animacji widoku przekształcał obiekty View, zmieniając sposób ich rysowania. Odbywało się to w kontenerze każdego widoku, ponieważ sam widok nie miał właściwości, którymi można było manipulować. Spowodowało to animację widoku, ale nie zmieniło samego obiektu widoku. Powodowało to takie zachowania jak pozostawanie obiektu w pierwotnej lokalizacji, mimo że był on rysowany w innym miejscu na ekranie. W Androidzie 3.0 dodano nowe właściwości oraz odpowiednie metody pobierające i ustawiające, aby wyeliminować tę wadę.
System animacji właściwości może animować obiekty View na ekranie, zmieniając rzeczywiste właściwości w obiektach View. Dodatkowo widoki automatycznie wywołują metodę invalidate()
w celu odświeżenia ekranu za każdym razem, gdy zmienią się ich właściwości. Nowe właściwości w klasie View, które ułatwiają animacje właściwości:
translationXitranslationY: te właściwości określają położenie widoku jako różnicę między jego współrzędnymi lewą i górną, które są ustawiane przez kontener układu.rotation,rotationXirotationY: te właściwości kontrolują obrót w 2D (właściwośćrotation) i 3D wokół punktu obrotu.scaleXiscaleY: te właściwości kontrolują skalowanie 2D widoku wokół punktu obrotu.pivotXipivotY: te właściwości określają położenie punktu osi obrotu, wokół którego zachodzą przekształcenia rotacji i skalowania. Domyślnie punkt obrotu znajduje się w środku obiektu.xiy: są to proste właściwości narzędziowe opisujące ostateczne położenie widoku w kontenerze jako sumę wartości po lewej i u góry oraz wartości translationX i translationY.alpha: reprezentuje przezroczystość alfa w obiekcie View. Domyślnie ta wartość wynosi 1 (nieprzezroczysty), a wartość 0 oznacza pełną przezroczystość (niewidoczny).
Aby animować właściwość obiektu View, np. kolor lub wartość rotacji, wystarczy utworzyć animatora właściwości i określić właściwość obiektu View, którą chcesz animować. 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 dotyczących animacji za pomocą klas ValueAnimator i ObjectAnimator.
Animowanie za pomocą ViewPropertyAnimator
ViewPropertyAnimator to prosty sposób na animowanie kilku właściwości elementu View równolegle przy użyciu jednego obiektu Animator. Działa podobnie jak ObjectAnimator, ponieważ modyfikuje rzeczywiste wartości właściwości widoku, ale jest bardziej wydajny podczas animowania wielu właściwości jednocześnie. Dodatkowo kod używający ViewPropertyAnimator jest znacznie bardziej zwięzły i czytelny. Poniższe fragmenty kodu pokazują różnice w używaniu wielu obiektów ObjectAnimator, pojedynczego obiektu ObjectAnimator i obiektu ViewPropertyAnimator podczas jednoczesnego 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();
Jeden obiekt 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 o ViewPropertyAnimator znajdziesz w odpowiednim poście na blogu dla deweloperów aplikacji na Androida.
Deklarowanie animacji w XML
System animacji właściwości umożliwia deklarowanie animacji właściwości za pomocą kodu XML zamiast programowo. Definiując animacje w XML, możesz łatwo ponownie wykorzystywać je w wielu działaniach i łatwiej edytować sekwencję animacji.
Aby odróżnić pliki animacji, które korzystają z nowych interfejsów API animacji właściwości, od tych, które korzystają ze starszej struktury animacji widoku, od Androida 3.1 pliki XML animacji właściwości należy zapisywać w katalogu res/animator/.
Te 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żyć w deklaracji XML, znajdziesz w sekcji Zasoby animacji. W poniższym przykładzie odtwarzane są sekwencyjnie 2 zestawy animacji obiektów. Pierwszy zagnieżdżony zestaw odtwarza 2 animacje obiektów jednocześnie:
<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 rozwinąć zasoby XML w kodzie do obiektu AnimatorSet, a następnie ustawić obiekty docelowe dla wszystkich animacji przed rozpoczęciem zestawu animacji. Wywołanie funkcji setTarget() ustawia jeden obiekt docelowy dla wszystkich elementów podrzędnych elementu 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();
Możesz też zadeklarować ValueAnimator w XML, jak pokazano 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 rozwinąć obiekt, dodać AnimatorUpdateListener, pobrać zaktualizowaną wartość animacji i użyć jej we właściwości jednego z widoków, jak pokazano 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 do definiowania animacji właściwości znajdziesz w artykule Zasoby animacji .
Potencjalny wpływ na wydajność interfejsu
Animatory, które aktualizują interfejs, powodują dodatkową pracę związaną z renderowaniem w każdej klatce, w której działa animacja. Dlatego używanie animacji wymagających dużych zasobów może negatywnie wpływać na wydajność aplikacji.
Praca wymagana do animowania interfejsu jest dodawana do etapu animacji potoku renderowania. Aby sprawdzić, czy animacje wpływają na wydajność aplikacji, włącz Profil renderowania GPU i monitoruj etap animacji. Więcej informacji znajdziesz w przewodniku po profilu renderowania GPU.