Animações no Android Part 2 – Property Animation

Standard

Depois do primeiro post desta mini série onde escrevi um pouco sobre como funcionam as animações no android em particular o mecanismo mais básico que permite animar Views, iremos continuar a busca do conhecimento para que possamos criar aplicações que aplicam o interessante principio de “Meaningfull motion” do Material design e oferecer uma experiência extra aos utilizadores.

Este post será focado no segundo mecanismo para poder criar animações disponível no android denominado Property Animation.

O property Animation é um framework disponibilzado por default no android e foi criado com o objectivo de oferecer mais robustez e controle ao aplicar animações. Ao contrário do View animation que vimos no post anterior, o Property Animation permite que qualquer objecto seja animado, seja este visível na UI ou não.

O funcionamento do Property Animation parte do princípio que é possível animar qualquer atributo de um objecto por um certo período de tempo e definir as seguintes características importantes :

  • Duração: O tempo total de execução da animação que se não alterado fica com o tempo default de 300ms.
  • Interpolação do Tempo : Como os valores da animacao serão calculados em função do tempo.
    Assim como na física que podemos ter um corpo a viajar em Movimento rectilíneo uniforme significando que este vai viajar de um ponto ao outro em intervalos de tempos iguais e a velocidade será constante, se definimos uma interpolação linear a uma animação os valores serão constantes e teremos um comportamento similar ao do corpo da física.
    O android dispõe de varias interpolações que se formos ver o código, não passam de formulas que podem ser copiadas e modificadas seguindo para satisfazer qual que seja a nossa vontade.
  • Comportamentos :Podemos definir  o que acontece a aplicação quando esta chega ao fim. Repete? Repete revertendo os valores? Quantas vezes a animação deve repetir?
  • Coreografia: Assim como magia, boas animações são nada mais e nada menos que mover imagens de forma rápida e organizada a uma frequência que possamos enganar o cérebro a ver algo diferente do que realmente esta acontecer.
    Tendo isto em mente, este framework nos permite definir como as animações serão coreografadas.

O Property Animation pode ser um pouco complicado de entender  logo de princípio pois possui vários componentes interligados que podem criar alguma complicação.
Para criar animações, este framework oferece 3 classes importantes que estendem a classe Animator:

  • ValueAnimator: Esta classe apenas serve para calcular os valores da propriedade a animar.
    Esta classe sabe quais são os valores no início, fim e no momento em que esta está a decorrer.
    Apesar desta classe possuir a parte mais importante do sistema de animações, por si só não é suficiente para nos ajudar com a tarefa de implementar animações de forma fácil, pois para actualizar os valores calculados a propriedade do objecto com que estamos a trabalhar, teremos de colocar um listener que fica a escuta das mudanças destes valores e manualmente fazer o trabalho de actualizar estas propriedades.
  • ObjectAnimator: Esta classe vem para oferecer mais facilidade em relação a ValueAnimator pois esta nos permite animar um objecto, especificando de uma única vez, o objecto, a propriedade a animar, os valores de início e fim e o cálculo dos valores como a actualização dos valores da propriedade será feita de forma mágica.
    Tanta facilidade vem com algumas desvantagens e uma delas é  que a classe ObjectAnimator permite-nos animar valores de propriedades de  um certo tipo de dados como int, float, argb (muita das vezes e o que precisamos) que podem não ser o suficiente caso o nosso objecto tenha uma propriedade com forma muito especifica de calcular os valores.
    Nestes casos em que nos encontramos com essas particularidades nos nossos componentes, é  muito melhor utilizar o ValueAnimator e definir um TypeEvaluator que especifica como calcular os valores da propriedade do objecto com que trabalhamos.
  • AnimatorSet: Como escrevi acima, animações assim como magia não passam de movimentos coordenados que enganam o nosso cérebro e nos fazem ver algo que não corresponde a realidade. A classe AnimatorSet como o nome ja sugere, é  a classe responsável por coreografar animações, isto é , sequência do conjunto de animações, duração, etc.

 

Passando para a parte interessante, vamos começar a mostrar como é  possível fazer algumas animações utilizando este framework  e vamos nos focar em 2 exemplos em que o primeiro será basicamente ré-implementar as animações do post anterior utilizando a classe ObjectAnimator e o segundo criar uma animação que poderia ser utilizada em uma app em produção.

Para  começar o código abaixo e um método que nos permite animar a visibilidade da view passada para ele.

public void fadeAnimation(View view){
    ObjectAnimator fadeAnim = ObjectAnimator.ofFloat(view,"alpha",0f,1f);
    fadeAnim.setDuration(ANIM_DURATION);
    fadeAnim.start();
}

Olhando para as diferentes partes deste método, temos apenas um ObjectAnimator em que nos especificamos através do método ofFloat que queremos animar valores de uma propriedade do tipo float  e passamos o objecto que queremos animar, especificamos o nome da propriedade (Visibilidade ou alpha),o valor inicial e final da animação.

Com os valores principais da animação definidos, podemos definir outros valores como a duração e a interpolação. Caso deixemos a animação sem estes atributos adicionais e invocamos o método start, esta será executada por default por 300ms e utilizando uma interpolação linear.

O segundo exemplo ainda na implementação de animações simples é a aplicação de Scale em que diminuímos ou aumentamos o tamanho de uma view.

Este exemplo é particularmente interessante pois para animar o tamanho de uma view temos de animar duas propriedades : a largura (SCALE_X) e o comprimento (SCALE_Y).

Olhando para o exemplo anterior e o que estivemos a falar durante o post, espero que todos tenham tomado atenção e notado  que ate então podemos implementar esta animação criando dois object animators em que cada um anima uma das propriedades e utilizar um Animator Set para coreografar as duas animações para que comecem ao mesmo tempo (Desafio voces a implementar esta animacao desta  forma).

Apesar de ser um método valido, teríamos muito código desnecessário pois o android oferece a class ProperValueHolder que nos permite guardar os valores de cada propriedade que queremos animar  e por fim passar este objecto para um único object animator como mostra o código abaixo.

public void scaleAnimOneObjectAnimator(View view){
    PropertyValuesHolder valueX = PropertyValuesHolder.ofFloat("scaleX",0,1);
    PropertyValuesHolder valueY = PropertyValuesHolder.ofFloat("scaleY",0,1);
    ObjectAnimator scale = ObjectAnimator.ofPropertyValuesHolder(view,valueX,valueY);
    scale.start();
}

Com todos os pontos cobertos ja podemos implementar uma aplicação minimamente interessante para colocar em uma app em produção.

giphy.gif

Como mostra a figura acima, a animacao simplesmente consistem nessas barras horizontais que serão animadas a propriedade alfa para criar um efeito como houvesse um movimento da esquerda para direita( Acredito que no facebook haja uma animacao similar).

Dividindo esta animacao em partes, temos primeiro a UI em que as barras nao passam de Views dentro de um linear layout como mostra o codigo abaixo:

Nota : Foi utilizado o DataBinding neste exemplo e para entender melhor algumas partes do código sugiro que leiam o post deste BLOG e o mais detalhado escrito no MEDIUM.

<?xml version="1.0" encoding="utf-8"?>
<layout>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="horizontal" android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        >

        <View
            android:id="@+id/first"
            android:layout_width="8dp"
            android:layout_height="30dp"
            android:background="@color/colorAccent"
            android:layout_margin="4dp"
            android:alpha="0"
        />

        <View
            android:id="@+id/second"
            android:layout_width="8dp"
            android:layout_height="45dp"
            android:background="@color/colorAccent"
            android:layout_margin="4dp"
            android:alpha="0"
        />

        <View
            android:id="@+id/third"
            android:layout_width="8dp"
            android:layout_height="60dp"
            android:background="@color/colorAccent"
            android:layout_margin="8dp"
            android:alpha="0"
            />

        <View
            android:id="@+id/fourth"
            android:layout_width="8dp"
            android:layout_height="45dp"
            android:background="@color/colorAccent"
            android:layout_margin="4dp"
            android:alpha="0"
        />
        <View
            android:id="@+id/fifth"
            android:layout_width="8dp"
            android:layout_height="30dp"
            android:background="@color/colorAccent"
            android:layout_margin="4dp"
            android:alpha="0"
            />

    </LinearLayout>
</layout>

Depois de definirmos o layout, com as views que queremos animar, a parte a seguir não e novidade e simplesmente temos de mapear estas views a instâncias no java para que possamos utilizar ao criar as animações.
Esta animacao foi dividida em 2 métodos em que o primeiro e simplesmente um helper para ajudar a criar um ObjectAnimator que define a animação de cada barra e o segundo método que simplesmente cria e coreografa animação como um todo

public ObjectAnimator animateStripe(View view){
    ObjectAnimator animator = ObjectAnimator.ofFloat(view,"alpha",0f,0.8f);
    return animator;
}

public void startLoadingAnimation(){

    ObjectAnimator firstStripeAnimator = animateStripe(first);
    ObjectAnimator secondeStripe = animateStripe(second);
    ObjectAnimator thirdStripeAniamtor = animateStripe(third);
    ObjectAnimator fourthStripeAnimator = animateStripe(fourth);
    ObjectAnimator fifthStripeAnimator = animateStripe(fifth);
    
    AnimatorSet set = new AnimatorSet();
    set.playSequentially(firstStripeAnimator,secondeStripe,thirdStripeAniamtor,fourthStripeAnimator,fifthStripeAnimator);
    set.setInterpolator(AnimationUtils.loadInterpolator(getActivity(),android.R.anim.accelerate_interpolator));
    set.setDuration(150);
    set.addListener(new Animator.AnimatorListener() {
        @Override
        public void onAnimationStart(Animator animation) {

        }

        @Override
        public void onAnimationEnd(Animator animation) {
                animation.setStartDelay(100);
                animation.start();
        }

        @Override
        public void onAnimationCancel(Animator animation) {

        }

        @Override
        public void onAnimationRepeat(Animator animation) {

        }
    });
    set.start();
}

O segundo método acima é o mais interessante pois inclui o AnimatorSet para coreografar a animação de todas barras para que estas seja executadas de forma sequencial.
Como podem ver podemos também definir a interpolação e a duração para o conjunto e o AnimatorSet saberá qual o tempo e como deverá calcular os valores de cada animação de forma individual para que estas se encaixem de forma perfeita ao tempo e a interpolação definida.
Por fim, adicionamos um listener ao AnimatorSet que nos permite capturar eventos dos diferentes momentos da animação  que nos permitem fazer varias coisas como ré-iniciar a animação sempre que ela chega ao fim no nosso exemplo.

E com este ultimo exemplo chegamos ao fim de mais um post e até então foi possível cobrir um mecanismo importante e que serve como base para maior parte  das animações no android.
Iremos continuar com a série, explorando o que mais pode ser feito utilizando este mecanismo e brevemente falar sobre Drawable Animations já que estas foram lançadas no novo update da android support library.

Ate a próxima,

DM

 

 

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s