call,
A animator,
boolean isReverse
) {
int size = list == null ? 0 : list.size();
if (size > 0) {
// Try to reuse mCacheList to store the items of list.
Object[] array = mCachedList.getAndSet(null);
if (array == null || array.length < size) {
array = new Object[size];
}
list.toArray(array);
for (int i = 0; i < size; i++) {
//noinspection unchecked
T item = (T) array[i];
call.call(item, animator, isReverse);
array[i] = null;
}
// Store it for the next call so we can reuse this array, if needed.
mCachedList.compareAndSet(null, array);
}
}
/**
* An animation listener receives notifications from an animation.
* Notifications indicate animation related events, such as the end or the
* repetition of the animation.
*/
public static interface AnimatorListener {
/**
* Notifies the start of the animation as well as the animation's overall play direction.
* This method's default behavior is to call {@link #onAnimationStart(Animator)}. This
* method can be overridden, though not required, to get the additional play direction info
* when an animation starts. Skipping calling super when overriding this method results in
* {@link #onAnimationStart(Animator)} not getting called.
*
* @param animation The started animation.
* @param isReverse Whether the animation is playing in reverse.
*/
default void onAnimationStart(@NonNull Animator animation, boolean isReverse) {
onAnimationStart(animation);
}
/**
*
Notifies the end of the animation. This callback is not invoked
* for animations with repeat count set to INFINITE.
*
* This method's default behavior is to call {@link #onAnimationEnd(Animator)}. This
* method can be overridden, though not required, to get the additional play direction info
* when an animation ends. Skipping calling super when overriding this method results in
* {@link #onAnimationEnd(Animator)} not getting called.
*
* @param animation The animation which reached its end.
* @param isReverse Whether the animation is playing in reverse.
*/
default void onAnimationEnd(@NonNull Animator animation, boolean isReverse) {
onAnimationEnd(animation);
}
/**
*
Notifies the start of the animation.
*
* @param animation The started animation.
*/
void onAnimationStart(@NonNull Animator animation);
/**
* Notifies the end of the animation. This callback is not invoked
* for animations with repeat count set to INFINITE.
*
* @param animation The animation which reached its end.
*/
void onAnimationEnd(@NonNull Animator animation);
/**
* Notifies the cancellation of the animation. This callback is not invoked
* for animations with repeat count set to INFINITE.
*
* @param animation The animation which was canceled.
*/
void onAnimationCancel(@NonNull Animator animation);
/**
* Notifies the repetition of the animation.
*
* @param animation The animation which was repeated.
*/
void onAnimationRepeat(@NonNull Animator animation);
}
/**
* A pause listener receives notifications from an animation when the
* animation is {@link #pause() paused} or {@link #resume() resumed}.
*
* @see #addPauseListener(AnimatorPauseListener)
*/
public static interface AnimatorPauseListener {
/**
* Notifies that the animation was paused.
*
* @param animation The animaton being paused.
* @see #pause()
*/
void onAnimationPause(@NonNull Animator animation);
/**
* Notifies that the animation was resumed, after being
* previously paused.
*
* @param animation The animation being resumed.
* @see #resume()
*/
void onAnimationResume(@NonNull Animator animation);
}
/**
* Whether or not the Animator is allowed to run asynchronously off of
* the UI thread. This is a hint that informs the Animator that it is
* OK to run the animation off-thread, however the Animator may decide
* that it must run the animation on the UI thread anyway.
*
*
Regardless of whether or not the animation runs asynchronously, all
* listener callbacks will be called on the UI thread.
*
* To be able to use this hint the following must be true:
*
* - The animator is immutable while {@link #isStarted()} is true. Requests
* to change duration, delay, etc... may be ignored.
* - Lifecycle callback events may be asynchronous. Events such as
* {@link Animator.AnimatorListener#onAnimationEnd(Animator)} or
* {@link Animator.AnimatorListener#onAnimationRepeat(Animator)} may end up delayed
* as they must be posted back to the UI thread, and any actions performed
* by those callbacks (such as starting new animations) will not happen
* in the same frame.
* - State change requests ({@link #cancel()}, {@link #end()}, {@link #reverse()}, etc...)
* may be asynchronous. It is guaranteed that all state changes that are
* performed on the UI thread in the same frame will be applied as a single
* atomic update, however that frame may be the current frame,
* the next frame, or some future frame. This will also impact the observed
* state of the Animator. For example, {@link #isStarted()} may still return true
* after a call to {@link #end()}. Using the lifecycle callbacks is preferred over
* queries to {@link #isStarted()}, {@link #isRunning()}, and {@link #isPaused()}
* for this reason.
*
* @hide
*/
public void setAllowRunningAsynchronously(boolean mayRunAsync) {
// It is up to subclasses to support this, if they can.
}
/**
* Creates a {@link ConstantState} which holds changing configurations information associated
* with the given Animator.
*
* When {@link #newInstance()} is called, default implementation clones the Animator.
*/
private static class AnimatorConstantState extends ConstantState {
final Animator mAnimator;
@Config int mChangingConf;
public AnimatorConstantState(Animator animator) {
mAnimator = animator;
// ensure a reference back to here so that constante state is not gc'ed.
mAnimator.mConstantState = this;
mChangingConf = mAnimator.getChangingConfigurations();
}
@Override
public @Config int getChangingConfigurations() {
return mChangingConf;
}
@Override
public Animator newInstance() {
final Animator clone = mAnimator.clone();
clone.mConstantState = this;
return clone;
}
}
/**
* Internally used by {@link #callOnList(ArrayList, AnimatorCaller, Object, boolean)} to
* make a call on all children of a list. This can be for start, stop, pause, cancel, update,
* etc notifications.
*
* @param The type of listener to make the call on
* @param The type of animator that is passed as a parameter
*/
interface AnimatorCaller {
void call(T listener, A animator, boolean isReverse);
AnimatorCaller ON_START = AnimatorListener::onAnimationStart;
AnimatorCaller ON_END = AnimatorListener::onAnimationEnd;
AnimatorCaller ON_CANCEL =
(listener, animator, isReverse) -> listener.onAnimationCancel(animator);
AnimatorCaller ON_REPEAT =
(listener, animator, isReverse) -> listener.onAnimationRepeat(animator);
AnimatorCaller ON_PAUSE =
(listener, animator, isReverse) -> listener.onAnimationPause(animator);
AnimatorCaller ON_RESUME =
(listener, animator, isReverse) -> listener.onAnimationResume(animator);
AnimatorCaller ON_UPDATE =
(listener, animator, isReverse) -> listener.onAnimationUpdate(animator);
}
}