java.lang.Object
↳androidx.dynamicanimation.animation.DynamicAnimation<T>
Subclasses:
SpringAnimation, FlingAnimation
Gradle dependencies
compile group: 'androidx.dynamicanimation', name: 'dynamicanimation', version: '1.1.0-alpha03'
- groupId: androidx.dynamicanimation
- artifactId: dynamicanimation
- version: 1.1.0-alpha03
Artifact androidx.dynamicanimation:dynamicanimation:1.1.0-alpha03 it located at Google repository (https://maven.google.com/)
Androidx artifact mapping:
androidx.dynamicanimation:dynamicanimation com.android.support:support-dynamic-animation
Androidx class mapping:
androidx.dynamicanimation.animation.DynamicAnimation android.support.animation.DynamicAnimation
Overview
This class is the base class of physics-based animations. It manages the animation's
lifecycle such as DynamicAnimation.start() and DynamicAnimation.cancel(). This base class also handles the common
setup for all the subclass animations. For example, DynamicAnimation supports adding
DynamicAnimation.OnAnimationEndListener and DynamicAnimation.OnAnimationUpdateListener so that the important
animation events can be observed through the callbacks. The start conditions for any subclass of
DynamicAnimation can be set using DynamicAnimation.setStartValue(float) and
DynamicAnimation.setStartVelocity(float).
Summary
Methods |
---|
public DynamicAnimation<T> | addEndListener(DynamicAnimation.OnAnimationEndListener listener)
Adds an end listener to the animation for receiving onAnimationEnd callbacks. |
public DynamicAnimation<T> | addUpdateListener(DynamicAnimation.OnAnimationUpdateListener listener)
Adds an update listener to the animation for receiving per-frame animation update callbacks. |
public void | cancel()
Cancels the on-going animation. |
public boolean | doAnimationFrame(long frameTime)
This gets call on each frame of the animation. |
public float | getMinimumVisibleChange()
Returns the minimum change in the animation property that could be visibly different to
users. |
public boolean | isRunning()
Returns whether the animation is currently running. |
public void | removeEndListener(DynamicAnimation.OnAnimationEndListener listener)
Removes the end listener from the animation, so as to stop receiving animation end callbacks. |
public void | removeUpdateListener(DynamicAnimation.OnAnimationUpdateListener listener)
Removes the update listener from the animation, so as to stop receiving animation update
callbacks. |
public DynamicAnimation<T> | setMaxValue(float max)
Sets the max value of the animation. |
public DynamicAnimation<T> | setMinimumVisibleChange(float minimumVisibleChange)
This method sets the minimal change of animation value that is visible to users, which helps
determine a reasonable threshold for the animation's termination condition. |
public DynamicAnimation<T> | setMinValue(float min)
Sets the min value of the animation. |
public DynamicAnimation<T> | setStartValue(float startValue)
Sets the start value of the animation. |
public DynamicAnimation<T> | setStartVelocity(float startVelocity)
Start velocity of the animation. |
public void | start()
Starts an animation. |
from java.lang.Object | clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait |
Fields
View's translationX property.
View's translationY property.
View's translationZ property.
View's scaleX property.
View's scaleY property.
View's rotation property.
View's rotationX property.
View's rotationY property.
View's x property.
View's y property.
View's z property.
View's alpha property.
View's scrollX property.
View's scrollY property.
public static final float
MIN_VISIBLE_CHANGE_PIXELSThe minimum visible change in pixels that can be visible to users.
public static final float
MIN_VISIBLE_CHANGE_ROTATION_DEGREESThe minimum visible change in degrees that can be visible to users.
public static final float
MIN_VISIBLE_CHANGE_ALPHAThe minimum visible change in alpha that can be visible to users.
public static final float
MIN_VISIBLE_CHANGE_SCALEThe minimum visible change in scale that can be visible to users.
Methods
Sets the start value of the animation. If start value is not set, the animation will get
the current value for the view's property, and use that as the start value.
Parameters:
startValue: start value for the animation
Returns:
the Animation whose start value is being set
Start velocity of the animation. Default velocity is 0. Unit: change in property per
second (e.g. pixels per second, scale/alpha value change per second).
Note when using a fixed value as the start velocity (as opposed to getting the velocity
through touch events), it is recommended to define such a value in dp/second and convert it
to pixel/second based on the density of the screen to achieve a consistent look across
different screens.
To convert from dp/second to pixel/second:
float pixelPerSecond = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpPerSecond,
getResources().getDisplayMetrics());
Parameters:
startVelocity: start velocity of the animation
Returns:
the Animation whose start velocity is being set
Sets the max value of the animation. Animations will not animate beyond their max value.
Whether or not animation will come to an end when max value is reached is dependent on the
child animation's implementation.
Parameters:
max: maximum value of the property to be animated
Returns:
the Animation whose max value is being set
Sets the min value of the animation. Animations will not animate beyond their min value.
Whether or not animation will come to an end when min value is reached is dependent on the
child animation's implementation.
Parameters:
min: minimum value of the property to be animated
Returns:
the Animation whose min value is being set
Adds an end listener to the animation for receiving onAnimationEnd callbacks. If the listener
is null or has already been added to the list of listeners for the animation, no op.
Parameters:
listener: the listener to be added
Returns:
the animation to which the listener is added
Removes the end listener from the animation, so as to stop receiving animation end callbacks.
Parameters:
listener: the listener to be removed
Adds an update listener to the animation for receiving per-frame animation update callbacks.
If the listener is null or has already been added to the list of listeners for the
animation, no op.
Note that update listener should only be added before the start of the animation.
Parameters:
listener: the listener to be added
Returns:
the animation to which the listener is added
Removes the update listener from the animation, so as to stop receiving animation update
callbacks.
Parameters:
listener: the listener to be removed
This method sets the minimal change of animation value that is visible to users, which helps
determine a reasonable threshold for the animation's termination condition. It is critical
to set the minimal visible change for custom properties (i.e. non-ViewProperty
s)
unless the custom property is in pixels.
For custom properties, this minimum visible change defaults to change in pixel
(i.e. DynamicAnimation.MIN_VISIBLE_CHANGE_PIXELS. It is recommended to adjust this value that is
reasonable for the property to be animated. A general rule of thumb to calculate such a value
is: minimum visible change = range of custom property value / corresponding pixel range. For
example, if the property to be animated is a progress (from 0 to 100) that corresponds to a
200-pixel change. Then the min visible change should be 100 / 200. (i.e. 0.5).
It's not necessary to call this method when animating DynamicAnimation.ViewPropertys, as the
minimum visible change will be derived from the property. For example, if the property to be
animated is in pixels (i.e. DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y,
DynamicAnimation.TRANSLATION_Z, @DynamicAnimation.SCROLL_X or DynamicAnimation.SCROLL_Y), the default minimum visible
change is 1 (pixel). For DynamicAnimation.ROTATION, DynamicAnimation.ROTATION_X or DynamicAnimation.ROTATION_Y, the
animation will use DynamicAnimation.MIN_VISIBLE_CHANGE_ROTATION_DEGREES as the min visible change,
which is 1/10. Similarly, the minimum visible change for alpha (
i.e. DynamicAnimation.MIN_VISIBLE_CHANGE_ALPHA is defined as 1 / 256.
Parameters:
minimumVisibleChange: minimum change in property value that is visible to users
Returns:
the animation whose min visible change is being set
public float
getMinimumVisibleChange()
Returns the minimum change in the animation property that could be visibly different to
users.
Returns:
minimum change in property value that is visible to users
Starts an animation. If the animation has already been started, no op. Note that calling
DynamicAnimation.start() will not immediately set the property value to start value of the animation.
The property values will be changed at each animation pulse, which happens before the draw
pass. As a result, the changes will be reflected in the next frame, the same as if the values
were set immediately. This method should only be called on main thread.
Cancels the on-going animation. If the animation hasn't started, no op. Note that this method
should only be called on main thread.
public boolean
isRunning()
Returns whether the animation is currently running.
Returns:
true if the animation is currently running, false otherwise
public boolean
doAnimationFrame(long frameTime)
This gets call on each frame of the animation. Animation value and velocity are updated
in this method based on the new frame time. The property value of the view being animated
is then updated. The animation's ending conditions are also checked in this method. Once
the animation reaches equilibrium, the animation will come to its end, and end listeners
will be notified, if any.
Source
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.dynamicanimation.animation;
import android.annotation.SuppressLint;
import android.os.Looper;
import android.util.AndroidRuntimeException;
import android.view.View;
import androidx.annotation.FloatRange;
import androidx.annotation.MainThread;
import androidx.annotation.RestrictTo;
import androidx.core.view.ViewCompat;
import java.util.ArrayList;
/**
* This class is the base class of physics-based animations. It manages the animation's
* lifecycle such as {@link #start()} and {@link #cancel()}. This base class also handles the common
* setup for all the subclass animations. For example, DynamicAnimation supports adding
* {@link OnAnimationEndListener} and {@link OnAnimationUpdateListener} so that the important
* animation events can be observed through the callbacks. The start conditions for any subclass of
* DynamicAnimation can be set using {@link #setStartValue(float)} and
* {@link #setStartVelocity(float)}.
*
* @param <T> subclass of DynamicAnimation
*/
public abstract class DynamicAnimation<T extends DynamicAnimation<T>>
implements AnimationHandler.AnimationFrameCallback {
/**
* ViewProperty holds the access of a property of a {@link View}. When an animation is
* created with a {@link ViewProperty} instance, the corresponding property value of the view
* will be updated through this ViewProperty instance.
*/
public abstract static class ViewProperty extends FloatPropertyCompat<View> {
private ViewProperty(String name) {
super(name);
}
}
/**
* View's translationX property.
*/
public static final ViewProperty TRANSLATION_X = new ViewProperty("translationX") {
@Override
public void setValue(View view, float value) {
view.setTranslationX(value);
}
@Override
public float getValue(View view) {
return view.getTranslationX();
}
};
/**
* View's translationY property.
*/
public static final ViewProperty TRANSLATION_Y = new ViewProperty("translationY") {
@Override
public void setValue(View view, float value) {
view.setTranslationY(value);
}
@Override
public float getValue(View view) {
return view.getTranslationY();
}
};
/**
* View's translationZ property.
*/
public static final ViewProperty TRANSLATION_Z = new ViewProperty("translationZ") {
@Override
public void setValue(View view, float value) {
ViewCompat.setTranslationZ(view, value);
}
@Override
public float getValue(View view) {
return ViewCompat.getTranslationZ(view);
}
};
/**
* View's scaleX property.
*/
public static final ViewProperty SCALE_X = new ViewProperty("scaleX") {
@Override
public void setValue(View view, float value) {
view.setScaleX(value);
}
@Override
public float getValue(View view) {
return view.getScaleX();
}
};
/**
* View's scaleY property.
*/
public static final ViewProperty SCALE_Y = new ViewProperty("scaleY") {
@Override
public void setValue(View view, float value) {
view.setScaleY(value);
}
@Override
public float getValue(View view) {
return view.getScaleY();
}
};
/**
* View's rotation property.
*/
public static final ViewProperty ROTATION = new ViewProperty("rotation") {
@Override
public void setValue(View view, float value) {
view.setRotation(value);
}
@Override
public float getValue(View view) {
return view.getRotation();
}
};
/**
* View's rotationX property.
*/
public static final ViewProperty ROTATION_X = new ViewProperty("rotationX") {
@Override
public void setValue(View view, float value) {
view.setRotationX(value);
}
@Override
public float getValue(View view) {
return view.getRotationX();
}
};
/**
* View's rotationY property.
*/
public static final ViewProperty ROTATION_Y = new ViewProperty("rotationY") {
@Override
public void setValue(View view, float value) {
view.setRotationY(value);
}
@Override
public float getValue(View view) {
return view.getRotationY();
}
};
/**
* View's x property.
*/
public static final ViewProperty X = new ViewProperty("x") {
@Override
public void setValue(View view, float value) {
view.setX(value);
}
@Override
public float getValue(View view) {
return view.getX();
}
};
/**
* View's y property.
*/
public static final ViewProperty Y = new ViewProperty("y") {
@Override
public void setValue(View view, float value) {
view.setY(value);
}
@Override
public float getValue(View view) {
return view.getY();
}
};
/**
* View's z property.
*/
public static final ViewProperty Z = new ViewProperty("z") {
@Override
public void setValue(View view, float value) {
ViewCompat.setZ(view, value);
}
@Override
public float getValue(View view) {
return ViewCompat.getZ(view);
}
};
/**
* View's alpha property.
*/
public static final ViewProperty ALPHA = new ViewProperty("alpha") {
@Override
public void setValue(View view, float value) {
view.setAlpha(value);
}
@Override
public float getValue(View view) {
return view.getAlpha();
}
};
// Properties below are not RenderThread compatible
/**
* View's scrollX property.
*/
public static final ViewProperty SCROLL_X = new ViewProperty("scrollX") {
@Override
public void setValue(View view, float value) {
view.setScrollX((int) value);
}
@Override
public float getValue(View view) {
return view.getScrollX();
}
};
/**
* View's scrollY property.
*/
public static final ViewProperty SCROLL_Y = new ViewProperty("scrollY") {
@Override
public void setValue(View view, float value) {
view.setScrollY((int) value);
}
@Override
public float getValue(View view) {
return view.getScrollY();
}
};
/**
* The minimum visible change in pixels that can be visible to users.
*/
@SuppressLint("MinMaxConstant")
public static final float MIN_VISIBLE_CHANGE_PIXELS = 1f;
/**
* The minimum visible change in degrees that can be visible to users.
*/
@SuppressLint("MinMaxConstant")
public static final float MIN_VISIBLE_CHANGE_ROTATION_DEGREES = 1f / 10f;
/**
* The minimum visible change in alpha that can be visible to users.
*/
@SuppressLint("MinMaxConstant")
public static final float MIN_VISIBLE_CHANGE_ALPHA = 1f / 256f;
/**
* The minimum visible change in scale that can be visible to users.
*/
@SuppressLint("MinMaxConstant")
public static final float MIN_VISIBLE_CHANGE_SCALE = 1f / 500f;
// Use the max value of float to indicate an unset state.
private static final float UNSET = Float.MAX_VALUE;
// Multiplier to the min visible change value for value threshold
private static final float THRESHOLD_MULTIPLIER = 0.75f;
// Internal tracking for velocity.
float mVelocity = 0;
// Internal tracking for value.
float mValue = UNSET;
// Tracks whether start value is set. If not, the animation will obtain the value at the time
// of starting through the getter and use that as the starting value of the animation.
boolean mStartValueIsSet = false;
// Target to be animated.
final Object mTarget;
// View property id.
final FloatPropertyCompat mProperty;
// Package private tracking of animation lifecycle state. Visible to subclass animations.
boolean mRunning = false;
// Min and max values that defines the range of the animation values.
float mMaxValue = Float.MAX_VALUE;
float mMinValue = -mMaxValue;
// Last frame time. Always gets reset to -1 at the end of the animation.
private long mLastFrameTime = 0;
private float mMinVisibleChange;
// List of end listeners
private final ArrayList<OnAnimationEndListener> mEndListeners = new ArrayList<>();
// List of update listeners
private final ArrayList<OnAnimationUpdateListener> mUpdateListeners = new ArrayList<>();
// Internal state for value/velocity pair.
static class MassState {
float mValue;
float mVelocity;
}
/**
* Creates a dynamic animation with the given FloatValueHolder instance.
*
* @param floatValueHolder the FloatValueHolder instance to be animated.
*/
DynamicAnimation(final FloatValueHolder floatValueHolder) {
mTarget = null;
mProperty = new FloatPropertyCompat("FloatValueHolder") {
@Override
public float getValue(Object object) {
return floatValueHolder.getValue();
}
@Override
public void setValue(Object object, float value) {
floatValueHolder.setValue(value);
}
};
mMinVisibleChange = MIN_VISIBLE_CHANGE_PIXELS;
}
/**
* Creates a dynamic animation to animate the given property for the given {@link View}
*
* @param object the Object whose property is to be animated
* @param property the property to be animated
*/
<K> DynamicAnimation(K object, FloatPropertyCompat<K> property) {
mTarget = object;
mProperty = property;
if (mProperty == ROTATION || mProperty == ROTATION_X
|| mProperty == ROTATION_Y) {
mMinVisibleChange = MIN_VISIBLE_CHANGE_ROTATION_DEGREES;
} else if (mProperty == ALPHA) {
mMinVisibleChange = MIN_VISIBLE_CHANGE_ALPHA;
} else if (mProperty == SCALE_X || mProperty == SCALE_Y) {
mMinVisibleChange = MIN_VISIBLE_CHANGE_ALPHA;
} else {
mMinVisibleChange = MIN_VISIBLE_CHANGE_PIXELS;
}
}
/**
* Sets the start value of the animation. If start value is not set, the animation will get
* the current value for the view's property, and use that as the start value.
*
* @param startValue start value for the animation
* @return the Animation whose start value is being set
*/
@SuppressWarnings("unchecked")
public T setStartValue(float startValue) {
mValue = startValue;
mStartValueIsSet = true;
return (T) this;
}
/**
* Start velocity of the animation. Default velocity is 0. Unit: change in property per
* second (e.g. pixels per second, scale/alpha value change per second).
*
* <p>Note when using a fixed value as the start velocity (as opposed to getting the velocity
* through touch events), it is recommended to define such a value in dp/second and convert it
* to pixel/second based on the density of the screen to achieve a consistent look across
* different screens.
*
* <p>To convert from dp/second to pixel/second:
* <pre class="prettyprint">
* float pixelPerSecond = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpPerSecond,
* getResources().getDisplayMetrics());
* </pre>
*
* @param startVelocity start velocity of the animation
* @return the Animation whose start velocity is being set
*/
@SuppressWarnings("unchecked")
public T setStartVelocity(float startVelocity) {
mVelocity = startVelocity;
return (T) this;
}
/**
* Sets the max value of the animation. Animations will not animate beyond their max value.
* Whether or not animation will come to an end when max value is reached is dependent on the
* child animation's implementation.
*
* @param max maximum value of the property to be animated
* @return the Animation whose max value is being set
*/
@SuppressWarnings("unchecked")
public T setMaxValue(float max) {
// This max value should be checked and handled in the subclass animations, instead of
// assuming the end of the animations when the max/min value is hit in the base class.
// The reason is that hitting max/min value may just be a transient state, such as during
// the spring oscillation.
mMaxValue = max;
return (T) this;
}
/**
* Sets the min value of the animation. Animations will not animate beyond their min value.
* Whether or not animation will come to an end when min value is reached is dependent on the
* child animation's implementation.
*
* @param min minimum value of the property to be animated
* @return the Animation whose min value is being set
*/
@SuppressWarnings("unchecked")
public T setMinValue(float min) {
mMinValue = min;
return (T) this;
}
/**
* Adds an end listener to the animation for receiving onAnimationEnd callbacks. If the listener
* is {@code null} or has already been added to the list of listeners for the animation, no op.
*
* @param listener the listener to be added
* @return the animation to which the listener is added
*/
@SuppressWarnings("unchecked")
public T addEndListener(OnAnimationEndListener listener) {
if (!mEndListeners.contains(listener)) {
mEndListeners.add(listener);
}
return (T) this;
}
/**
* Removes the end listener from the animation, so as to stop receiving animation end callbacks.
*
* @param listener the listener to be removed
*/
public void removeEndListener(OnAnimationEndListener listener) {
removeEntry(mEndListeners, listener);
}
/**
* Adds an update listener to the animation for receiving per-frame animation update callbacks.
* If the listener is {@code null} or has already been added to the list of listeners for the
* animation, no op.
*
* <p>Note that update listener should only be added before the start of the animation.
*
* @param listener the listener to be added
* @return the animation to which the listener is added
* @throws UnsupportedOperationException if the update listener is added after the animation has
* started
*/
@SuppressWarnings("unchecked")
public T addUpdateListener(OnAnimationUpdateListener listener) {
if (isRunning()) {
// Require update listener to be added before the animation, such as when we start
// the animation, we know whether the animation is RenderThread compatible.
throw new UnsupportedOperationException("Error: Update listeners must be added before"
+ "the animation.");
}
if (!mUpdateListeners.contains(listener)) {
mUpdateListeners.add(listener);
}
return (T) this;
}
/**
* Removes the update listener from the animation, so as to stop receiving animation update
* callbacks.
*
* @param listener the listener to be removed
*/
public void removeUpdateListener(OnAnimationUpdateListener listener) {
removeEntry(mUpdateListeners, listener);
}
/**
* This method sets the minimal change of animation value that is visible to users, which helps
* determine a reasonable threshold for the animation's termination condition. It is critical
* to set the minimal visible change for custom properties (i.e. non-<code>ViewProperty</code>s)
* unless the custom property is in pixels.
*
* <p>For custom properties, this minimum visible change defaults to change in pixel
* (i.e. {@link #MIN_VISIBLE_CHANGE_PIXELS}. It is recommended to adjust this value that is
* reasonable for the property to be animated. A general rule of thumb to calculate such a value
* is: minimum visible change = range of custom property value / corresponding pixel range. For
* example, if the property to be animated is a progress (from 0 to 100) that corresponds to a
* 200-pixel change. Then the min visible change should be 100 / 200. (i.e. 0.5).
*
* <p>It's not necessary to call this method when animating {@link ViewProperty}s, as the
* minimum visible change will be derived from the property. For example, if the property to be
* animated is in pixels (i.e. {@link #TRANSLATION_X}, {@link #TRANSLATION_Y},
* {@link #TRANSLATION_Z}, @{@link #SCROLL_X} or {@link #SCROLL_Y}), the default minimum visible
* change is 1 (pixel). For {@link #ROTATION}, {@link #ROTATION_X} or {@link #ROTATION_Y}, the
* animation will use {@link #MIN_VISIBLE_CHANGE_ROTATION_DEGREES} as the min visible change,
* which is 1/10. Similarly, the minimum visible change for alpha (
* i.e. {@link #MIN_VISIBLE_CHANGE_ALPHA} is defined as 1 / 256.
*
* @param minimumVisibleChange minimum change in property value that is visible to users
* @return the animation whose min visible change is being set
* @throws IllegalArgumentException if the given threshold is not positive
*/
@SuppressWarnings("unchecked")
public T setMinimumVisibleChange(@FloatRange(from = 0.0, fromInclusive = false)
float minimumVisibleChange) {
if (minimumVisibleChange <= 0) {
throw new IllegalArgumentException("Minimum visible change must be positive.");
}
mMinVisibleChange = minimumVisibleChange;
setValueThreshold(minimumVisibleChange * THRESHOLD_MULTIPLIER);
return (T) this;
}
/**
* Returns the minimum change in the animation property that could be visibly different to
* users.
*
* @return minimum change in property value that is visible to users
*/
public float getMinimumVisibleChange() {
return mMinVisibleChange;
}
/**
* Remove {@code null} entries from the list.
*/
private static <T> void removeNullEntries(ArrayList<T> list) {
// Clean up null entries
for (int i = list.size() - 1; i >= 0; i--) {
if (list.get(i) == null) {
list.remove(i);
}
}
}
/**
* Remove an entry from the list by marking it {@code null} and clean up later.
*/
private static <T> void removeEntry(ArrayList<T> list, T entry) {
int id = list.indexOf(entry);
if (id >= 0) {
list.set(id, null);
}
}
/****************Animation Lifecycle Management***************/
/**
* Starts an animation. If the animation has already been started, no op. Note that calling
* {@link #start()} will not immediately set the property value to start value of the animation.
* The property values will be changed at each animation pulse, which happens before the draw
* pass. As a result, the changes will be reflected in the next frame, the same as if the values
* were set immediately. This method should only be called on main thread.
*
* @throws AndroidRuntimeException if this method is not called on the main thread
*/
@MainThread
public void start() {
if (Looper.myLooper() != Looper.getMainLooper()) {
throw new AndroidRuntimeException("Animations may only be started on the main thread");
}
if (!mRunning) {
startAnimationInternal();
}
}
/**
* Cancels the on-going animation. If the animation hasn't started, no op. Note that this method
* should only be called on main thread.
*
* @throws AndroidRuntimeException if this method is not called on the main thread
*/
@MainThread
public void cancel() {
if (Looper.myLooper() != Looper.getMainLooper()) {
throw new AndroidRuntimeException("Animations may only be canceled on the main thread");
}
if (mRunning) {
endAnimationInternal(true);
}
}
/**
* Returns whether the animation is currently running.
*
* @return {@code true} if the animation is currently running, {@code false} otherwise
*/
public boolean isRunning() {
return mRunning;
}
/************************** Private APIs below ********************************/
// This gets called when the animation is started, to finish the setup of the animation
// before the animation pulsing starts.
private void startAnimationInternal() {
if (!mRunning) {
mRunning = true;
if (!mStartValueIsSet) {
mValue = getPropertyValue();
}
// Sanity check:
if (mValue > mMaxValue || mValue < mMinValue) {
throw new IllegalArgumentException("Starting value need to be in between min"
+ " value and max value");
}
AnimationHandler.getInstance().addAnimationFrameCallback(this, 0);
}
}
/**
* This gets call on each frame of the animation. Animation value and velocity are updated
* in this method based on the new frame time. The property value of the view being animated
* is then updated. The animation's ending conditions are also checked in this method. Once
* the animation reaches equilibrium, the animation will come to its end, and end listeners
* will be notified, if any.
*
* @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY)
@Override
public boolean doAnimationFrame(long frameTime) {
if (mLastFrameTime == 0) {
// First frame.
mLastFrameTime = frameTime;
setPropertyValue(mValue);
return false;
}
long deltaT = frameTime - mLastFrameTime;
mLastFrameTime = frameTime;
boolean finished = updateValueAndVelocity(deltaT);
// Clamp value & velocity.
mValue = Math.min(mValue, mMaxValue);
mValue = Math.max(mValue, mMinValue);
setPropertyValue(mValue);
if (finished) {
endAnimationInternal(false);
}
return finished;
}
/**
* Updates the animation state (i.e. value and velocity). This method is package private, so
* subclasses can override this method to calculate the new value and velocity in their custom
* way.
*
* @param deltaT time elapsed in millisecond since last frame
* @return whether the animation has finished
*/
abstract boolean updateValueAndVelocity(long deltaT);
/**
* Internal method to reset the animation states when animation is finished/canceled.
*/
private void endAnimationInternal(boolean canceled) {
mRunning = false;
AnimationHandler.getInstance().removeCallback(this);
mLastFrameTime = 0;
mStartValueIsSet = false;
for (int i = 0; i < mEndListeners.size(); i++) {
if (mEndListeners.get(i) != null) {
mEndListeners.get(i).onAnimationEnd(this, canceled, mValue, mVelocity);
}
}
removeNullEntries(mEndListeners);
}
/**
* Updates the property value through the corresponding setter.
*/
@SuppressWarnings("unchecked")
void setPropertyValue(float value) {
mProperty.setValue(mTarget, value);
for (int i = 0; i < mUpdateListeners.size(); i++) {
if (mUpdateListeners.get(i) != null) {
mUpdateListeners.get(i).onAnimationUpdate(this, mValue, mVelocity);
}
}
removeNullEntries(mUpdateListeners);
}
/**
* Returns the default threshold.
*/
float getValueThreshold() {
return mMinVisibleChange * THRESHOLD_MULTIPLIER;
}
/**
* Obtain the property value through the corresponding getter.
*/
@SuppressWarnings("unchecked")
private float getPropertyValue() {
return mProperty.getValue(mTarget);
}
/****************Sub class animations**************/
/**
* Returns the acceleration at the given value with the given velocity.
**/
abstract float getAcceleration(float value, float velocity);
/**
* Returns whether the animation has reached equilibrium.
*/
abstract boolean isAtEquilibrium(float value, float velocity);
/**
* Updates the default value threshold for the animation based on the property to be animated.
*/
abstract void setValueThreshold(float threshold);
/**
* An animation listener that receives end notifications from an animation.
*/
public interface OnAnimationEndListener {
/**
* Notifies the end of an animation. Note that this callback will be invoked not only when
* an animation reach equilibrium, but also when the animation is canceled.
*
* @param animation animation that has ended or was canceled
* @param canceled whether the animation has been canceled
* @param value the final value when the animation stopped
* @param velocity the final velocity when the animation stopped
*/
void onAnimationEnd(DynamicAnimation animation, boolean canceled, float value,
float velocity);
}
/**
* Implementors of this interface can add themselves as update listeners
* to an <code>DynamicAnimation</code> instance to receive callbacks on every animation
* frame, after the current frame's values have been calculated for that
* <code>DynamicAnimation</code>.
*/
public interface OnAnimationUpdateListener {
/**
* Notifies the occurrence of another frame of the animation.
*
* @param animation animation that the update listener is added to
* @param value the current value of the animation
* @param velocity the current velocity of the animation
*/
void onAnimationUpdate(DynamicAnimation animation, float value, float velocity);
}
}