Animatable
public class Animatable<T, V : AnimationVector>
@RememberInComposition
constructor(
initialValue: T,
public val typeConverter: TwoWayConverter<T, V>,
private val visibilityThreshold: T? = null,
public val label: String = "Animatable",
)
Animatable
is a value holder that automatically animates its value when the value is changed
via animateTo
. If animateTo
is invoked during an ongoing value change animation, a new
animation will transition Animatable
from its current value (i.e. value at the point of
interruption) to the new targetValue
. This ensures that the value change is always
continuous using animateTo
. If a spring
animation (e.g. default animation) is used with
animateTo
, the velocity change will guarantee to be continuous as well.
Unlike AnimationState
, Animatable
ensures mutual exclusiveness on its animations. To
achieve this, when a new animation is started via animateTo
(or animateDecay
), any ongoing
animation will be canceled via a CancellationException
.
Parameters
initialValue | initial value of the animatable value holder |
typeConverter | A two-way converter that converts the given type T from and to AnimationVector |
visibilityThreshold | Threshold at which the animation may round off to its target value. |
label | An optional label for differentiating this animation from others in android studio. |
Secondary Constructors
public constructor(
initialValue: T,
typeConverter: TwoWayConverter<T, V>,
visibilityThreshold: T? = null,
) : this(initialValue, typeConverter, visibilityThreshold, "Animatable")
Functions
public fun updateBounds(lowerBound: T? = this.lowerBound, upperBound: T? = this.upperBound)
Updates either lowerBound
or upperBound
, or both. This will update
Animatable.lowerBound
and/or Animatable.upperBound
accordingly after a check to ensure
the provided lowerBound
is no greater than upperBound
in any dimension.
Setting the bounds will immediate clamp the value
, only if the animation isn't running. For
the on-going animation, the value at the next frame update will be checked against the
bounds. If the value reaches the bound, then the animation will end with BoundReached
end
reason.
Parameters
lowerBound | lower bound of the animation. Defaults to the Animatable.lowerBound that is currently set. |
upperBound | upper bound of the animation. Defaults to the Animatable.upperBound that is currently set. |
public suspend fun animateTo(
targetValue: T,
animationSpec: AnimationSpec<T> = defaultSpringSpec,
initialVelocity: T = velocity,
block: (Animatable<T, V>.() -> Unit)? = null,
): AnimationResult<T, V>
Starts an animation to animate from value
to the provided targetValue
. If there is
already an animation in-flight, this method will cancel the ongoing animation before starting
a new animation continuing the current value
and velocity
. It's recommended to set the
optional initialVelocity
only when animateTo
is used immediately after a fling. In most
of the other cases, altering velocity would result in visual discontinuity.
The animation will use the provided animationSpec
to animate the value towards the
targetValue
. When no animationSpec
is specified, a spring
will be used. block
will be
invoked on each animation frame.
Returns an AnimationResult
object. It contains: 1) the reason for ending the animation,
and 2) an end state of the animation. The reason for ending the animation can be either of
the following two:
Finished
, when the animation finishes successfully without any interruption,BoundReached
If the animation reaches the eitherlowerBound
orupperBound
in any dimension, the animation will end withBoundReached
being the end reason.
If the animation gets interrupted by 1) another call to start an animation (i.e.
animateTo
/animateDecay
), 2) Animatable.stop
, or 3)Animatable.snapTo
, the canceled
animation will throw a CancellationException
as the job gets canceled. As a result, all the
subsequent work in the caller's coroutine will be canceled. This is often the desired
behavior. If there's any cleanup that needs to be done when an animation gets canceled,
consider starting the animation in a try-catch
block.
Note: once the animation ends, its velocity will be reset to 0. The animation state at
the point of interruption/reaching bound is captured in the returned AnimationResult
. If
there's a need to continue the momentum that the animation had before it was interrupted or
reached the bound, it's recommended to use the velocity in the returned
AnimationResult.endState
to start another animation.
public suspend fun animateDecay(
initialVelocity: T,
animationSpec: DecayAnimationSpec<T>,
block: (Animatable<T, V>.() -> Unit)? = null,
): AnimationResult<T, V>
Start a decay animation (i.e. an animation that slows down from the given initialVelocity
starting at current Animatable.value
until the velocity reaches 0. If there's already an
ongoing animation, the animation in-flight will be immediately cancelled. Decay animation is
often used after a fling gesture.
animationSpec
defines the decay animation that will be used for this animation. Some
options for this animationSpec
include: splineBasedDecay
and exponentialDecay
. block
will be invoked on each
animation frame.
Returns an AnimationResult
object, that contains the reason
for
ending the animation, and an end state of the animation. The reason for ending the animation
will be Finished
if the animation finishes successfully without any interruption. If the
animation reaches the either lowerBound
or upperBound
in any dimension, the animation
will end with BoundReached
being the end reason.
If the animation gets interrupted by 1) another call to start an animation (i.e.
animateTo
/animateDecay
), 2) Animatable.stop
, or 3)Animatable.snapTo
, the canceled
animation will throw a CancellationException
as the job gets canceled. As a result, all the
subsequent work in the caller's coroutine will be canceled. This is often the desired
behavior. If there's any cleanup that needs to be done when an animation gets canceled,
consider starting the animation in a try-catch
block.
Note, once the animation ends, its velocity will be reset to 0. If there's a need to
continue the momentum before the animation gets interrupted or reaches the bound, it's
recommended to use the velocity in the returned AnimationResult.endState
to start another
animation.
public suspend fun snapTo(targetValue: T)
Sets the current value to the target value, without any animation. This will also cancel any
on-going animation with a CancellationException
. This function will return after
canceling any on-going animation and updating the Animatable.value
and
Animatable.targetValue
to the provided targetValue
.
Note: If the lowerBound
or upperBound
is specified, the provided targetValue
will
be clamped to the bounds to ensure Animatable.value
is always within bounds.
See animateTo
and animateDecay
for more details about animation being canceled.
Parameters
targetValue | The new target value to set value to. |
public suspend fun stop()
Stops any on-going animation with a CancellationException
.
This function will not return until the ongoing animation has been canceled (if any). Note,
stop
function does not skip the animation value to its target value. Rather the
animation will be stopped in its track. Consider snapTo
if it's desired to not only stop
the animation but also snap the value
to a given value.
See animateTo
and animateDecay
for more details about animation being canceled.
public fun asState(): State<T>
Returns a State
representing the current value
of this animation. This allows hoisting
the animation's current value without causing unnecessary recompositions when the value
changes.