El movimiento basado en la física está impulsado por la fuerza. Un ejemplo es la fuerza de resorte, que guía la interacción y el movimiento. Una fuerza de resorte tiene las siguientes propiedades: amortiguamiento y rigidez. En una animación basada en resortes, el valor y la velocidad se calculan en función de la fuerza de resorte que se aplica en cada fotograma.
Si deseas que las animaciones de tu app se ralenticen en una sola dirección, procura usar una animación de desplazamiento basada en la fricción.
Ciclo de vida de una animación de resorte
En una animación basada en resortes, la clase SpringForce
te permite personalizar la rigidez del resorte, su proporción de amortiguamiento y
su posición final. Apenas comienza la animación, la fuerza de resorte actualiza
el valor de la animación y la velocidad en cada fotograma. La animación continúa
hasta que la fuerza de resorte alcanza un equilibrio.
Por ejemplo, si arrastras el ícono de una app por la pantalla y lo sueltas levantando el dedo del ícono, este volverá de un tirón a su sitio original mediante una fuerza invisible pero conocida.
En la figura 1, se muestra un efecto de resorte similar. El signo más (+) en el medio del círculo indica la fuerza aplicada mediante un gesto táctil.

Cómo crear una animación de resorte
Los pasos generales en el momento de crear una animación de resorte para tu aplicación son los siguientes:
- Agrega la biblioteca de compatibilidad: Debes agregar la biblioteca de compatibilidad al proyecto para usar las clases de animación de resorte.
- Crea una animación de resorte:
El paso principal es crear una instancia de la clase
SpringAnimation
y establecer los parámetros de comportamiento de movimiento. - (Opcional) Registra objetos de escucha:
Registra objetos de escucha para detectar cambios en el ciclo de vida y actualizaciones de valores
de la animación.
Nota: El objeto de escucha de actualizaciones debe registrarse solo si necesitas una actualización por fotograma sobre los cambios de valores en la animación. Un detector de escucha de actualizaciones elimina la posibilidad de que la animación se ejecute en un subproceso separado.
- (Opcional) Quita los objetos de escucha: Quita los objetos de escucha que no se usen.
- (Opcional) Establece un valor de inicio: Personaliza el valor de inicio de la animación.
- (Opcional) Establece un rango de valores: Establece el rango mínimo y máximo de valores de la animación.
- (Opcional) Establece la velocidad de inicio: Establece la velocidad de inicio de la animación.
- Opcional) Establece las propiedades del resorte: Establece la proporción de amortiguamiento y la rigidez del resorte.
- (Opcional) Crea un resorte personalizado: Crea un resorte personalizado en caso de que no planees usar el predeterminado o de que quieras usar un resorte común durante el transcurso de la animación.
- Inicia la animación: Inicia la animación de resorte.
- (Opcional) Cancela la animación: Cancela la animación en caso de que el usuario cierre de manera repentina la app o de que la vista se vuelva invisible.
En las siguientes secciones, se tratan en detalle los pasos generales para compilar una animación de resorte.
Cómo agregar la biblioteca de compatibilidad
Para usar la biblioteca de compatibilidad basada en la física, debes agregar la biblioteca a tu proyecto de la siguiente manera:
- Abre el archivo
build.gradle
correspondiente al módulo de tu app. Agrega la biblioteca de compatibilidad a la sección
dependencies
.dependencies { def dynamicanimation_version = "1.0.0" implementation 'androidx.dynamicanimation:dynamicanimation:$dynamicanimation_version' }
Para ver las versiones actuales de esta biblioteca, consulta la información sobre Dynamicanimation en la página de versiones.
Cómo crear una animación de resorte
La clase SpringAnimation
te permite crear
una animación de resorte para un objeto. Para compilar una animación de resorte, necesitas
crear una instancia de la clase
SpringAnimation
y proporcionar un objeto, la propiedad correspondiente que deseas animar y una
posición final opcional del resorte donde quieres que repose la animación.
Nota: En el momento de crear una animación de resorte, la posición final de este es opcional. Sin embargo, debe definirse antes de iniciar la animación.
Kotlin
val springAnim = findViewById<View>(R.id.imageView).let { img -> // Setting up a spring animation to animate the view’s translationY property with the final // spring position at 0. SpringAnimation(img, DynamicAnimation.TRANSLATION_Y, 0f) }
Java
final View img = findViewById(R.id.imageView); // Setting up a spring animation to animate the view’s translationY property with the final // spring position at 0. final SpringAnimation springAnim = new SpringAnimation(img, DynamicAnimation.TRANSLATION_Y, 0);
La animación basada en resortes puede animar vistas en la pantalla cambiando las propiedades en los objetos de vista. Las siguientes vistas están disponibles en el sistema:
ALPHA
: Representa la transparencia alfa de la vista. El valor es 1 (opaca) de forma predeterminada, y el valor 0 representa la transparencia total (no visible).TRANSLATION_X
,TRANSLATION_Y
yTRANSLATION_Z
: Estas propiedades controlan la ubicación de la vista como una delta a partir de sus coordenadas izquierda y superior y de la elevación, que establece su container de diseño.TRANSLATION_X
describe la coordenada izquierda.TRANSLATION_Y
describe la coordenada superior.TRANSLATION_Z
describe la profundidad de la vista en relación con su elevación.
ROTATION
,ROTATION_X
yROTATION_Y
: Estas propiedades controlan la rotación en 2D (propiedadrotation
) y en 3D alrededor del punto de pivote.SCROLL_X
ySCROLL_Y
: Estas propiedades indican el desplazamiento de los bordes izquierdo y superior de origen en píxeles. También indican la posición en términos de cuánto se desplaza la página.SCALE_X
ySCALE_Y
: Estas propiedades controlan el ajuste en 2D de una vista alrededor de su punto de pivote.X
,Y
yZ
: Estas son propiedades de utilidad básicas para describir la ubicación final de la vista en su container.X
es una suma del valor izquierdo y deTRANSLATION_X
.Y
es una suma del valor superior y deTRANSLATION_Y
.Z
es una suma del valor de elevación y deTRANSLATION_Z
.
Cómo registrar objetos de escucha
La clase DynamicAnimation
proporciona dos
objetos de escucha: OnAnimationUpdateListener
y OnAnimationEndListener
.
Estos escuchan las actualizaciones en la animación, como cuando hay un
cambio en el valor de la animación y cuando llega a su fin.
OnAnimationUpdateListener
Si deseas animar varias vistas para crear una animación encadenada, puedes
configurar OnAnimationUpdateListener
para recibir una devolución de llamada cada vez que haya un cambio en la propiedad actual
de la vista. La devolución de llamada comunica a la otra vista que actualice su posición del resorte
en función del cambio generado en la propiedad de la vista actual. Para registrar el
objeto de escucha, sigue estos pasos:
-
Llama al método
addUpdateListener()
y adjunta el objeto de escucha a la animación.Nota: Debes registrar el objeto de escucha de actualizaciones antes de que comience la animación. Sin embargo, el objeto de escucha de actualizaciones debe registrarse solo si necesitas una actualización por fotograma sobre los cambios de valores en la animación. Un detector de escucha de actualizaciones elimina la posibilidad de que la animación se ejecute en un subproceso separado.
-
Anula el método
onAnimationUpdate()
para comunicarle al emisor el cambio en el objeto actual. En el siguiente código de ejemplo, se muestra el uso general deOnAnimationUpdateListener
.
Kotlin
// Setting up a spring animation to animate the view1 and view2 translationX and translationY properties val (anim1X, anim1Y) = findViewById<View>(R.id.view1).let { view1 -> SpringAnimation(view1, DynamicAnimation.TRANSLATION_X) to SpringAnimation(view1, DynamicAnimation.TRANSLATION_Y) } val (anim2X, anim2Y) = findViewById<View>(R.id.view2).let { view2 -> SpringAnimation(view2, DynamicAnimation.TRANSLATION_X) to SpringAnimation(view2, DynamicAnimation.TRANSLATION_Y) } // Registering the update listener anim1X.addUpdateListener { _, value, _ -> // Overriding the method to notify view2 about the change in the view1’s property. anim2X.animateToFinalPosition(value) } anim1Y.addUpdateListener { _, value, _ -> anim2Y.animateToFinalPosition(value) }
Java
// Creating two views to demonstrate the registration of the update listener. final View view1 = findViewById(R.id.view1); final View view2 = findViewById(R.id.view2); // Setting up a spring animation to animate the view1 and view2 translationX and translationY properties final SpringAnimation anim1X = new SpringAnimation(view1, DynamicAnimation.TRANSLATION_X); final SpringAnimation anim1Y = new SpringAnimation(view1, DynamicAnimation.TRANSLATION_Y); final SpringAnimation anim2X = new SpringAnimation(view2, DynamicAnimation.TRANSLATION_X); final SpringAnimation anim2Y = new SpringAnimation(view2, DynamicAnimation.TRANSLATION_Y); // Registering the update listener anim1X.addUpdateListener(new DynamicAnimation.OnAnimationUpdateListener() { // Overriding the method to notify view2 about the change in the view1’s property. @Override public void onAnimationUpdate(DynamicAnimation dynamicAnimation, float value, float velocity) { anim2X.animateToFinalPosition(value); } }); anim1Y.addUpdateListener(new DynamicAnimation.OnAnimationUpdateListener() { @Override public void onAnimationUpdate(DynamicAnimation dynamicAnimation, float value, float velocity) { anim2Y.animateToFinalPosition(value); } });
OnAnimationEndListener
OnAnimationEndListener
comunica el final de una animación. Puedes configurar el objeto de escucha para que reciba
una devolución de llamada cada vez que la animación alcance el equilibrio o se cancele. Para
registrar el objeto de escucha, sigue estos pasos:
-
Llama al método
addEndListener()
y adjunta el objeto de escucha a la animación. -
Anula el método
onAnimationEnd()
para recibir una notificación cada vez que una animación alcance el equilibrio o se cancele.
Cómo quitar objetos de escucha
Para dejar de recibir devoluciones de llamada cuando la animación se actualice o finalice,
llama a los métodos
removeUpdateListener()
y
removeEndListener()
, respectivamente.
Cómo establecer el valor de inicio de la animación
Para establecer el valor de inicio de la animación, llama al método
setStartValue()
y pasa el valor de inicio de la animación. Si no estableces el
valor de inicio, la animación usará el valor actual de la propiedad del objeto
como valor de inicio.
Cómo establecer un rango de valores de la animación
Puedes establecer los valores de animación mínimos y máximos cuando quieras restringir el valor de la propiedad a un rango determinado. Esto también ayuda a controlar el rango en caso de que animes propiedades que tienen un rango intrínseco, como alfa (de 0 a 1).
-
Para establecer el valor mínimo, llama al método
setMinValue()
y pasa el valor mínimo de la propiedad. -
Para establecer el valor máximo, llama al método
setMaxValue()
y pasa el valor máximo de la propiedad.
Ambos métodos muestran la animación para la que se establece el valor.
Nota: Si estableciste el valor de inicio y definiste un rango de valores de la animación, asegúrate de que el valor de inicio esté dentro del rango de valores mínimo y máximo.
Cómo establecer la velocidad de inicio
La velocidad de inicio define la velocidad a la que cambia la propiedad de la animación durante su comienzo. La velocidad de inicio predeterminada se establece en cero píxeles por segundo. Puedes establecerla con la velocidad de los gestos táctiles o usando un valor fijo como velocidad de inicio. Si eliges ingresar un valor fijo, recomendamos definirlo en dp por segundo y luego convertirlo a píxeles por segundo. Definir el valor en dp por segundo permite que la velocidad sea independiente de la densidad y de los factores de forma. Para obtener más información sobre la conversión de valores a píxeles por segundo, consulta la sección Cómo convertir dp por segundo a píxeles por segundo.
Para establecer la velocidad, llama al
método
setStartVelocity()
y pasa la velocidad en píxeles por segundo El método muestra el
objeto de fuerza de resorte en el cual se establece la velocidad.
Nota: Usa los métodos de clase GestureDetector.OnGestureListener
o VelocityTracker
para recuperar y calcular la velocidad de los gestos táctiles.
Kotlin
findViewById<View>(R.id.imageView).also { img -> SpringAnimation(img, DynamicAnimation.TRANSLATION_Y).apply { … // Compute velocity in the unit pixel/second vt.computeCurrentVelocity(1000) val velocity = vt.yVelocity setStartVelocity(velocity) } }
Java
final View img = findViewById(R.id.imageView); final SpringAnimation anim = new SpringAnimation(img, DynamicAnimation.TRANSLATION_Y); … // Compute velocity in the unit pixel/second vt.computeCurrentVelocity(1000); float velocity = vt.getYVelocity(); anim.setStartVelocity(velocity);
Cómo convertir dp por segundo a píxeles por segundo
La velocidad de un resorte debe expresarse en píxeles por segundo. Si eliges ingresar un
valor fijo como el inicio de la velocidad, proporciónalo en dp por segundo
y luego conviértelo a píxeles por segundo. Para la conversión, usa el
método
applyDimension()
de la clase TypedValue
. Consulta el
siguiente código de ejemplo:
Kotlin
val pixelPerSecond: Float = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpPerSecond, resources.displayMetrics)
Java
float pixelPerSecond = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpPerSecond, getResources().getDisplayMetrics());
Cómo establecer las propiedades del resorte
La clase SpringForce
define los métodos
de captador y establecedor para cada una de las propiedades del resorte, como la proporción de
amortiguamiento y la rigidez. Para establecer las propiedades del resorte, es importante
recuperar el objeto de fuerza de resorte o crear una fuerza de resorte personalizada en la que
puedas establecer las propiedades. Para obtener más información sobre cómo crear una fuerza
de resorte personalizada, consulta la sección
Cómo crear una fuerza de resorte
personalizada.
Sugerencia: Mientras usas los métodos de establecedor, puedes crear una cadena de métodos, ya que todos ellos muestran el objeto de fuerza de resorte.
Proporción de amortiguamiento
La proporción de amortiguamiento describe una reducción gradual en la oscilación de un resorte. Si usas la proporción de amortiguamiento, puedes definir qué tan rápido decaen las oscilaciones de un rebote al siguiente. Hay cuatro formas diferentes de amortiguar un resorte:
- El sobreamortiguamiento se produce cuando la proporción de amortiguamiento es mayor que uno. Permite que el objeto vuelva rápidamente a la posición de reposo.
- El amortiguamiento crítico se produce cuando la proporción de amortiguamiento es igual a uno. Permite que el objeto regrese a la posición de reposo en el menor tiempo posible.
- El subamortiguamiento se produce cuando la proporción de amortiguamiento es menor que uno. Permite que el objeto se sobrepase varias veces al pasar la posición de reposo y luego alcance de forma gradual la posición de reposo.
- El no amortiguamiento se produce cuando la proporción de amortiguamiento es igual a cero. Permite que el objeto oscile de manera permanente.
Para agregar la proporción de amortiguamiento al resorte, sigue estos pasos:
-
Llama al método
getSpring()
para recuperar el resorte al que le vas a agregar la proporción de amortiguamiento. -
Llama al método
setDampingRatio()
y pasa la proporción de amortiguamiento que quieres agregar al resorte. El método muestra el objeto de fuerza de resorte en el que se estableció la proporción de amortiguamiento.Nota: La proporción de amortiguamiento debe ser unnúmero no negativo. Si la estableces en cero, el resorte nunca alcanzará la posición de reposo. En otras palabras, oscilará de manera permanente.
Las siguientes constantes de proporción de amortiguamiento están disponibles en el sistema:
DAMPING_RATIO_HIGH_BOUNCY
DAMPING_RATIO_MEDIUM_BOUNCY
DAMPING_RATIO_LOW_BOUNCY
DAMPING_RATIO_NO_BOUNCY
Figura 2: Rebote alto
Figura 3: Rebote medio
Figura 4: Rebote bajo
Figura 5: Sin rebote
La proporción de amortiguamiento predeterminada se establece en DAMPING_RATIO_MEDIUM_BOUNCY
.
Kotlin
findViewById<View>(R.id.imageView).also { img -> SpringAnimation(img, DynamicAnimation.TRANSLATION_Y).apply { … //Setting the damping ratio to create a low bouncing effect. spring.dampingRatio = SpringForce.DAMPING_RATIO_LOW_BOUNCY … } }
Java
final View img = findViewById(R.id.imageView); final SpringAnimation anim = new SpringAnimation(img, DynamicAnimation.TRANSLATION_Y); … //Setting the damping ratio to create a low bouncing effect. anim.getSpring().setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY); …
Rigidez
La rigidez define la constante del resorte, la cual mide su resistencia. Un resorte rígido aplica más fuerza al objeto adjuntado cuando el resorte no está en la posición de reposo. Para agregar la rigidez al resorte, sigue estos pasos:
-
Llama al método
getSpring()
para recuperar el resorte al que le agregarás la rigidez. -
Llama al método
setStiffness()
y pasa el valor de rigidez que quieres agregar al resorte. El método muestra el objeto de fuerza de resorte en el que se estableció la rigidez.Nota: La rigidez debe ser un número positivo.
Las siguientes constantes de rigidez están disponibles en el sistema:
Figura 6: Rigidez alta
Figura 7: Rigidez media
Figura 8: Rigidez baja
Figura 9: Rigidez muy baja
La rigidez predeterminada se establece en STIFFNESS_MEDIUM
.
Kotlin
findViewById<View>(R.id.imageView).also { img -> SpringAnimation(img, DynamicAnimation.TRANSLATION_Y).apply { … //Setting the spring with a low stiffness. spring.stiffness = SpringForce.STIFFNESS_LOW … } }
Java
final View img = findViewById(R.id.imageView); final SpringAnimation anim = new SpringAnimation(img, DynamicAnimation.TRANSLATION_Y); … //Setting the spring with a low stiffness. anim.getSpring().setStiffness(SpringForce.STIFFNESS_LOW); …
Cómo crear una fuerza de resorte personalizada
Puedes crear una fuerza de resorte personalizada como alternativa a usar la fuerza de resorte predeterminada. La fuerza de resorte personalizada te permite compartir la misma instancia de fuerza de resorte en varias animaciones de resorte. Una vez que hayas creado la fuerza de resorte, puedes establecer propiedades como la proporción de amortiguamiento y la rigidez.
-
Crea un objeto
SpringForce
.SpringForce force = new SpringForce();
-
Llama a los métodos correspondientes para asignar las propiedades. También puedes
crear una cadena de métodos.
force.setDampingRatio(DAMPING_RATIO_LOW_BOUNCY).setStiffness(STIFFNESS_LOW);
-
Llama al método
setSpring()
para establecer el resorte en la animación.setSpring(force);
Cómo iniciar la animación
Hay dos formas de iniciar una animación de resorte: llamar al
método start()
o llamar
al método
animateToFinalPosition()
. Ambos métodos deben ser llamados en el subproceso principal.
El método
animateToFinalPosition()
realiza dos tareas:
- Establece la posición final del resorte.
- Inicia la animación si no ha comenzado.
Dado que el método actualiza la posición final del resorte y luego inicia la
animación si es necesario, puedes llamarlo en cualquier momento para cambiar el curso
de una animación. Por ejemplo, en una animación de resorte encadenada, la animación
de una vista depende de otra vista. Para tal animación, es más
conveniente usar el
método
animateToFinalPosition()
. Si usas este método en una animación de resorte encadenada, no necesitas
preocuparte de que la animación que deseas actualizar a continuación se esté ejecutando en ese momento.
En la figura 10, se muestra una animación de resorte encadenada, donde la animación de una vista depende de otra vista.

Para usar el método
animateToFinalPosition()
, llama al
método
animateToFinalPosition()
y pasa la posición de reposo del resorte. También puedes establecer la posición
de reposo del resorte si llamas al
método
setFinalPosition()
.
El método start()
no
establece el valor de la propiedad en el valor de inicio de manera inmediata. El valor
de la propiedad cambia con cada pulso de la animación, que se produce antes del pase de dibujo.
Por lo tanto, los cambios se reflejan en el siguiente fotograma, como si
los valores se establecieran de inmediato.
Kotlin
findViewById<View>(R.id.imageView).also { img -> SpringAnimation(img, DynamicAnimation.TRANSLATION_Y).apply { … //Starting the animation start() … } }
Java
final View img = findViewById(R.id.imageView); final SpringAnimation anim = new SpringAnimation(img, DynamicAnimation.TRANSLATION_Y); … //Starting the animation anim.start(); …
Cómo cancelar la animación
Puedes cancelar la animación o puedes omitirla hasta el final. Una situación ideal en la que necesitas cancelar la animación u omitirla hasta el final es cuando la interacción de un usuario requiere que la animación finalice de inmediato. Esto ocurre más que nada cuando un usuario sale de una app de manera repentina o la vista se vuelve invisible.
Existen dos métodos que puedes usar para finalizar la animación.
El método cancel()
finaliza la animación en el valor en el que se encuentra. El
método skipToEnd()
omite la animación hasta el valor final y luego la finaliza.
Antes de que puedas finalizar la animación, es importante verificar primero el
estado del resorte. Si el estado no está amortiguado, la animación nunca podrá alcanzar
la posición de reposo.
Para verificar el estado del resorte, llama al
método canSkipToEnd()
. Si
el resorte está amortiguado, el método muestra true
; de lo contrario,
false
.
Una vez que conozcas el estado del resorte, podrás finalizar una animación
mediante los métodos
skipToEnd()
o
cancel()
. Debes
llamar al método
cancel()
solo en el subproceso principal.
Nota: En general, el método skipToEnd()
provoca un salto visual.