Participe do evento ⁠#Android11: apresentação de lançamento da versão Beta no dia 3 de junho.

Visão geral de animação de propriedade

O sistema de animação de propriedades é um framework robusto que permite animar quase tudo. É possível definir uma animação para mudar qualquer propriedade de objeto ao longo do tempo, independentemente de ela ser desenhada na tela ou não. Esse tipo de animação muda o valor de uma propriedade (campo em um objeto) durante um período especificado. Para animar algo, especifique a propriedade de objeto que você quer animar, como a posição de um objeto na tela, por quanto tempo você quer que ele seja animado e com que valores quer animar.

O sistema de animação de propriedade 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: é possível especificar como os valores da propriedade são calculados como uma função do tempo decorrido atual da animação.
  • Contagem e comportamento repetidos: é possível especificar se uma animação deve ou não ser repetida ao chegar ao final de um período e quantas vezes ela será repetida. Também é possível especificar se você quer exibir a animação em ordem reversa. A configuração de ordem reversa avança e retrocede a animação várias vezes, até que o número de repetições seja atingido.
  • Conjuntos de animadores: é possível agrupar animações em conjuntos lógicos que são reproduzidos juntos, sequencialmente ou depois de 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 em que seu aplicativo pode atualizar frames depende, na verdade, da ocupação geral do sistema e da rapidez com que ele pode atender o timer subjacente.

Para ver um exemplo completo de animação de propriedade, consulte a classe ChangeColor na amostra 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 representa um objeto hipotético que é 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 padrão do frame, o objeto se move horizontalmente por 10 pixels. Ao final de 40 ms, a animação é interrompida, e o objeto termina na posição horizontal 40. Esse é o exemplo de uma animação com interpolação linear, o que significa que o objeto se move a uma velocidade constante.

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 ao final dela. O objeto ainda percorre 40 pixels em 40 ms, mas não de forma linear. Inicialmente, 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 final da animação é menor que no meio.

Figura 2. Exemplo de uma animação não linear.

Vamos ver com mais detalhes como os componentes importantes do sistema de animação de propriedade calculariam animações como as ilustradas acima. A Figura 3 mostra como as principais classes funcionam umas com as outras.

Figura 3. Como as animações são calculadas.

O objeto ValueAnimator monitora o tempo da animação, por exemplo, o tempo de execução da animação e o valor atual da propriedade que ela está animando.

O ValueAnimator encapsula um TimeInterpolator, que define a interpolação da 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 TypeEvaluator seria IntEvaluator.

Para iniciar uma animação, crie um ValueAnimator e atribua valores de início e de término para a propriedade que você quer animar, assim como a duração da animação. Ao chamar 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 em quanto tempo já passou. A fração decorrida representa a porcentagem do tempo que a animação já concluiu, 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 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 considera 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, em 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 adequado 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 nesse momento seria 0,15 × (40 - 0), ou seja, 6.

Diferenças entre a animação de propriedade e a animação de visualização

O sistema de animação de visualizações permite animar somente objetos View, então se você quiser animar objetos não , será necessário implementar o próprio código. O sistema de animação de visualizações também é restrito ao fato de que ele expõe apenas alguns aspectos de um 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 está desenhada, não a visualização em si. Por exemplo, se você animou um botão para movê-lo pela tela, ele é desenhado corretamente, mas o local real onde você pode clicar no botão não muda. Portanto, é preciso implementar a própria lógica para processar essa ação.

Com o sistema de animação de propriedades, essas restrições são totalmente removidas, e você pode animar qualquer propriedade de qualquer objeto (visualizações ou não) e o próprio objeto é 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 precisa ser feito ou se o código existente já funcionar da maneira esperada, não será necessário usar o sistema de animação de propriedades. Você também pode usar os dois sistemas de animação para situações diferentes se o caso de uso surgir.

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, também é possível 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 oferece a estrutura básica para criar animações. Em geral, essa classe não é usada diretamente, porque ela oferece apenas as funcionalidades mínimas que precisam ser estendidas para que haja compatibilidade total com os valores da animação. As subclasses a seguir estendem Animator:

Tabela 1. Animadores.

Classe Descrição
ValueAnimator Principal mecanismo de tempo da animação de propriedade que também calcula os valores para a propriedade a ser animada. Ele tem todas as funcionalidades básicas que calculam valores de animação e contém os detalhes de tempo de cada animação, informações sobre possíveis repetições, listeners que recebem eventos de atualização e a capacidade de definir tipos personalizados para avaliar. A animação de propriedades é dividida em duas partes: cálculo dos valores animados e definição desses valores no objeto e na propriedade que está sendo animada. ValueAnimator não realiza a segunda parte, então é necessário ouvir atualizações para 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 Subclasse de ValueAnimator que permite definir um objeto de destino e uma propriedade de objeto para animar. Essa classe atualiza a propriedade corretamente ao calcular um novo valor para a animação. Use ObjectAnimator na maioria das vezes, já que isso facilita muito o processo de animação de valores em objetos de destino. No entanto, em alguns casos você pode usar ValueAnimator diretamente, porque ObjectAnimator tem um pouco mais de restrições, como exigir que métodos do acessador específicos estejam presentes no objeto de destino.
AnimatorSet Oferece um mecanismo para agrupar animações. Assim elas são executadas de forma relacionada. É possível definir que as animações sejam exibidas juntas, em sequência ou depois de 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 nessas informações. 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 os valores das 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, será preciso implementar 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 e float, além de valores de cor, se você quiser processar esses tipos de forma diferente do comportamento padrão. Consulte a seção sobre Usar um TypeEvaluator para ver mais informações sobre como programar um avaliador personalizado.

Um interpolador de tempo define como os 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, ou seja, a animação se moverá de maneira uniforme o tempo todo. Ou você pode especificar que animações usem o 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 oferecidos atender às suas necessidades, implemente a interface TimeInterpolator e crie seu interpolador. Consulte Usar interpoladores para ver mais informações sobre como programar 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 para o 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. Para receber um ValueAnimator, chame 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 calculando 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 calculando 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.

Use 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 de animação atualizado e usá-lo em uma propriedade de uma das suas visualizações. Para mais informações sobre listeners, consulte a seção sobre Listeners de animação.

Animar com o ObjectAnimator

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 a propriedade nomeada de um objeto de destino. Isso torna a animação de qualquer objeto muito mais fácil, já que não é mais necessário implementar o ValueAnimator.AnimatorUpdateListener porque a propriedade animada é atualizada automaticamente.

A instanciação de um ObjectAnimator é semelhante à de ValueAnimator, mas o objeto e o nome da propriedade dele (como uma string) também são especificados junto com o intervalo de valores para animar:

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 o ObjectAnimator atualiza automaticamente a propriedade durante a animação, ele precisa acessar a propriedade com esse método setter. Por exemplo, se o nome da propriedade for foo, será necessário usar um método setFoo(). 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ê possa mudar, faça o wrapper receber o valor com um método setter válido e encaminhe-o para o objeto original.
    • Use ValueAnimator.
  • Se você especificar apenas um valor para o parâmetro values... em um dos métodos ObjectAnimator de fábrica, 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 de get<PropertyName>(). Por exemplo, se o nome da propriedade for foo, será necessário usar um método getFoo().
  • 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, será necessário ter targetObject.setPropName(float) e targetObject.getPropName(float) se você construir o seguinte ObjectAnimator:
        ObjectAnimator.ofFloat(targetObject, "propName", 1f)
        
  • Dependendo da propriedade ou objeto que você estiver animando, poderá 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 callback onAnimationUpdate(). Por exemplo, a animação da propriedade de cor de um objeto drawable só causará atualizações na tela quando esse objeto for redesenhado. Todos os setters de propriedade em visualização, como setAlpha() e setTranslationX(), invalidam a visualização corretamente. Assim, você não precisa invalidar a visualização ao chamar esses métodos com novos valores. Para mais informações sobre listeners, consulte a seção sobre Listeners de animação.

Coreografar várias animações com um AnimatorSet

É muito comum querer exibir uma animação que depende do momento em que outra começa ou termina. O sistema Android permite agrupar animações em um AnimatorSet para que você possa especificar se quer iniciar animações simultaneamente, sequencialmente ou depois de um atraso especificado. Também é possível aninhar objetos AnimatorSet dentro uns dos outros.

O snippet de código a seguir exibe os objetos Animator assim:

  1. Exibe bounceAnim.
  2. Exibe squashAnim1, squashAnim2, stretchAnim1 e stretchAnim2 ao mesmo tempo.
  3. Exibe bounceBackAnim.
  4. 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
  • ValueAnimator.AnimatorUpdateListener
    • onAnimationUpdate(): chamado em todos os frames da animação. Ouça esse evento para usar os valores calculados gerados por ValueAnimator durante uma animação. Para usar o valor, consulte o objeto ValueAnimator transmitido para o evento para receber o valor animado atual com o método getAnimatedValue(). A implementação desse listener é necessária se você usa ValueAnimator.

      Dependendo da propriedade ou objeto que você está animando, talvez seja 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, a animação da 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, como setAlpha() e setTranslationX(), invalidam a visualização corretamente. Assim, você não precisa invalidar a visualização ao chamar esses métodos com novos valores.

Se você não quer implementar todos os métodos da interface Animator.AnimatorListener, estenda a classe AnimatorListenerAdapter em vez de implementar a interface . A classe AnimatorListenerAdapter oferece implementações vazias dos métodos que você pode modificar.

Por exemplo, o snippet de código a seguir cria um AnimatorListenerAdapter somente 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 permite animar mudanças em objetos ViewGroup, além de oferecer uma maneira fácil de animar os próprios objetos View.

É possível animar mudanças de layout em um ViewGroup com a classe LayoutTransition. As visualizações dentro de 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 seguintes animações em um objeto LayoutTransition chamando setAnimator() e transmitindo um objeto Animator com uma das seguintes constantes LayoutTransition:

  • APPEARING: sinalização que indica a animação executada nos itens exibidos 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 porque um item está desaparecendo do contêiner.

Defina as próprias animações exclusivas 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.

A amostra LayoutAnimations nas Demonstrações de API mostra como definir animações para transições de layout e depois configurar as animações nos objetos View que você quer animar.

A LayoutAnimationsByDefault e o arquivo de recurso de layout layout_animations_by_default.xml correspondente mostram como ativar as transições de layout padrão para ViewGroups em XML. Para isso, basta definir o atributo android:animateLayoutchanges como true para o ViewGroup. Por exemplo:

    <LinearLayout
        android:orientation="vertical"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:id="@+id/verticalContainer"
        android:animateLayoutChanges="true" />
    

A definição desse 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 para um objeto Animator, chamando essa animação sempre que o estado de visualização especificado (como "pressionado" ou "focalizado") mudar.

O StateListAnimator pode ser definido em um recurso XML com um elemento <selector> raiz e elementos <item> filhos que especificam, individualmente, um estado de visualização diferente definido pela classe . Cada <item> contém a definição de um conjunto de animações de propriedade.

Por exemplo, o arquivo a seguir 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 da seguinte 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 é alterado.

Ou se preferir 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 as propriedades da visualização, você pode exibir uma animação drawable entre as mudanças de estado por meio de AnimatedStateListDrawable. Alguns dos widgets do sistema no Android 5.0 usam essas animações por padrão. O exemplo a seguir mostra como definir um AnimatedStateListDrawable como 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 queira animar um tipo desconhecido para o sistema Android, você pode criar o 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 evaluate(). Isso permite que o animador em questão retorne um valor adequado para sua propriedade animada no ponto atual da animação. A classe FloatEvaluator mostra 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) for executado, ele calculará uma fração atual de tempo decorrido da animação (valor entre 0 e 1) e depois uma versão interpolada desse dado, dependendo do interpolador que você estiver 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, é possível especificar que animações aconteçam linearmente em toda a animação, ou seja, a animação se moverá de maneira uniforme o tempo todo, ou especificar que animações usem o tempo não linear, acelerando ou desacelerando no início ou fim da animação.

Os interpoladores no sistema de animações recebem uma fração de 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 a ser fornecido. 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 seu interpolador.

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 ao entrar na animação e desacelera ao sair 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
1000 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 lentamente entre 600 ms e 1.000 ms.

Especificar frames-chave

Um objeto Keyframe consiste em um par de tempo e valor que permite definir um estado específico em um determinado momento 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 a hora do frame-chave anterior e a hora do frame-chave atual.

Para instanciar um objeto Keyframe, é preciso usar um dos métodos de fábrica, ofInt(), ofFloat() ou ofObject(), para ter o tipo correto de . Em seguida, chame o método de fábrica ofKeyframe() para receber um objeto PropertyValuesHolder. Quando você tiver o objeto, poderá acessar um animador transmitindo o objeto PropertyValuesHolder e o objeto a 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 dos objetos View e oferece algumas vantagens em relação ao sistema de animação de visualizações. O sistema de animação de visualizações transformava objetos View mudando a forma como eles eram desenhados. Isso era processado no contêiner de cada visualização, porque ela mesma 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 gerava certos comportamentos, como um objeto ainda existir no local original, mesmo que tivesse sido desenhado em outro lugar 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 delas são alteradas. As novas propriedades na classe View que facilitam as animações de propriedade são:

  • translationX e translationY: 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 e rotationY: essas propriedades controlam a rotação em 2D (propriedade ) e em 3D ao redor do ponto de rotação.
  • scaleX e scaleY: essas propriedades controlam o escalonamento 2D de uma visualização ao redor do ponto de rotação.
  • pivotX e pivotY: essas propriedades controlam o local do ponto de rotação, em torno do qual ocorrem as transformações de rotação e escalonamento. Por padrão, o ponto de rotação está localizado no centro do objeto.
  • x e y: são propriedades de utilitários simples que descrevem 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. Esse valor é 1 (opaco) por padrão, 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 ao mesmo tempo por meio de um único objeto Animator subjacente. Ele se comporta de maneira semelhante a um ObjectAnimator, porque modifica os valores reais das propriedades da visualização, mas é mais eficiente ao animar várias propriedades de uma só vez. Além disso, o código para usar o ViewPropertyAnimator é muito mais conciso e fácil de ler. Os snippets de código a seguir 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 mais detalhes sobre o ViewPropertyAnimator, consulte a postagem correspondente do Blog do Android Developers (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 mais facilmente.

Para distinguir os arquivos de animação que usam as novas APIs de animação de propriedades daqueles que usam o framework legado animação de visualizações, a partir do Android 3.1 você deve salvar os XML files de 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:

Para encontrar os atributos que podem ser usados na sua declaração XML, consulte Recursos de animação. O exemplo a seguir exibe os dois conjuntos de animações de objeto em sequência, sendo que o primeiro conjunto aninhado exibe 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 a seguir 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();
    

Também é possível declarar um ValueAnimator em XML, como 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, é preciso inflar o objeto, adicionar um AnimatorUpdateListener, acessar o valor de animação atualizado e usá-lo em uma propriedade de uma das suas visualizações, como mostrado no código a seguir:

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 mais 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 IU geram uma renderização extra para cada frame em que a animação é executada. Por isso, o uso de animações que consomem muitos recursos pode afetar negativamente o desempenho do seu app.

O trabalho necessário para animar a IU é adicionado ao estágio de animação do pipeline de renderização. Para saber se suas animações afetam o desempenho do app, ative a opção Classificar renderização da GPU e monitore o estágio de animação. Para mais informações, consulte o Tutorial da criação do perfil de renderização de GPU.