El sistema de animación de propiedades es un marco de trabajo robusto que te permite animar casi cualquier cosa. Puedes definir una animación para cambiar cualquier propiedad de un objeto a lo largo del tiempo, más allá de que se renderice en la pantalla o no. Una animación de propiedad cambia el valor de una propiedad (un campo en un objeto) durante un período especificado. Para animar un objeto, debes indicar la propiedad que quieres animar, como la posición del objeto en la pantalla, durante cuánto tiempo quieres animarla y entre qué valores ocurrirá la animación.
El sistema de animación de propiedades te permite definir las siguientes características de una animación:
- Duración: Puedes especificar la duración de una animación. El valor predeterminado es 300 ms.
- Interpolación de tiempo: Puedes especificar cómo se calculan los valores de la propiedad en función del tiempo transcurrido actual de la animación.
- Cantidad de repeticiones y comportamiento: Puedes especificar si se repite o no una animación cuando llega al punto final del intervalo y cuántas veces se repite. También puedes especificar si quieres que la animación se reproduzca en orden inverso. Si es así, se reproducirá hacia delante y luego hacia atrás de manera repetida hasta completar el número de repeticiones.
- Conjuntos de animaciones: Puedes agrupar animaciones en conjuntos lógicos que se reproducen juntos o de manera secuencial o después de retrasos específicos.
- Retraso de la actualización de fotogramas: Puedes especificar la frecuencia con la que se actualizan los fotogramas de la animación. El valor predeterminado es cada 10 ms, pero la velocidad con la que tu aplicación puede actualizar los fotogramas depende en última instancia de la disponibilidad del sistema en general y de qué tan rápido pueda obedecer al temporizador subyacente.
Para ver un ejemplo completo de animación de propiedades, consulta la clase
ChangeColor
del ejemplo CustomTransition
en GitHub.
Cómo funciona la animación de propiedades
Primero, usemos un ejemplo sencillo para ver cómo funciona una animación. En la figura 1, se muestra un
objeto hipotético animado con la propiedad x
, que representa
su posición horizontal en la pantalla. Se estableció la duración de la animación en 40 ms y la distancia
para recorrer en 40 píxeles. Cada 10 ms, que es la frecuencia de actualización de fotogramas predeterminada, el objeto se mueve
de forma horizontal en 10 píxeles. Al final de los 40 ms, se detiene la animación y el objeto termina en la
posición horizontal 40. Este es el ejemplo de una animación con interpolación lineal, lo que significa que el
objeto se mueve a una velocidad constante.

Figura 1: Ejemplo de animación lineal
También puedes especificar que las animaciones tengan una interpolación no lineal. En la figura 2, aparece un objeto hipotético que se acelera al principio de la animación y se desacelera al final. El objeto recorre los 40 píxeles en 40 ms, pero de forma no lineal. Primero, esta animación acelera el objeto hasta el punto medio y luego lo desacelera desde el punto medio hasta el final de la animación. Como se muestra en la figura 2, la distancia recorrida al principio y al final de la animación es menor que en el medio.

Figura 2: Ejemplo de animación no lineal
Analicemos en detalle cómo los componentes importantes del sistema de animación de propiedades calcularían animaciones como las que aparecen más arriba. En la figura 3, se muestra cómo funcionan las clases principales entre sí.

Figura 3: Cómo se calculan las animaciones
El objeto ValueAnimator
realiza un seguimiento del tiempo de la animación,
como el tiempo durante el que se estuvo ejecutando y el valor actual de la propiedad que está
animando.
ValueAnimator
encapsula un TimeInterpolator
, que define la interpolación de la animación, y un TypeEvaluator
, que define el modo de calcular valores para la propiedad
que vas a animar. Por ejemplo, en la figura 2, el TimeInterpolator
utilizado sería
AccelerateDecelerateInterpolator
y el TypeEvaluator
sería IntEvaluator
.
Para iniciar una animación, crea un ValueAnimator
y asígnale los
valores de inicio y fin correspondientes a la propiedad que quieres animar, junto con la duración
de la animación. Cuando llamas a start()
, comienza
la animación. Durante toda la animación, ValueAnimator
calcula una fracción transcurrida
entre 0 y 1, en función de cuánto dura la animación y de cuánto tiempo ha transcurrido. La
fracción transcurrida representa el porcentaje del tiempo que completó la animación, de manera que 0 equivale al 0%
y 1 equivale al 100%. Por ejemplo, en la figura 1, la fracción transcurrida en t = 10 ms es 0.25,
ya que la duración total es de t = 40 ms.
Cuando ValueAnimator
termina de calcular una fracción transcurrida,
llama al TimeInterpolator
que está definido en el momento para calcular una
fracción interpolada. Una fracción interpolada mapea la fracción transcurrida a una nueva
fracción que tiene en cuenta la interpolación de tiempo definida. Por ejemplo, en la figura 2,
debido a que la animación se acelera poco a poco, la fracción interpolada, alrededor de 0.15, es menor que la fracción
transcurrida, 0.25, en t = 10 ms. En la figura 1, la fracción interpolada siempre es la misma que
la fracción transcurrida.
Cuando se calcula la fracción interpolada, ValueAnimator
llama
al TypeEvaluator
apropiado para calcular el valor de la
propiedad que estás animando en función de la fracción interpolada, el valor de inicio y
el valor de fin de la animación. Por ejemplo, en la figura 2, la fracción interpolada fue 0.15 en t = 10 ms,
por lo que el valor de la propiedad en ese momento sería 0.15 × (40 - 0), es decir, 6.
Diferencias entre la animación de propiedades y la animación de vista
Con el sistema de animación de vista, solo se pueden animar objetos
View
; por lo tanto, si deseas animar objetos que no son del tipo , debes implementar
tu propio código. El sistema de animación de vista también es limitado en el sentido de que solo
expone algunos aspectos de un objeto para animar, como el ajuste y
la rotación de una vista, pero no el color de fondo, por ejemplo.
Otra desventaja del sistema de animación de vista es que solo modificó el área donde se dibujó la vista, pero no la vista en sí. Por ejemplo, si animas un botón para que se mueva por la pantalla, el botón se dibujará correctamente, pero la ubicación real donde puedes hacer clic en el botón no cambiará, por lo que deberás implementar tu propia lógica para solucionar ese problema.
Con el sistema de animación de propiedades, estas restricciones desaparecen por completo y puedes animar cualquier propiedad de los objetos (vistas o no), ya que lo que se modifica en realidad es el objeto en sí. El sistema de animación de propiedades también es más robusto en la forma en que lleva a cabo la animación. En un nivel alto, asignas animadores a las propiedades que quieres animar, como el color, la posición o el tamaño, y puedes definir aspectos de la animación, como la interpolación y la sincronización de varios animadores.
Sin embargo, el sistema de animación de vista se configura en menos tiempo y no requiere tanto código. Si la animación de vista te permite hacer todo lo que planeas, o si tu código ya funciona de la manera deseada, no necesitas usar el sistema de animación de propiedades. Por otro lado, puede que tenga más sentido usar ambos sistemas de animación para situaciones diferentes si surge el caso práctico.
Descripción general de la API
Puedes encontrar la mayoría de las API del sistema de animación de propiedades en android.animation
. Debido a que el sistema de animación de vista ya
define muchos interpoladores en android.view.animation
, también puedes usar
esos interpoladores en el sistema de animación de propiedades. En las siguientes tablas, se describen los componentes
principales del sistema de animación de propiedades.
La clase Animator
proporciona la estructura básica para crear
animaciones. Por lo general, no se usa esta clase directamente, ya que solo brinda una funcionalidad
mínima que se debe ampliar para admitir todos los valores de la animación. Las siguientes
subclases amplían la clase Animator
:
Tabla 1: Animadores
Clase | Descripción |
---|---|
ValueAnimator |
Es el motor principal de control de tiempo y también calcula los valores de la
propiedad que se va a animar. Tiene toda la funcionalidad central para calcular los valores
de la animación y contiene los detalles del control de tiempo de cada animación, la información sobre si una
animación se repite, los objetos de escucha que reciben eventos de actualización y la capacidad de definir tipos
personalizados para evaluar. La animación de propiedades tiene dos partes: calcular los valores
animados y definir esos valores en el objeto y en la propiedad que se va a animar. ValueAnimator no lleva a cabo la segunda parte, por lo que debes escuchar
actualizaciones de los valores que calcula ValueAnimator y
usar tu propia lógica para modificar los objetos que quieres animar. Consulta la sección
Cómo animar valores con ValueAnimator para obtener más información. |
ObjectAnimator |
Es una subclase de ValueAnimator que te permite establecer un objeto
de destino y un objeto de la propiedad para animar. Esta clase actualiza la propiedad según lo que corresponde cuando
calcula un nuevo valor para la animación. Conviene usar
ObjectAnimator la mayoría del tiempo
porque facilita mucho el proceso de animar valores en los objetos de destino. Sin embargo,
a veces es mejor usar ValueAnimator directamente porque ObjectAnimator tiene más restricciones; por ejemplo, requiere que
el objeto de destino tenga métodos de acceso específicos. |
AnimatorSet |
Proporciona un mecanismo para agrupar animaciones de manera que se ejecuten una en relación con la otra. Puedes configurar las animaciones para que se reproduzcan juntas, de manera secuencial o después de un retraso definido. Consulta la sección Cómo coreografiar varias animaciones con conjuntos de animadores para obtener más información. |
Los evaluadores le indican al sistema de animación de propiedades cómo calcular valores para una propiedad
específica. Toman los datos de tiempo que proporciona una clase
Animator
, además del valor de inicio y fin de la animación, y calculan los valores animados de la propiedad
en función de estos datos. El sistema de animación de propiedades proporciona los siguientes evaluadores:
Tabla 2: Evaluadores
Clase/interfaz | Descripción |
---|---|
IntEvaluator |
Es el evaluador predeterminado para calcular valores de las propiedades int . |
FloatEvaluator |
Es el evaluador predeterminado para calcular valores de las propiedades float . |
ArgbEvaluator |
El evaluador predeterminado para calcular valores de las propiedades de color que se representan como valores hexadecimales. |
TypeEvaluator |
Es una interfaz que te permite crear tu propio evaluador. Si estás animando la
propiedad de un objeto que no es int , float o de color,
debes implementar la interfaz TypeEvaluator para definir cómo
calcular los valores animados de la propiedad del objeto. También puedes especificar un TypeEvaluator para los valores int , float y de color
si quieres procesarlos de un modo diferente del comportamiento predeterminado.
Consulta la sección Cómo usar un TypeEvaluator para obtener
más información sobre cómo escribir un evaluador personalizado. |
Un interpolador de tiempo define cómo se calculan los valores específicos de una animación en
función del tiempo. Por ejemplo, puedes especificar que las animaciones se reproduzcan de modo lineal durante todo
el transcurso, con lo cual la animación se mueve de manera uniforme todo el tiempo. También puedes especificar
que usen un tiempo no lineal; por ejemplo, que aceleren al principio y desaceleren al
final. En la tabla 3, se describen los interpoladores que contiene android.view.animation
. Si ninguno de los interpoladores proporcionados satisface
tus necesidades, implementa la interfaz TimeInterpolator
y crea el tuyo. Consulta Cómo usar interpoladores para obtener más información sobre cómo escribir un interpolador
personalizado.
Tabla 3: Interpoladores
Clase/interfaz | Descripción |
---|---|
AccelerateDecelerateInterpolator |
Es un interpolador cuya velocidad de cambio comienza y finaliza lentamente, pero se acelera en el medio. |
AccelerateInterpolator |
Es un interpolador cuya velocidad de cambio comienza lentamente y luego se acelera. |
AnticipateInterpolator |
Es un interpolador cuyo cambio comienza hacia atrás y luego se desplaza hacia delante. |
AnticipateOvershootInterpolator |
Es un interpolador cuyo cambio comienza hacia atrás, se desplaza hacia delante y sobrepasa el valor de destino para luego regresar al valor final. |
BounceInterpolator |
Es un interpolador cuyo cambio rebota en el final. |
CycleInterpolator |
Es un interpolador cuya animación se repite durante un número específico de ciclos. |
DecelerateInterpolator |
Es un interpolador cuya velocidad de cambio comienza rápidamente y luego se desacelera. |
LinearInterpolator |
Es un interpolador cuya velocidad de cambio es constante. |
OvershootInterpolator |
Es un interpolador cuyo cambio se desplaza hacia delante, sobrepasa el último valor y, luego, regresa. |
TimeInterpolator |
Es una interfaz que te permite implementar tu propio interpolador. |
Cómo animar valores con ValueAnimator
La clase ValueAnimator
te permite animar valores de algún tipo para la
duración de una animación si especificas un conjunto de valores de int
, float
o color
para animar. Puedes obtener un ValueAnimator
si llamas a uno de sus
métodos de fábrica: ofInt()
, ofFloat()
o ofObject()
. Por ejemplo:
Kotlin
ValueAnimator.ofFloat(0f, 100f).apply { duration = 1000 start() }
Java
ValueAnimator animation = ValueAnimator.ofFloat(0f, 100f); animation.setDuration(1000); animation.start();
En este código, ValueAnimator
empieza a calcular los valores de la
animación, entre 0 y 100, para una duración de 1,000 ms, cuando se ejecuta el método start()
.
También puedes especificar un tipo personalizado para animarlo de la siguiente manera:
Kotlin
ValueAnimator.ofObject(MyTypeEvaluator(), startPropertyValue, endPropertyValue).apply { duration = 1000 start() }
Java
ValueAnimator animation = ValueAnimator.ofObject(new MyTypeEvaluator(), startPropertyValue, endPropertyValue); animation.setDuration(1000); animation.start();
En este código, ValueAnimator
comienza a calcular los valores de la
animación, entre startPropertyValue
y endPropertyValue
, con la
lógica de MyTypeEvaluator
en una duración de 1,000 ms, cuando se ejecuta el método start()
.
Puedes usar los valores de la animación si agregas un
AnimatorUpdateListener
al objeto ValueAnimator
, como se muestra en el
siguiente código:
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); } });
En el método onAnimationUpdate()
,
puedes acceder al valor actualizado de la animación y usarlo en una propiedad
de una de tus vistas. Para obtener más información sobre los objetos de escucha, consulta la sección
Objetos de escucha de la animación.
Cómo animar propiedades con ObjectAnimator
ObjectAnimator
es una subclase de ValueAnimator
(que tratamos en la sección anterior) y combina el motor
de control de tiempo y el cálculo del valor de ValueAnimator
con la capacidad de
animar una propiedad con nombre de un objeto de destino. Esto facilita mucho la animación de cualquier objeto porque
ya no necesitas implementar el ValueAnimator.AnimatorUpdateListener
,
dado que la propiedad animada se actualiza automáticamente.
Crear instancias de un ObjectAnimator
es similar a un ValueAnimator
, pero también especificas el objeto y el nombre de la propiedad de ese objeto (como
una string) junto con los valores entre los que vas a 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 las propiedades de actualización de ObjectAnimator
sean correctas, debes hacer lo siguiente:
- La propiedad del objeto que estás animando debe tener una función de establecedor (en mayúsculas y minúsculas) con el formato
set<PropertyName>()
. Debido a queObjectAnimator
actualiza automáticamente la propiedad durante la animación, debe poder acceder a la propiedad con este método establecedor. Por ejemplo, si el nombre de la propiedad esfoo
, debes tener un métodosetFoo()
. Si este método establecedor no existe, tienes tres opciones:- Agrega el método establecedor a la clase si tienes los derechos para hacerlo.
- Si tiene derechos para cambiar una clase wrapper, úsala y haz que ese wrapper reciba el valor con un método establecedor válido y lo reenvíe al objeto original.
- Usa
ValueAnimator
en su lugar.
- Si especificas solo un valor para el parámetro
values...
en uno de los métodos de fábricaObjectAnimator
, se deduce que ese es el valor del fin de la animación. Por lo tanto, la propiedad del objeto que estás animando debe tener una función de captador, que se usa para obtener el valor de inicio de la animación. La función de captador debe tener el formatoget<PropertyName>()
. Por ejemplo, si el nombre de la propiedad esfoo
, debes tener un métodogetFoo()
. - Los métodos captador (si es necesario) y establecedor de la propiedad que estás animando deben
operar en el mismo tipo que los valores de inicio y fin que especificas en
ObjectAnimator
. Por ejemplo, debes tenertargetObject.setPropName(float)
ytargetObject.getPropName(float)
si construyes el siguienteObjectAnimator
:ObjectAnimator.ofFloat(targetObject, "propName", 1f)
- En función de la propiedad o el objeto que estés animando, tal vez debas llamar al método
invalidate()
en una vista para forzar a la pantalla a que se vuelva a dibujar con los valores animados actualizados. Eso se hace en la devolución de llamadaonAnimationUpdate()
. Por ejemplo, animar la propiedad de color de un objeto de elemento de diseño solo genera actualizaciones en la pantalla cuando ese objeto se vuelve a dibujar a sí mismo. Todos los establecedores de propiedades en la vista, comosetAlpha()
ysetTranslationX()
, invalidan la vista de manera apropiada, por lo que no necesitas invalidarla cuando llamas a estos métodos con valores nuevos. Para obtener más información sobre los objetos de escucha, consulta la sección Objetos de escucha de la animación.
Cómo coreografiar varias animaciones con un AnimatorSet
En muchos casos, querrás reproducir una animación que dependa de cuándo comienza o termina
otra animación. El sistema Android te permite agrupar animaciones en un AnimatorSet
para que puedas definir si deseas iniciar animaciones
de manera simultánea o secuencial, o después de un retraso específico. También puedes anidar objetos AnimatorSet
entre sí.
El fragmento de código a continuación reproduce estos objetos Animator
de la siguiente manera:
- Reproduce
bounceAnim
. - Reproduce
squashAnim1
,squashAnim2
,stretchAnim1
ystretchAnim2
al mismo tiempo. - Reproduce
bounceBackAnim
. - Reproduce
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();
Objetos de escucha de la animación
Puedes escuchar eventos importantes durante el transcurso de una animación con los objetos de escucha que se describen a continuación.
Animator.AnimatorListener
onAnimationStart()
: Se lo llama cuando comienza la animación.onAnimationEnd()
: Se lo llama cuando finaliza la animación.onAnimationRepeat()
: Se lo llama cuando la animación se repite.onAnimationCancel()
: Se lo llama cuando se cancela la animación. Una animación cancelada también llama aonAnimationEnd()
, más allá de cómo finalizó.
ValueAnimator.AnimatorUpdateListener
-
onAnimationUpdate()
: Se lo llama en todos los fotogramas de la animación. Escucha este evento para usar los valores calculados que generóValueAnimator
durante una animación. Para usar el valor, consulta el objetoValueAnimator
que se pasó al evento a fin de conocer el valor animado actual con el métodogetAnimatedValue()
. Es necesario implementar este objeto de escucha si usasValueAnimator
.En función de la propiedad o el objeto que estés animando, tal vez debas llamar a
invalidate()
en una vista para forzar esa área de la pantalla a que se vuelva a dibujar con los nuevos valores animados. Por ejemplo, animar la propiedad de color de un objeto de elemento de diseño solamente genera actualizaciones en la pantalla cuando ese objeto se vuelve a dibujar a sí mismo. Todos los establecedores de propiedades en la vista, comosetAlpha()
ysetTranslationX()
, invalidan la vista de manera apropiada, por lo que no necesitas invalidarla cuando llamas a estos métodos con valores nuevos.
-
Puedes ampliar la clase AnimatorListenerAdapter
en lugar de
implementar la interfaz Animator.AnimatorListener
si no
quieres implementar todos los métodos de la interfaz
Animator.AnimatorListener
. La clase AnimatorListenerAdapter
proporciona implementaciones
vacías de los métodos que puedes elegir anular.
Por ejemplo, el siguiente fragmento de código crea un AnimatorListenerAdapter
para la devolución
de llamada onAnimationEnd()
solamente:
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()); }
Cómo animar cambios de diseño en objetos ViewGroup
El sistema de animación de propiedades ofrece la posibilidad de animar los cambios en los objetos ViewGroup y un método sencillo para animar los objetos de vista en sí.
Puedes animar los cambios de diseño dentro de un ViewGroup con la
clase LayoutTransition
. Las vistas dentro de un ViewGroup pueden
pasar por una animación que aparece y desaparece cuando las agregas
a un ViewGroup o las quitas de él, o cuando llamas al método
setVisibility()
de una vista con
VISIBLE
, INVISIBLE
o
GONE
. Las vistas restantes del ViewGroup también pueden
animarse en sus nuevas posiciones cuando agregas o quitas vistas. Puedes definir
las siguientes animaciones en un objeto LayoutTransition
si llamas a setAnimator()
y pasas un objeto Animator
con una de las
siguientes constantes LayoutTransition
:
APPEARING
: Es un indicador de la animación que se ejecuta en elementos que están apareciendo en el container.CHANGE_APPEARING
: Es un indicador de la animación que se ejecuta en elementos que están cambiando debido a la aparición de un elemento nuevo en el container.DISAPPEARING
: Es un indicador de la animación que se ejecuta en elementos que están desapareciendo del container.CHANGE_DISAPPEARING
: Es un indicador de la animación que se ejecuta en elementos que están cambiando debido a la desaparición de un elemento del container.
Puedes definir tus propias animaciones personalizadas para estos cuatro tipos de eventos a fin de personalizar el aspecto de tus transiciones de diseño o simplemente indicarle al sistema de animación que use las animaciones predeterminadas.
En el ejemplo de LayoutAnimations de API Demos, se muestra cómo definir animaciones para transiciones de diseños y luego establecer las animaciones en los objetos de vista que quieres animar.
LayoutAnimationsByDefault y el archivo de recursos de diseño correspondiente layout_animations_by_default.xml
te muestran cómo habilitar las transiciones predeterminadas de diseños para ViewGroups en XML.
Lo único que debes hacer es establecer el atributo android:animateLayoutchanges
en true
para el ViewGroup. Por ejemplo:
<LinearLayout android:orientation="vertical" android:layout_width="wrap_content" android:layout_height="match_parent" android:id="@+id/verticalContainer" android:animateLayoutChanges="true" />
Si configuras este atributo como verdadero, se animarán automáticamente las vistas que agregaste al ViewGroup o que quitaste de allí, además de las vistas restantes dentro del ViewGroup.
Cómo animar los cambios de estado de las vistas con StateListAnimator
La clase StateListAnimator
te permite definir animadores que se ejecutan cuando cambia
el estado de una vista. Este objeto se comporta como un wrapper para un
objeto Animator
y llama a esa animación cada vez que cambia el estado de la vista
especificado (como "presionado" o "enfocado").
El StateListAnimator
se puede definir en un recurso XML con un elemento raíz
<selector>
y elementos secundarios <item>
que especifiquen
un estado de vista diferente definido por la clase . Cada
<item>
contiene la definición de un conjunto de animación de propiedades.
Por ejemplo, el siguiente archivo crea un animador de listas de estados que cambia las escalas Y y X de la vista cuando se presiona:
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 adjuntar el animador de listas de estados a una vista, agrega el atributo
android:stateListAnimator
de la siguiente manera:
<Button android:stateListAnimator="@xml/animate_scale" ... />
Ahora, se usan las animaciones definidas en animate_scale.xml
cuando cambia el estado de
este botón.
Como alternativa, para asignar un animador de listas de estados a una vista en tu código, usa el método
AnimatorInflater.loadStateListAnimator()
y asigna el animador a
tu vista con el método View.setStateListAnimator()
.
O bien, en lugar de animar las propiedades de la vista, puedes reproducir una animación de un elemento de diseño entre
cambios de estado con AnimatedStateListDrawable
.
Algunos de los widgets del sistema de
Android 5.0 usan estas animaciones de forma predeterminada. En el siguiente ejemplo, se muestra cómo
definir un 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>
Cómo usar un TypeEvaluator
Si deseas animar un tipo desconocido para el sistema Android, puedes crear tu propio
evaluador si implementas la interfaz TypeEvaluator
. Los tipos que
conoce el sistema Android son int
, float
o un color, que
son compatibles con los evaluadores de tipos IntEvaluator
, FloatEvaluator
y
ArgbEvaluator
.
Solo hay un método para implementar en la interfaz
TypeEvaluator
: evaluate()
. De esta manera,
el animador que estás usando puede mostrar un valor apropiado para la propiedad animada en el
punto actual de la animación. La clase FloatEvaluator
demuestra
el proceso:
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); } }
Nota: Cuando se ejecuta ValueAnimator
(o ObjectAnimator
), calcula una fracción transcurrida actual de la animación (un valor entre 0 y 1) y luego calcula una versión interpolada correspondiente en función del interpolador que estás usando. La fracción interpolada es lo que recibe tu TypeEvaluator
a través del parámetro fraction
, por lo que no
tienes que tener en cuenta el interpolador cuando calculas los valores animados.
Cómo usar interpoladores
Un interpolador define cómo se calculan los valores específicos de una animación en función del tiempo. Por ejemplo, puedes especificar que las animaciones se reproduzcan de modo lineal durante todo el transcurso, lo que significa que la animación se mueve de manera uniforme todo el tiempo, o puedes especificar que sigan un tiempo no lineal; por ejemplo, si usas aceleración o desaceleración al principio o al final de la animación.
Los interpoladores del sistema de animación reciben una fracción de los animadores que representa el
tiempo transcurrido de la animación. Los interpoladores modifican esta fracción para que coincida con el tipo de
animación que pretende proporcionar. El sistema Android brinda un conjunto de interpoladores comunes en
android.view.animation package
. Si ninguno se adapta a tus
necesidades, puedes implementar la interfaz TimeInterpolator
y crear
uno propio.
A continuación, se muestra un ejemplo comparativo de cómo el interpolador predeterminado AccelerateDecelerateInterpolator
y el LinearInterpolator
calculan las fracciones interpoladas.
LinearInterpolator
no tiene efecto en la fracción transcurrida. AccelerateDecelerateInterpolator
inicia la animación con una aceleración y la finaliza
con una desaceleración. Los siguientes métodos definen la lógica para estos 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; }
La siguiente tabla representa los valores aproximados que calculan estos interpoladores para una animación que dura 1,000 ms:
ms transcurridos | Fracción transcurrida/fracción interpolada (lineal) | Fracción interpolada (aceleración/desaceleración) |
---|---|---|
0 | 0 | 0 |
200 | 0.2 | 0.1 |
400 | 0.4 | 0.345 |
600 | 0.6 | 0.8 |
800 | 0.8 | 0.9 |
1,000 | 1 | 1 |
Como muestra la tabla, LinearInterpolator
cambia los valores
a la misma velocidad, 0.2 por cada 200 ms que transcurren. AccelerateDecelerateInterpolator
cambia los valores más rápido que LinearInterpolator
entre 200 ms y 600 ms, y más lento entre 600 ms y
1,000 ms.
Cómo especificar fotogramas clave
Un objeto Keyframe
consta de un par tiempo/valor que te permite definir
un estado específico en un tiempo determinado de una animación. Cada fotograma clave también puede tener su propio
interpolador para controlar el comportamiento de la animación en el intervalo entre el tiempo
del fotograma clave anterior y el tiempo del fotograma clave actual.
Para crear una instancia de un objeto Keyframe
, debes usar uno de los métodos
de fábrica, ofInt()
, ofFloat()
, o ofObject()
, para obtener el tipo de apropiado. Luego, llamas al
método de fábrica ofKeyframe()
para
obtener un objeto PropertyValuesHolder
. Cuando tengas el objeto, podrás
obtener un animador si pasas el objeto PropertyValuesHolder
y
el objeto que se va a animar. En el siguiente fragmento de código, se muestra el proceso:
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);
Cómo animar vistas
El sistema de animación de propiedades permite realizar una animación optimizada de los objetos de vista y ofrece algunas ventajas en comparación con el sistema de animación de vistas. El sistema de animación de vistas transformó los objetos de vista, ya que cambió la forma en que se dibujaban. Se manejó en el container de cada vista, porque la vista no tenía propiedades para manipular. Como resultado, la vista se animó, pero no se produjeron cambios en el objeto de vista. Esto generó comportamientos tales como el de un objeto que seguía presente en su ubicación original a pesar de que se dibujó en un área distinta de la pantalla. En Android 3.0, se agregaron nuevas propiedades y los métodos captador y establecedor correspondientes para eliminar este problema.
El sistema de animación de propiedades
puede animar vistas en la pantalla cambiando las propiedades reales en los objetos de vista. Además,
las vistas llaman automáticamente al método invalidate()
para actualizar la pantalla cada vez que se cambian sus propiedades. Las nuevas propiedades de la clase View
que facilitan las animaciones de propiedades son las siguientes:
translationX
ytranslationY
: Estas propiedades controlan la ubicación de la vista como una delta a partir de sus coordenadas izquierda y superior, que establece su container de diseño.rotation
,rotationX
yrotationY
: Estas propiedades controlan la rotación en 2D (propiedad ) y en 3D alrededor del punto de pivote.scaleX
yscaleY
: Estas propiedades controlan el ajuste en 2D de una vista alrededor de su punto de pivote.pivotX
ypivotY
: Estas propiedades controlan la ubicación del punto de pivote, alrededor del cual se producen las transformaciones de rotación y ajuste. De manera predeterminada, el punto de pivote se encuentra en el centro del objeto.x
yy
: Estas son propiedades de utilidad simples para describir la ubicación final de la vista en su contenedor, como una suma de los valores izquierdo y superior, y los valores de translationX y translationY.alpha
: Representa el valor de transparencia alfa en la vista. Este valor es 1 (opaca) de forma predeterminada, y el valor 0 representa la transparencia total (no visible).
Para animar una propiedad de un objeto de vista, como su valor de color o rotación, solo tienes que crear un animador de propiedades y especificar la propiedad de vista que quieres animar. Por ejemplo:
Kotlin
ObjectAnimator.ofFloat(myView, "rotation", 0f, 360f)
Java
ObjectAnimator.ofFloat(myView, "rotation", 0f, 360f);
Para obtener más información sobre cómo crear animadores, consulta las secciones sobre cómo animar con ValueAnimator y ObjectAnimator.
Cómo animar propiedades con ViewPropertyAnimator
ViewPropertyAnimator
brinda un método sencillo de animar varias
propiedades de una View
en paralelo usando un solo objeto Animator
subyacente. Se comporta casi igual que ObjectAnimator
porque modifica los
valores reales de las propiedades de la vista, pero es más eficiente cuando anima muchas propiedades a la
vez. Además, el código para usar ViewPropertyAnimator
es mucho
más conciso y fácil de leer. En los siguientes fragmentos de código, se muestran las diferencias entre usar varios objetos
ObjectAnimator
, un solo
ObjectAnimator
y el ViewPropertyAnimator
cuando
se animan al mismo tiempo las propiedades y
y x
de una vista.
Varios objetos de 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();
Un 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);
Si quieres obtener información más detallada sobre ViewPropertyAnimator
, consulta la
entrada
correspondiente del blog para desarrolladores de Android.
Cómo declarar animaciones en XML
El sistema de animación de propiedades te permite declarar animaciones de propiedades con XML en lugar de hacerlo de manera programática. Puedes definir tus animaciones en XML para reutilizarlas en varias actividades y editar la secuencia de animación con más facilidad.
Para distinguir los archivos de animación que usan las nuevas API de animación de propiedades de aquellos que utilizan el
marco de trabajo heredado de animación de vista,
a partir de Android 3.1, debes guardar los archivos XML de las animaciones de propiedades en el directorio res/animator/
.
Las siguientes clases de animación de propiedades admiten la declaración en XML mediante estas etiquetas XML:
ValueAnimator
-<animator>
ObjectAnimator
-<objectAnimator>
AnimatorSet
-<set>
Para encontrar los atributos que puedes usar en tu declaración en XML, consulta Recursos de animación. En el siguiente ejemplo, se reproducen los dos conjuntos de animaciones de objetos de forma secuencial; el primer conjunto anidado reproduce dos animaciones de objetos 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 ejecutar esta animación, debes ampliar los recursos XML en tu código a un objeto AnimatorSet
y luego establecer los objetos de destino para todas las animaciones
antes de iniciar el conjunto. Cuando llamas a setTarget()
, se establece un único objeto de destino para todos los elementos secundarios del AnimatorSet
, lo cual es más conveniente. En el siguiente código, se muestra el proceso:
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();
También puedes declarar un ValueAnimator
en XML, como
se detalla en el siguiente ejemplo:
<animator xmlns:android="http://schemas.android.com/apk/res/android" android:duration="1000" android:valueType="floatType" android:valueFrom="0f" android:valueTo="-100f" />
Para usar el ValueAnimator
anterior en tu código, debes
ampliar el objeto, agregar un
AnimatorUpdateListener
,
obtener el valor de animación actualizado y usarlo en una propiedad de una de tus vistas,
como se muestra en el siguiente código:
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();
Si deseas obtener información sobre la sintaxis XML para definir animaciones de propiedades, consulta Recursos de animación.
Posibles efectos en el rendimiento de la IU
Los animadores que actualizan la IU aumentan la labor de procesamiento para cada fotograma en el que se ejecuta la animación. Por esta razón, el uso de animaciones que consumen muchos recursos puede perjudicar el rendimiento de tu app.
El trabajo requerido para animar tu IU se agrega a la etapa de animación de la canalización de procesamiento. Para saber si tus animaciones afectan el rendimiento de la app, habilita Represent. GPU del perfil y supervisa la etapa de animación. Para obtener más información, consulta la explicación sobre Representación GPU del perfil.