Material Animations Android
Francisco Manuel López Jurado Colaboraciones 18/11/2016
Importante: Todas las animaciones que se van a detallar están disponibles a partir de la API 21 (Lollipop).
En este post haremos un breve recorrido por algunas de las animaciones que nos ha traído Material Design. Pero antes de nada, comentaremos una clase llamada ViewAnimationUtils que fue añadida en la API 21. Ésta contiene un método estático que nos ayudará a realizar el efecto que comentaremos en el punto siguiente. Sin más, ¡Comenzamos!
1. Efecto “circular reveal”
Se trata de una animación circular con el propósito de mostrar u ocultar elementos en nuestra pantalla.
Para crear dicha animación necesitamos llamar al método createCircularReveal de la clase ViewAnimationUtils que recibe los siguientes parámetros:
-
View, se trata de la vista que será animada
-
Coordernada X, la coordenada X donde comenzará la animación
-
Coordenada Y, la coordenada Y donde comenzará la animación
-
Radio inicial de la animación
-
Radio final de la animación
Para ver cómo funciona la animación, crearemos una vista que tiene 3 métodos:
-
showLayout(), muestra la vista con la animación de circular reveal
-
hideLayout(), oculta la vista con la misma animación
-
toggleLayout(), comprueba los estados y efectúa una animación u otra.
A continuación adjuntamos el código de dicha vista y un ejemplo en vídeo de la animación.
package com.franlopez.betabeersdemo.common;
import android.animation.Animator;
import android.content.Context;
import android.util.AttributeSet;
import android.view.ViewAnimationUtils;
import android.widget.RelativeLayout;
public class CircularRevealRelativeLayout extends RelativeLayout {
private int ANIMATION_DURATION = 500;
/**
* CONSTRUCTORES
*/
public CircularRevealRelativeLayout(Context context) {
super(context);
}
public CircularRevealRelativeLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CircularRevealRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
private void showLayout() {
//- Obtenemos las coordenadas inferior-derecha para comenzar la animación
int cx = getRight();
int cy = getBottom();
//- Obtenemos el radio final de la animación
int finalRadius = Math.max(this.getWidth(), this.getHeight());
//- Creamos la animación a la que le pasamos los parámetros anteriormente comentados
Animator anim = ViewAnimationUtils.createCircularReveal(this, cx, cy, 0, finalRadius*2);
anim.setDuration(ANIMATION_DURATION);
anim.addListener(mShowAnimatorListener);
//- Comenzamos la animación
anim.start();
}
private void hideLayout() {
//- Obtenemos las coordenadas inferior-derecha para comenzar la animación
int cx = getRight();
int cy = getBottom();
//- Obtenemos el radio final de la animación
int finalRadius = Math.max(this.getWidth(), this.getHeight()) * 2;
//- Creamos la animación a la que le pasamos los parámetros anteriormente comentados
Animator anim = ViewAnimationUtils.createCircularReveal(this, cx, cy, finalRadius, 0);
anim.setDuration(ANIMATION_DURATION);
anim.addListener(mHideAnimatorListener);
//- Comenzamos la animación
anim.start();
}
public void toggleView() {
if (this.getVisibility() == VISIBLE) {
hideLayout();
} else {
showLayout();
}
}
/**
* LISTENERS
*/
//- Se trata del animator que usaremos para esconder la vista, sólo nos será útil sobreescribir el método onAnimationEnd.
private Animator.AnimatorListener mHideAnimatorListener = new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
//- Este evento es llamado cuando la animación ha comenzado
}
@Override
public void onAnimationEnd(Animator animation) {
//- Este evento es llamado cuando la animación ha finalizado, es el momento de poner nuestra vista como invisible.
setVisibility(GONE);
}
@Override
public void onAnimationCancel(Animator animation) {
//- Este evento es llamado cuando la animación ha sido cancelada
}
@Override
public void onAnimationRepeat(Animator animation) {
//- Este evento es llamado cuando la animación se vuelve a repetir, ya que podemos hacer animaciones que se repitan cuantas veces queramos
}
};
private Animator.AnimatorListener mShowAnimatorListener = new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
//- Este evento es llamado cuando la animación ha comenzado, es el momento de poner nuestra vista visible para poder ver la animación.
setVisibility(VISIBLE);
}
@Override
public void onAnimationEnd(Animator animation) {
//- Este evento es llamado cuando la animación ha finalizado
}
@Override
public void onAnimationCancel(Animator animation) {
//- Este evento es llamado cuando la animación ha sido cancelada
}
@Override
public void onAnimationRepeat(Animator animation) {
//- Este evento es llamado cuando la animación se vuelve a repetir, ya que podemos hacer animaciones que se repitan cuantas veces queramos
}
};
}
Vídeo: https://youtu.be/LxW_C9tonQ4
2. Transiciones entre activities
A continuación describiremos las transiciones entre activities, cómo compartir elementos entre pantallas y cómo conseguir que esos elementos tengan distintas animaciones hasta conseguir una perfecta sincronización.
Los pasos que tenemos que seguir son los siguientes:
-
Habilitar las transiciones de elementos entre distintas ventanas en el tema de tu aplicación.
-
Especificar qué transición se usará para compartir dichos elementos y cómo serán las animaciones de los elementos no compartidos. Existen varias animaciones que podemos definir, en este tutorial explicaremos las siguientes:
-
Fade, este efecto tiene varios modos que se pueden especificar bajo el atributo fadingMode. Se trata de fade_in, fade_out y fade_in_out. No es más que pasar de alfa 1 a 0 o viceversa.
-
Explode, se trata de expulsar o atraer las vistas desde o hasta el centro de la pantalla.
-
Slide, como su nombre indica, se trata de desplazamientos de vistas desde distintos lados de la pantalla. El lado desde donde vendrán los elementos se especifica con la propiedad slideEdge. Hay que destacar que la vista se posicionará sobre la otra que está justamente debajo haciendo un efecto “overlay”.
-
A todas estas transiciones se les puede especificar su propiedad de duración (duration) y retraso (startDelay), y lo más importante, al estar todas bajo un animationSet se puede especificar cómo se harán las animaciones bajo la propiedad transitionOrdering. También podemos elegir si queremos ejecutarlas todas juntas o de manera secuencial.
Necesitaremos definir dos recursos XML, uno de entrada en la pantalla y otro de salida de la misma.
A continuación se muestra un ejemplo para la transición de entrada:
-
Debemos asignar un nombre en común para los elementos que se compartirán. Podemos hacer uso del atributo android:transitionName vía XML o view.setTransitionName() vía código. Hay que tener en cuenta que tenemos que ponerlo tanto en la vista de la que venimos como a la que vamos a transitar.
- Usar el método ActivityOptions.makeSceneTransitionAnimation() para propagar las vistas que se compartirán entre las distintas pantallas. Podemos compartir uno o más elementos:
a. Un solo elemento:
ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation(MainActivity.this, imageView, "imageView");
b. Más de un elemento:
ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation(MainActivity.this, Pair.create(imageView, "imageView"),
Pair.create(textView, "textView"));
Como conclusión, todo lo descrito anteriormente crearía una animación muy vistosa y con la capacidad de hacer más personal cada aplicación que desarrollemos. Debemos utilizar las animaciones con cabeza y nunca sobrecargar nuestras aplicaciones.
Vídeo de la animación que hemos desarrollado en este post.