O sistema de animação de propriedades é um framework robusto que permite animar quase tudo. Você pode definir uma animação para mudar qualquer propriedade de objeto ao longo do tempo, independentemente de ela ser desenhada na tela ou não. Uma animação de propriedade muda o valor de uma propriedade (campo em um objeto) durante um período especificado. Para fazer a animação, especifique a propriedade do objeto que você quer animar, como a posição de um objeto na tela, por quanto tempo você quer animar e quais valores serão animados.
O sistema de animação de propriedades permite definir as seguintes características de uma animação:
- Duração: é possível especificar a duração de uma animação. O padrão é 300 ms.
- Interpolação de tempo: você pode especificar como os valores da propriedade são calculados como uma função do tempo decorrido atual da animação.
- Contagem e comportamento repetidos: você pode especificar se uma animação deve ou não ser repetida ao chegar ao final de uma duração e quantas vezes ela será repetida. Você também pode especificar se quer que a animação seja reproduzida no sentido inverso. Reverter a animação para frente e para trás repetidas vezes, até que o número de repetições seja alcançado.
- Conjuntos de animadores: é possível agrupar animações em conjuntos lógicos que são reproduzidos juntos, em sequência ou após atrasos especificados.
- Atraso da atualização do frame: é possível especificar com que frequência os frames da animação serão atualizados. O padrão é atualizar a cada 10 ms, mas a velocidade com que o aplicativo pode atualizar frames depende, em última instância, da ocupação geral do sistema e da rapidez em que ele pode atender ao timer subjacente.
Para ver um exemplo completo de animação de propriedades, consulte a classe ChangeColor
no exemplo CustomTransition (em inglês) no GitHub.
Como a animação de propriedade funciona
Primeiro, vamos ver como uma animação funciona com um exemplo simples. A Figura 1 mostra um
objeto hipotético animado com a propriedade x
, que representa o
local horizontal dele em uma tela. A duração da animação é definida como 40 ms, e a distância a ser percorrida é de 40 pixels. A cada 10 ms, que é a taxa de atualização de frame padrão, o objeto se move horizontalmente em 10 pixels. Ao final de 40 ms, a animação é interrompida, e o objeto termina na posição horizontal 40. Esse é um exemplo de animação com interpolação linear, o que significa que o objeto se move a uma velocidade constante.
![](https://developer.android.com/static/images/animation/animation-linear.png?authuser=6&hl=pt)
Figura 1. Exemplo de uma animação linear.
Também é possível especificar que animações tenham uma interpolação não linear. A Figura 2 ilustra um objeto hipotético que acelera no início da animação e desacelera no final dela. O objeto ainda percorre 40 pixels em 40 ms, mas não de forma linear. No início, essa animação acelera até a metade e desacelera da metade até o final da animação. Como mostrado na Figura 2, a distância percorrida no início e no fim da animação é menor que no meio.
![](https://developer.android.com/static/images/animation/animation-nonlinear.png?authuser=6&hl=pt)
Figura 2. Exemplo de uma animação não linear.
Vamos analisar em detalhes como os componentes importantes do sistema de animação de propriedades calculariam animações como as ilustradas acima. A Figura 3 mostra como as principais classes funcionam umas com as outras.
![](https://developer.android.com/static/images/animation/valueanimator.png?authuser=6&hl=pt)
Figura 3. Como as animações são calculadas.
O objeto ValueAnimator
monitora o tempo da animação, por exemplo, por quanto tempo a animação está em execução e o valor atual da propriedade que ela está animando.
O ValueAnimator
encapsula um TimeInterpolator
, que define a interpolação de animação, e um TypeEvaluator
, que define como calcular valores para a propriedade que está sendo animada. Por exemplo, na Figura 2, o TimeInterpolator
usado seria
AccelerateDecelerateInterpolator
e o TypeEvaluator
seria IntEvaluator
.
Para iniciar uma animação, crie um ValueAnimator
e atribua a ele os valores inicial e final da propriedade que você quer animar, além da duração da animação. Quando você chama start()
, a animação começa. Durante toda a animação, o ValueAnimator
calcula uma fração decorrida entre 0 e 1, com base na duração da animação e no tempo decorrido. A fração decorrida representa a porcentagem de tempo decorrido até a conclusão da animação, em que 0 significa 0% e 1 significa 100%. Por exemplo, na Figura 1, a fração decorrida em t = 10 ms seria 0,25 porque a duração total é t = 40 ms.
Quando o ValueAnimator
termina o cálculo de uma fração decorrida, ele chama o TimeInterpolator
definido no momento para calcular uma fração interpolada. Uma fração interpolada mapeia a fração decorrida para uma nova fração que leva em consideração a interpolação de tempo definida. Por exemplo, na Figura 2, como a animação acelera lentamente, a fração interpolada, cerca de 0,15, é menor que a fração decorrida, 0,25, a t = 10 ms. Na Figura 1, a fração interpolada é sempre igual à fração decorrida.
Quando a fração interpolada é calculada, ValueAnimator
chama o TypeEvaluator
apropriado para calcular o valor da propriedade que você está animando, com base na fração interpolada, no valor inicial e no valor final da animação. Por exemplo, na Figura 2, a fração interpolada era 0,15 em t = 10 ms, então o valor da propriedade naquele momento seria 0,15 × (40 - 0) ou 6.
Diferenças entre a animação de propriedade e a animação de visualização
O sistema de animação de visualizações oferece o recurso de animar apenas objetos View
. Portanto, se você quiser animar objetos não View
, será necessário implementar seu próprio código. O sistema de animação de visualizações também é limitado pelo fato de que só expõe alguns aspectos de um objeto View
para animar, como o dimensionamento e a rotação de uma visualização, mas não a cor do plano de fundo, por exemplo.
Outra desvantagem do sistema de animação de visualizações é que ele só modifica onde a visualização foi desenhada, e não a visualização em si. Por exemplo, se você animou um botão para se mover pela tela, ele é desenhado corretamente, mas o local real em que você pode clicar no botão não muda. Portanto, é necessário implementar sua própria lógica para lidar com isso.
Com o sistema de animação de propriedades, essas restrições são completamente removidas e você pode animar qualquer propriedade de qualquer objeto (visualizações e não visualizações), e o próprio objeto é realmente modificado. O sistema de animação de propriedades também é mais robusto na forma como realiza animações. Em um nível alto, você atribui animadores às propriedades que quer animar, como cor, posição ou tamanho, e pode definir aspectos da animação, como interpolação e sincronização de vários animadores.
No entanto, o sistema de animação de visualizações leva menos tempo para ser configurado e requer menos código. Se a animação de visualizações realizar tudo o que você precisa fazer ou se o código existente já funcionar da maneira desejada, não será necessário usar o sistema de animação de propriedades. Também pode fazer sentido usar os dois sistemas de animação para diferentes situações, caso ocorra o caso de uso.
Visão geral da API
Veja a maioria das APIs do sistema de animação de propriedades em android.animation
. Como o sistema de animação de visualizações já define muitos interpoladores em android.view.animation
, você também pode usar esses interpoladores no sistema de animação de propriedades. As tabelas a seguir descrevem os principais componentes do sistema de animação de propriedades.
A classe Animator
fornece a estrutura básica para criar animações. Normalmente, essa classe não é usada diretamente, porque ela oferece apenas funcionalidades mínimas que precisam ser estendidas para oferecer suporte total à animação de valores. As subclasses
abaixo estendem Animator
:
Tabela 1. Animadores.
Classe. | Descrição |
---|---|
ValueAnimator |
Principal mecanismo de tempo para a animação de propriedades, que também calcula os valores da propriedade a ser animada. Ele tem todas as funcionalidades principais que calculam valores de animação e contém os detalhes de tempo de cada animação, informações sobre a repetição de uma animação, listeners que recebem eventos de atualização e a capacidade de definir tipos personalizados para avaliar. A animação de propriedades consiste em duas partes: calcular os valores animados e definir esses valores no objeto e na propriedade que está sendo animada. O ValueAnimator não executa a segunda parte. Portanto, é necessário detectar atualizações nos valores calculados por ValueAnimator e modificar os objetos que você quer animar com sua lógica. Consulte a seção sobre Animar com ValueAnimator para saber mais. |
ObjectAnimator |
Uma subclasse de ValueAnimator que permite definir um objeto de destino e uma propriedade de objeto que serão animados. Essa classe atualiza a propriedade adequadamente ao calcular um novo valor para a animação. Use ObjectAnimator na maioria das vezes, porque ele facilita muito o processo de animação de valores em objetos de destino. No entanto, às vezes você pode querer usar ValueAnimator diretamente porque ObjectAnimator tem mais algumas restrições, como exigir que métodos de acesso específicos estejam presentes no objeto de destino. |
AnimatorSet |
Fornece um mecanismo para agrupar animações para que sejam executadas uma em relação à outra. Você pode definir que as animações sejam reproduzidas juntas, em sequência ou após um atraso especificado. Consulte a seção sobre Coreografar várias animações com conjuntos de animadores para saber mais. |
Os avaliadores informam ao sistema de animação de propriedades como calcular valores para uma determinada
propriedade. Eles usam os dados de tempo fornecidos por uma classe Animator
e os valores inicial e final da animação para calcular os valores animados da propriedade com base nesses dados. O sistema de animação de propriedades disponibiliza os seguintes avaliadores:
Tabela 2. Avaliadores.
Classe/interface | Descrição |
---|---|
IntEvaluator |
Avaliador padrão para calcular valores para propriedades int . |
FloatEvaluator |
Avaliador padrão para calcular valores para propriedades float . |
ArgbEvaluator |
Avaliador padrão para calcular valores para propriedades de cor que são representadas como valores hexadecimais. |
TypeEvaluator |
Interface que permite criar o próprio avaliador. Se você estiver animando uma propriedade de objeto que não é um int , float ou cor, implemente a interface TypeEvaluator para especificar como calcular os valores animados da propriedade do objeto. Também será possível especificar um TypeEvaluator personalizado para int , float e valores de cor, se você quiser processar esses tipos de maneira diferente do comportamento padrão.
Consulte a seção Como usar um TypeEvaluator para mais informações sobre como criar um avaliador personalizado. |
Um interpolador de tempo define como valores específicos em uma animação são calculados como uma função do tempo. Por exemplo, é possível especificar que animações aconteçam linearmente em toda a animação, o que significa que ela se moverá uniformemente o tempo todo, ou especificar que animações usem tempo não linear, por exemplo, acelerando no início e desacelerando no final da animação. A Tabela 3 descreve os interpoladores contidos em android.view.animation
. Se nenhum dos interpoladores fornecidos atender às suas necessidades, implemente a interface TimeInterpolator
e crie seu próprio interpolador. Consulte Como usar interpoladores para mais informações sobre como escrever um interpolador personalizado.
Tabela 3. Interpoladores
Classe/interface | Descrição |
---|---|
AccelerateDecelerateInterpolator |
Interpolador cuja taxa de mudança começa e termina lentamente, mas acelera no meio. |
AccelerateInterpolator |
Interpolador cuja taxa de mudança começa lentamente e depois acelera. |
AnticipateInterpolator |
Interpolador cuja mudança começa na ordem inversa e depois avança rapidamente. |
AnticipateOvershootInterpolator |
Interpolador cuja mudança começa na ordem inversa, avança rapidamente, ultrapassa o valor de destino e, por fim, volta ao valor final. |
BounceInterpolator |
Interpolador cuja mudança oscila no final. |
CycleInterpolator |
Interpolador cuja animação se repete por um número especificado de ciclos. |
DecelerateInterpolator |
Interpolador cuja taxa de mudança começa rapidamente e depois desacelera. |
LinearInterpolator |
Interpolador cuja taxa de mudança é constante. |
OvershootInterpolator |
Interpolador cuja mudança avança rapidamente, ultrapassa o último valor e depois volta. |
TimeInterpolator |
Interface que permite implementar o próprio interpolador. |
Animar com o ValueAnimator
A classe ValueAnimator
permite animar valores de algum tipo durante uma animação, especificando um conjunto de int
, float
ou valores de cor para animar. Você consegue um ValueAnimator
chamando um dos métodos de fábrica dele: ofInt()
, ofFloat()
ou ofObject()
. Por exemplo:
Kotlin
ValueAnimator.ofFloat(0f, 100f).apply { duration = 1000 start() }
Java
ValueAnimator animation = ValueAnimator.ofFloat(0f, 100f); animation.setDuration(1000); animation.start();
Nesse código, o ValueAnimator
começa a calcular os valores da
animação, entre 0 e 100, para uma duração de 1.000 ms, quando o método start()
é executado.
Também é possível especificar um tipo personalizado para animação fazendo o seguinte:
Kotlin
ValueAnimator.ofObject(MyTypeEvaluator(), startPropertyValue, endPropertyValue).apply { duration = 1000 start() }
Java
ValueAnimator animation = ValueAnimator.ofObject(new MyTypeEvaluator(), startPropertyValue, endPropertyValue); animation.setDuration(1000); animation.start();
Nesse código, o ValueAnimator
começa a calcular os valores da animação, entre startPropertyValue
e endPropertyValue
, usando a lógica fornecida por MyTypeEvaluator
para uma duração de 1.000 ms, quando o método start()
é executado.
Você pode usar os valores da animação adicionando um AnimatorUpdateListener
ao objeto ValueAnimator
, conforme mostrado no código a seguir:
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); } });
No método onAnimationUpdate()
, é possível acessar o valor atualizado da animação e usá-lo em uma propriedade de uma das suas visualizações. Para saber mais sobre listeners, consulte a seção sobre
Listeners de animação.
Animar com o ObjectAnimator
O ObjectAnimator
é uma subclasse do ValueAnimator
(discutido na seção anterior) e combina o mecanismo de tempo e o cálculo de valor de ValueAnimator
com a capacidade de animar uma propriedade nomeada de um objeto de destino. Isso facilita muito a animação de qualquer objeto, já que não é mais necessário implementar o ValueAnimator.AnimatorUpdateListener
porque a propriedade animada é atualizada automaticamente.
Instanciar um ObjectAnimator
é semelhante a um ValueAnimator
, mas você também especifica o objeto e o nome da propriedade dele (como uma string) com os valores entre os quais a animação será animada:
Kotlin
ObjectAnimator.ofFloat(textView, "translationX", 100f).apply { duration = 1000 start() }
Java
ObjectAnimator animation = ObjectAnimator.ofFloat(textView, "translationX", 100f); animation.setDuration(1000); animation.start();
Para que ObjectAnimator
atualize as propriedades corretamente, faça o seguinte:
- A propriedade do objeto que você está animando precisa ter uma função setter (em camelCase) na forma de
set<PropertyName>()
. Como oObjectAnimator
atualiza automaticamente a propriedade durante a animação, ele precisa conseguir acessar a propriedade com esse método setter. Por exemplo, se o nome da propriedade forfoo
, será necessário ter um métodosetFoo()
. Se esse método setter não existir, você terá três opções:- Adicione o método setter à classe se você tem os direitos para fazer isso.
- Use uma classe wrapper que você tenha o direito de mudar e faça com que esse wrapper receba o valor com um método setter válido e o encaminhe para o objeto original.
- Use
ValueAnimator
- Se você especificar apenas um valor para o parâmetro
values...
em um dos métodos de fábricaObjectAnimator
, ele será considerado o valor final da animação. Portanto, a propriedade do objeto que você está animando precisa ter uma função getter usada para receber o valor inicial da animação. A função getter precisa estar na forma deget<PropertyName>()
. Por exemplo, se o nome da propriedade forfoo
, será necessário ter um métodogetFoo()
. - Os métodos getter (se necessário) e setter da propriedade que você está animando precisam operar no mesmo tipo que os valores inicial e final especificados para
ObjectAnimator
. Por exemplo, você precisa tertargetObject.setPropName(float)
etargetObject.getPropName()
se construir o seguinteObjectAnimator
:ObjectAnimator.ofFloat(targetObject, "propName", 1f)
- Dependendo da propriedade ou do objeto que você está animando, pode ser necessário chamar o método
invalidate()
em uma visualização para forçar a tela a se redesenhar com os valores animados atualizados. Faça isso no callbackonAnimationUpdate()
. Por exemplo, a animação da propriedade de cor de um objeto drawable só causa atualizações na tela quando esse objeto for redesenhado. Todos os setters de propriedade na visualização, comosetAlpha()
esetTranslationX()
, invalidam a visualização corretamente. Portanto, você não precisa invalidar a visualização ao chamar esses métodos com novos valores. Para saber mais sobre listeners, consulte a seção sobre Listeners de animação.
Coreografar várias animações com um AnimatorSet
Em muitos casos, você quer exibir uma animação que depende do momento em que outra começa ou termina. O sistema Android permite agrupar animações em uma AnimatorSet
para que você possa especificar se quer iniciar animações simultaneamente, em sequência ou após um atraso especificado. Também é possível aninhar objetos AnimatorSet
dentro uns dos outros.
O snippet de código abaixo reproduz os objetos Animator
abaixo da seguinte maneira:
- Exibe
bounceAnim
. - Abre
squashAnim1
,squashAnim2
,stretchAnim1
estretchAnim2
ao mesmo tempo. - Exibe
bounceBackAnim
. - Exibe
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();
Listeners de animação
Você pode ouvir eventos importantes durante a animação com os listeners descritos abaixo.
Animator.AnimatorListener
onAnimationStart()
- Chamado quando a animação é iniciada.onAnimationEnd()
: chamado quando a animação termina.onAnimationRepeat()
: chamado quando a animação se repete.onAnimationCancel()
: chamado quando a animação é cancelada. Uma animação cancelada também chamaonAnimationEnd()
, independente de como ela foi finalizada.
ValueAnimator.AnimatorUpdateListener
-
onAnimationUpdate()
: chamado em todos os frames da animação. Ouça esse evento para usar os valores calculados gerados porValueAnimator
durante uma animação. Para usar o valor, consulte o objetoValueAnimator
transmitido no evento para ver o valor animado atual com o métodogetAnimatedValue()
. A implementação desse listener será necessária se você usarValueAnimator
.Dependendo da propriedade ou do objeto que você está animando, pode ser necessário chamar
invalidate()
em uma visualização para forçar essa área da tela a se redesenhar com os novos valores animados. Por exemplo, animar a propriedade de cor de um objeto drawable só causará atualizações na tela quando esse objeto for redesenhado. Todos os setters de propriedade na visualização, comosetAlpha()
esetTranslationX()
, invalidam a visualização corretamente. Assim, você não precisa invalidar a visualização ao chamar esses métodos com novos valores.
-
Você pode estender a classe AnimatorListenerAdapter
em vez de
implementar a interface Animator.AnimatorListener
se não
quiser implementar todos os métodos da interface
Animator.AnimatorListener
. A classe AnimatorListenerAdapter
fornece implementações
vazias dos métodos que você pode substituir.
Por exemplo, o snippet de código a seguir cria um AnimatorListenerAdapter
apenas para o callback 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()); }
Animar mudanças de layout para objetos ViewGroup
O sistema de animação de propriedades oferece o recurso de animar mudanças em objetos ViewGroup, além de oferecer uma maneira fácil de animar os próprios objetos View.
Você pode animar mudanças de layout em um ViewGroup com a
classe LayoutTransition
. As visualizações em um ViewGroup podem
passar por uma animação que aparece e desaparece quando você as adiciona ou
remove de um ViewGroup ou quando você chama o método
setVisibility()
de uma visualização com
VISIBLE
, INVISIBLE
ou
GONE
. As visualizações restantes no ViewGroup também podem
ser animadas nas novas posições delas quando você adiciona ou remove visualizações. Você pode definir
as animações abaixo em um objeto LayoutTransition
chamando setAnimator()
e transmitindo um objeto Animator
com uma das
constantes LayoutTransition
abaixo:
APPEARING
: sinalização que indica a animação executada em itens que aparecem no contêiner.CHANGE_APPEARING
: sinalização que indica a animação executada em itens que estão mudando devido a um novo item exibido no contêiner.DISAPPEARING
: sinalização que indica a animação executada em itens que estão desaparecendo do contêiner.CHANGE_DISAPPEARING
: sinalização que indica a animação executada em itens que estão mudando devido ao desaparecimento de um item do contêiner.
Você pode definir suas animações personalizadas para esses quatro tipos de eventos para personalizar a aparência das transições de layout ou apenas dizer ao sistema de animação para usar as animações padrão.
Para definir o atributo android:animateLayoutchanges
como true
para o
ViewGroup, faça o seguinte:
<LinearLayout android:orientation="vertical" android:layout_width="wrap_content" android:layout_height="match_parent" android:id="@+id/verticalContainer" android:animateLayoutChanges="true" />
Definir esse atributo como verdadeiro anima automaticamente as visualizações adicionadas ou removidas do ViewGroup, bem como as visualizações restantes no ViewGroup.
Animar mudanças de estado de visualização com o StateListAnimator
A classe StateListAnimator
permite definir animadores que são executados quando
o estado de uma visualização muda. Esse objeto se comporta como um wrapper de um
objeto Animator
, chamando essa animação sempre que o estado de visualização
especificado (como "pressionado" ou "focado") muda.
O StateListAnimator
pode ser definido em um recurso XML com um elemento
<selector>
raiz e elementos <item>
filhos que especificam
um estado de visualização diferente definido pela classe StateListAnimator
. Cada
<item>
contém a definição de um conjunto de animações de propriedade.
Por exemplo, o arquivo abaixo cria um animador de lista de estados que muda a escala x e y da visualização quando é pressionado:
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>
Para anexar o animador da lista de estados a uma visualização, adicione o atributo
android:stateListAnimator
desta maneira:
<Button android:stateListAnimator="@xml/animate_scale" ... />
Agora, as animações definidas em animate_scale.xml
são usadas quando o estado
desse botão muda.
Ou, para atribuir um animador de lista de estados a uma visualização no código, use o método AnimatorInflater.loadStateListAnimator()
e atribua o animador à visualização com o método View.setStateListAnimator()
.
Ou, em vez de animar propriedades da visualização, você pode reproduzir uma animação drawable entre
mudanças de estado usando AnimatedStateListDrawable
.
Alguns dos widgets de sistema no
Android 5.0 usam essas animações por padrão. O exemplo abaixo mostra como
definir um AnimatedStateListDrawable
como um recurso 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>
Usar um TypeEvaluator
Caso você queira animar um tipo desconhecido para o sistema Android, crie seu próprio avaliador implementando a interface TypeEvaluator
. Os tipos conhecidos pelo sistema Android são int
, float
ou uma cor, que são compatíveis com os avaliadores de tipo IntEvaluator
, FloatEvaluator
e ArgbEvaluator
.
Há apenas um método para implementar na interface TypeEvaluator
, o método evaluate()
. Isso permite que o animador usado retorne um valor adequado para a propriedade animada no ponto atual da animação. A classe FloatEvaluator
demonstra
como fazer isso:
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); } }
Observação:quando ValueAnimator
(ou ObjectAnimator
) é executado, ele calcula uma fração atual decorrida da
animação (um valor entre 0 e 1) e, em seguida, uma versão interpolada disso, dependendo
do interpolador que você está usando. A fração interpolada é o que seu TypeEvaluator
recebe por meio do parâmetro fraction
. Assim, você não precisa considerar o interpolador ao calcular valores animados.
Usar interpoladores
Um interpolador define como valores específicos em uma animação são calculados como uma função do tempo. Por exemplo, você pode especificar que animações aconteçam linearmente em toda a animação, o que significa que a animação se moverá uniformemente o tempo todo, ou especificar animações para usar tempo não linear, por exemplo, usando aceleração ou desaceleração no início ou no final da animação.
Os interpoladores no sistema de animação recebem uma fração dos animadores que representam o tempo decorrido da animação. Os interpoladores modificam essa fração para que ela coincida com o tipo de animação que ela visa fornecer. O sistema Android oferece um conjunto de interpoladores comuns no
android.view.animation package
. Se nenhum deles atender às suas necessidades, implemente a interface TimeInterpolator
e crie sua própria interface.
Por exemplo, veja abaixo uma comparação da forma como o interpolador padrão AccelerateDecelerateInterpolator
e o LinearInterpolator
calculam frações interpoladas.
O LinearInterpolator
não afeta a fração decorrida. O AccelerateDecelerateInterpolator
acelera na animação e desacelera para fora dela. Os métodos a seguir definem a lógica desses interpoladores:
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; }
A tabela a seguir representa os valores aproximados calculados por esses interpoladores para uma animação com duração de 1.000 ms:
ms decorridos | Fração decorrida/fração interpolada (linear) | Fração interpolada (acelerar/desacelerar) |
---|---|---|
0 | 0 | 0 |
200 | 0,2 | 0,1 |
400 | 0,4 | 0,345 |
600 | 0,6 | 0,8 |
800 | 0,8 | 0,9 |
1.000 | 1 | 1 |
Como mostrado na tabela, o LinearInterpolator
muda os valores
na mesma velocidade, 0,2 para cada 200 ms decorridos. O AccelerateDecelerateInterpolator
muda os valores mais rapidamente que LinearInterpolator
entre 200 ms e 600 ms, e mais lento entre 600 ms e 1.000 ms.
Especificar frames-chave
Um objeto Keyframe
consiste em um par de tempo/valor que permite definir
um estado específico em um momento específico de uma animação. Cada frame-chave também pode ter o próprio
interpolador para controlar o comportamento da animação no intervalo entre o tempo do frame-chave
anterior e o do frame atual.
Para instanciar um objeto Keyframe
, use um dos métodos de fábrica, ofInt()
, ofFloat()
ou ofObject()
, para conseguir o tipo apropriado de Keyframe
. Em seguida, chame
o método de fábrica ofKeyframe()
para
receber um objeto PropertyValuesHolder
. Depois de ter o objeto, você pode acessar um animador transmitindo o objeto PropertyValuesHolder
e o objeto que será animado. O snippet de código a seguir mostra como fazer isso:
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);
Animar visualizações
O sistema de animação de propriedades permite uma animação simplificada de objetos View e oferece algumas vantagens sobre o sistema de animação de visualizações. O sistema de animação de visualizações transformou objetos View mudando a maneira como eles eram desenhados. Isso era processado no contêiner de cada visualização, porque ela não tinha propriedades para manipular. Isso resultava na animação da visualização, mas não mudava em nada o objeto View em si. Isso leva a um comportamento como um objeto ainda existente no local original, mesmo que tenha sido desenhado em um local diferente na tela. No Android 3.0, novas propriedades e os métodos getter e setter correspondentes foram adicionados para eliminar essa desvantagem.
O sistema de animação de propriedades
pode animar visualizações na tela mudando as propriedades reais nos objetos View. Além
disso, as visualizações também chamam automaticamente o método invalidate()
para atualizar a tela sempre que as propriedades são alteradas. As novas propriedades na classe View
que facilitam as animações de propriedade são:
translationX
etranslationY
: essas propriedades controlam onde a visualização está localizada como um delta das coordenadas superior e esquerda, que são definidas pelo contêiner do layout.rotation
,rotationX
erotationY
: essas propriedades controlam a rotação em 2D (propriedaderotation
) e 3D em torno do ponto de rotação.scaleX
escaleY
: essas propriedades controlam o escalonamento 2D de uma visualização ao redor do ponto de rotação.pivotX
epivotY
: essas propriedades controlam o local do ponto de rotação, em torno do qual ocorrem as transformações de rotação e dimensionamento. Por padrão, o ponto de rotação está localizado no centro do objeto.x
ey
: são propriedades de utilitário simples para descrever o local final da visualização no contêiner, como uma soma dos valores superior e esquerdo e dos valores translationX e translationY.alpha
: representa a transparência Alfa na visualização. Por padrão, esse valor é 1 (opaco), sendo que o valor 0 representa transparência total (não visível).
Para animar uma propriedade de um objeto View, como a cor ou o valor de rotação, basta criar um animador de propriedade e especificar a propriedade View que você quer animar. Por exemplo:
Kotlin
ObjectAnimator.ofFloat(myView, "rotation", 0f, 360f)
Java
ObjectAnimator.ofFloat(myView, "rotation", 0f, 360f);
Para mais informações sobre como criar animadores, consulte as seções sobre animação com o ValueAnimator e o ObjectAnimator.
Animar com o ViewPropertyAnimator
O ViewPropertyAnimator
oferece uma maneira simples de animar várias
propriedades de um View
em paralelo usando um único objeto
Animator
subjacente. Ele se comporta de maneira semelhante a uma ObjectAnimator
, porque modifica os
valores reais das propriedades da visualização, mas é mais eficiente ao animar várias propriedades de uma
vez. Além disso, o código para usar o ViewPropertyAnimator
é muito
mais conciso e fácil de ler. Os snippets de código abaixo mostram as diferenças no uso de vários
objetos ObjectAnimator
, um único
ObjectAnimator
e o ViewPropertyAnimator
ao
animar simultaneamente as propriedades x
e y
de uma visualização.
Vários objetos 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();
Um 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);
Para informações mais detalhadas sobre ViewPropertyAnimator
, consulte a
postagem
correspondente do Blog
de desenvolvedores Android (link em inglês).
Declarar animações em XML
O sistema de animação de propriedades permite declarar animações de propriedade com XML em vez de fazer isso de forma programática. Ao definir suas animações em XML, você pode reutilizá-las em várias atividades e editar a sequência de animação com mais facilidade.
Para distinguir os arquivos de animação que usam as novas APIs de animação de propriedades daqueles que usam o
framework legado de animação de visualizações,
no Android 3.1 e versões mais recentes, salve os arquivos XML para animações de propriedade no diretório res/animator/
.
As seguintes classes de animação de propriedades têm compatibilidade com a declaração XML com as seguintes tags XML:
ValueAnimator
—<animator>
ObjectAnimator
—<objectAnimator>
AnimatorSet
—<set>
Para encontrar os atributos que podem ser usados na declaração XML, consulte Recursos de animação. O exemplo a seguir exibe os dois conjuntos de animações de objeto sequencialmente, com o primeiro conjunto aninhado exibindo duas animações de objeto juntas:
<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>
Para executar essa animação, é preciso inflar os recursos XML no código para um objeto AnimatorSet
e definir os objetos de destino para todas as animações
antes de iniciar o conjunto. Por conveniência, a chamada de setTarget()
define um único objeto de destino para todos os filhos de AnimatorSet
. O código abaixo mostra como fazer isso:
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();
Você também pode declarar um ValueAnimator
em XML, conforme
mostrado no exemplo a seguir:
<animator xmlns:android="http://schemas.android.com/apk/res/android" android:duration="1000" android:valueType="floatType" android:valueFrom="0f" android:valueTo="-100f" />
Para usar o ValueAnimator
anterior no código,
é necessário inflar o objeto, adicionar um
AnimatorUpdateListener
,
receber o valor atualizado da animação e usá-lo em uma propriedade de uma das suas visualizações,
conforme mostrado no código abaixo:
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();
Para informações sobre a sintaxe XML para definir animações de propriedade, consulte Recursos de animação .
Possíveis efeitos no desempenho da IU
Os animadores que atualizam a interface causam trabalho extra de renderização para cada frame em que a animação é executada. Por esse motivo, o uso de animações que consomem muitos recursos pode afetar negativamente o desempenho do app.
O trabalho necessário para animar a IU é adicionado ao estágio de animação do pipeline de renderização. Para descobrir se as animações afetam o desempenho do app, ative a opção Criação do perfil de renderização de GPU e monitore o estágio de animação. Para saber mais, consulte o Tutorial da Criação do perfil de renderização de GPU.