Animações no Android- Property Animation
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.
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