>

Animaciones con ConstraintLayout y TransitionManager #AndroidMeetsKotlin

Francisco Manuel López Jurado     Colaboraciones    05/10/2018

Las animaciones siempre nos ha traído de cabeza a prácticamente la mayoría de desarrolladores en Android. Cada vez son más las facilidades que nos proporciona el SDK para conseguir que nuestras animaciones no acaben en un código ilegible o demasiado tedioso.

En este caso, ConstrantLayout junto con ConstraintSet y TransitionManager nos proporcionan una herramienta con la que podremos crear animaciones sin mucha complicación y realmente fluidas.

ConstraintSet

Permite definir programáticamente un conjunto de restricciones aplicables a nuestras vistas. Podemos crear, guardar o clonar restricciones y luego aplicarlas a un ConstraintLayout. Existen distintas formas de crear/inicializar esta clase:

  • Manualmente:
  • Desde un layout:
  • Desde un ConstraintLayout ya existente:

TransitionManager

Esta clase fue añadida en la API 19, su objetivo principal es la ejecución de animaciones basadas en cambios de escena. En este caso, los cambios de escena se deben a cambios en las constraints de los elementos. En post posteriores veremos más características de este manager y su gran capacidad.

¡Empezamos!

Una vez que ya tenemos los conceptos teóricos claros, vamos a ir paso a paso detallando su implementación. Es importante señalar que crearemos una vista propia donde realizaremos la animación, esta vista se llamará “ExpandableCard.kt”.

La animación que vamos a realizar es la siguiente.

1.- Creamos el layout inicial.

Este layout tendrá las constraints o restricciones iniciales para el comienzo de la animación.

Para comenzar vamos a definir una serie de guidelines que nos ayudarán a delimitar hasta dónde se moverán nuestras vistas en la animación. Las guidelines son:

  • card__guideline__left. Tipo porcentaje y vertical, valor 0.1 (10%).
  • card__guideline__right. Tipo porcentaje y vertical, valor 0.9 (90%).
  • card__guideline__bottom. Tipo porcentaje y horizontal, valor 0.7 (70%).
  • card__guideline__top. Tipo porcentaje y horizontal, valor 0.2 (20%).

Las vistas principales con sus constraints son:

  • card__container__image. CardView con la imagen, la cuál al inicio debe ocupar toda la vista por eso definiremos sus 4 constraints como “parent”.
  • card__container__info. CardView con el texto de información. Al igual que el contenedor de la imagen, ocupará también todo el ancho y alto ya que estará por debajo de la imagen.
  • card__label__title. Título de la vista, estará centrado y ocupará desde la guideline con id “card__guideline__left” hasta “card__guideline__right”.

La imagen inicial que tenemos es:

¡IMPORTANTE! Todos los hijos (que no nietos) que tenga el ConstraintLayout deben tener un id especificado, ya que el layout que contiene la animación final debe conocer la posición de todos sus elementos.

El código del layout “card_collapsed.xml” podemos encontrarlo en el siguiente enlace.

2.- Creamos el layout con la animación final.

En este caso NO es necesario especificar atributos referidos a establecer colores, tamaño de textos, etc. Sólo necesitaremos definir las constraints para cada elemento del layout inicial. De esta forma se podrán desplazar todas las vistas desde su punto inicial hasta el final.

En este caso tendremos las siguientes constraints:

  • card__container__image. Cambiaremos todas sus constraints exceptio la superior, la constraint inicial (“layout_constraintStart_toEndOf”) la estableceremos al final de la guideline con id “card__guideline__left”. La final (“layout_constraintEnd_toStartOf”) pasará a ser la guideline con id “card__guideline__right”. Finalmente, la inferior (“layout_constraintBottom_toTopOf”) pasará a ser la guideline con id “card__guideline__bottom”.
  • card__container__info. Mantendrá todas sus constraints excepto la superior (“layout_constraintTop_toBottomOf”) ya que le indicaremos que esté justo debajo de la guideline con id “card__guideline__top”.
  • card__label__title. Mantiene todas sus constraints.

La imagen final que tenemos gracias a estas constraints es:

El código del layout “card_expanded.xml” podemos encontrarlo en el siguiente enlace.

3.- Creamos la vista personalizada.

Ahora pasamos a implementar nuestra clase “ExpandableCard.kt” que extenderá de ConstraintLayout. Cada vez que creamos una vista personalizada debemos indicar todos sus constructores, en Kotlin podemos utilizar la anotación “@JvmOverloads” que nos ayudará a unificar todos los constructores en uno. Después, simplemente estableceremos el layout inicial a nuestra vista.

El código podemos encontrarlo en el siguiente enlace.

4.- Configuramos la animación.

Antes de nada, definimos una variable “applyInitAnimation” la cual nos ayudará a saber el estado (inicial o final) de nuestra animación.

Después, sacamos las constraints de los layouts inicial y finales, de esta forma aplicaremos uno u otro en función del estado de la misma. Para ello, hacemos uso del método “clone” de ConstraintSet, el cuál clonará las constraints del layout indicado y las establecerá en las distintas variables:

  • Variable “constraintsFinal” de tipo ConstraintSet. Sacaremos las constraints del layout “card_expanded”.
  • Variable “constraintsInitial” de tipo ConstraintSet. Sacaremos las constraints del layout “card_collapsed”.

Una vez que ya las tenemos, aplicamos los distintos ConstraintSet en función del estado de la misma (controlado por la variable “applyInitAnimation”) a la vista principal.

El método “beginDelayedTransition” es el encargado de hacer la animación cuando establezcamos las constraints al layout haciendo uso del método “applyTo” de ConstraintSet.

El código será el siguiente:

Sin más, ya tenemos implementados todos los elementos para nuestra animación. Podréis encontrar todo el código del proyecto en Github. Espero que te haya gustado.


Sobre el autor

Francisco Manuel López Jurado   

Apasionado de la tecnología y todo lo que la rodea. Desarrollador Senior Android e iOS.