mirror of
https://gitlab.com/android_translation_layer/android_translation_layer.git
synced 2025-04-28 12:17:57 +03:00
copy AnimatorSet and related classes from AOSP
This makes AnimatedVectorDrawableCompat from androidx functional and probably also helps other animation stuff.
This commit is contained in:
parent
87b254156d
commit
c7f1e05f5d
11 changed files with 6252 additions and 121 deletions
518
src/api-impl/android/animation/AnimationHandler.java
Normal file
518
src/api-impl/android/animation/AnimationHandler.java
Normal file
|
@ -0,0 +1,518 @@
|
|||
/*
|
||||
* Copyright (C) 2015 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 android.animation;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.os.SystemClock;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.Log;
|
||||
import android.view.Choreographer;
|
||||
|
||||
/**
|
||||
* This custom, static handler handles the timing pulse that is shared by all active
|
||||
* ValueAnimators. This approach ensures that the setting of animation values will happen on the
|
||||
* same thread that animations start on, and that all animations will share the same times for
|
||||
* calculating their values, which makes synchronizing animations possible.
|
||||
*
|
||||
* The handler uses the Choreographer by default for doing periodic callbacks. A custom
|
||||
* AnimationFrameCallbackProvider can be set on the handler to provide timing pulse that
|
||||
* may be independent of UI frame update. This could be useful in testing.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class AnimationHandler {
|
||||
|
||||
private static final String TAG = "AnimationHandler";
|
||||
private static final boolean LOCAL_LOGV = false;
|
||||
|
||||
/**
|
||||
* Internal per-thread collections used to avoid set collisions as animations start and end
|
||||
* while being processed.
|
||||
*/
|
||||
private final ArrayMap<AnimationFrameCallback, Long> mDelayedCallbackStartTime =
|
||||
new ArrayMap<>();
|
||||
private final ArrayList<AnimationFrameCallback> mAnimationCallbacks =
|
||||
new ArrayList<>();
|
||||
private final ArrayList<AnimationFrameCallback> mCommitCallbacks =
|
||||
new ArrayList<>();
|
||||
private AnimationFrameCallbackProvider mProvider;
|
||||
|
||||
// Static flag which allows the pausing behavior to be globally disabled/enabled.
|
||||
private static boolean sAnimatorPausingEnabled = isPauseBgAnimationsEnabledInSystemProperties();
|
||||
|
||||
// Static flag which prevents the system property from overriding sAnimatorPausingEnabled field.
|
||||
private static boolean sOverrideAnimatorPausingSystemProperty = false;
|
||||
|
||||
/**
|
||||
* This paused list is used to store animators forcibly paused when the activity
|
||||
* went into the background (to avoid unnecessary background processing work).
|
||||
* These animators should be resume()'d when the activity returns to the foreground.
|
||||
*/
|
||||
private final ArrayList<Animator> mPausedAnimators = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* This structure is used to store the currently active objects (ViewRootImpls or
|
||||
* WallpaperService.Engines) in the process. Each of these objects sends a request to
|
||||
* AnimationHandler when it goes into the background (request to pause) or foreground
|
||||
* (request to resume). Because all animators are managed by AnimationHandler on the same
|
||||
* thread, it should only ever pause animators when *all* requestors are in the background.
|
||||
* This list tracks the background/foreground state of all requestors and only ever
|
||||
* pauses animators when all items are in the background (false). To simplify, we only ever
|
||||
* store visible (foreground) requestors; if the set size reaches zero, there are no
|
||||
* objects in the foreground and it is time to pause animators.
|
||||
*/
|
||||
private final ArrayList<WeakReference<Object>> mAnimatorRequestors = new ArrayList<>();
|
||||
|
||||
private final Choreographer.FrameCallback mFrameCallback = new Choreographer.FrameCallback() {
|
||||
@Override
|
||||
public void doFrame(long frameTimeNanos) {
|
||||
doAnimationFrame(getProvider().getFrameTime());
|
||||
if (mAnimationCallbacks.size() > 0) {
|
||||
getProvider().postFrameCallback(this);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public final static ThreadLocal<AnimationHandler> sAnimatorHandler = new ThreadLocal<>();
|
||||
private static AnimationHandler sTestHandler = null;
|
||||
private boolean mListDirty = false;
|
||||
|
||||
public static AnimationHandler getInstance() {
|
||||
if (sTestHandler != null) {
|
||||
return sTestHandler;
|
||||
}
|
||||
if (sAnimatorHandler.get() == null) {
|
||||
sAnimatorHandler.set(new AnimationHandler());
|
||||
}
|
||||
return sAnimatorHandler.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an instance that will be returned by {@link #getInstance()} on every thread.
|
||||
* @return the previously active test handler, if any.
|
||||
* @hide
|
||||
*/
|
||||
public static @Nullable AnimationHandler setTestHandler(@Nullable AnimationHandler handler) {
|
||||
AnimationHandler oldHandler = sTestHandler;
|
||||
sTestHandler = handler;
|
||||
return oldHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* System property that controls the behavior of pausing infinite animators when an app
|
||||
* is moved to the background.
|
||||
*
|
||||
* @return the value of 'framework.pause_bg_animations.enabled' system property
|
||||
*/
|
||||
private static boolean isPauseBgAnimationsEnabledInSystemProperties() {
|
||||
if (sOverrideAnimatorPausingSystemProperty) return sAnimatorPausingEnabled;
|
||||
return true; /*SystemProperties
|
||||
.getBoolean("framework.pause_bg_animations.enabled", true);*/
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable the default behavior of pausing infinite animators when
|
||||
* apps go into the background.
|
||||
*
|
||||
* @param enable Enable (default behavior) or disable background pausing behavior.
|
||||
*/
|
||||
public static void setAnimatorPausingEnabled(boolean enable) {
|
||||
sAnimatorPausingEnabled = enable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevents the setAnimatorPausingEnabled behavior from being overridden
|
||||
* by the 'framework.pause_bg_animations.enabled' system property value.
|
||||
*
|
||||
* This is for testing purposes only.
|
||||
*
|
||||
* @param enable Enable or disable (default behavior) overriding the system
|
||||
* property.
|
||||
*/
|
||||
public static void setOverrideAnimatorPausingSystemProperty(boolean enable) {
|
||||
sOverrideAnimatorPausingSystemProperty = enable;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is called when a window goes away. We should remove
|
||||
* it from the requestors list to ensure that we are counting requests correctly and not
|
||||
* tracking obsolete+enabled requestors.
|
||||
*/
|
||||
public static void removeRequestor(Object requestor) {
|
||||
getInstance().requestAnimatorsEnabledImpl(false, requestor);
|
||||
if (LOCAL_LOGV) {
|
||||
Log.v(TAG, "removeRequestor for " + requestor);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called from ViewRootImpl or WallpaperService when either a window is no
|
||||
* longer visible (enable == false) or when a window becomes visible (enable == true).
|
||||
* If animators are not properly disabled when activities are backgrounded, it can lead to
|
||||
* unnecessary processing, particularly for infinite animators, as the system will continue
|
||||
* to pulse timing events even though the results are not visible. As a workaround, we
|
||||
* pause all un-paused infinite animators, and resume them when any window in the process
|
||||
* becomes visible.
|
||||
*/
|
||||
public static void requestAnimatorsEnabled(boolean enable, Object requestor) {
|
||||
getInstance().requestAnimatorsEnabledImpl(enable, requestor);
|
||||
}
|
||||
|
||||
private void requestAnimatorsEnabledImpl(boolean enable, Object requestor) {
|
||||
boolean wasEmpty = mAnimatorRequestors.isEmpty();
|
||||
setAnimatorPausingEnabled(isPauseBgAnimationsEnabledInSystemProperties());
|
||||
synchronized (mAnimatorRequestors) {
|
||||
// Only store WeakRef objects to avoid leaks
|
||||
if (enable) {
|
||||
// First, check whether such a reference is already on the list
|
||||
WeakReference<Object> weakRef = null;
|
||||
for (int i = mAnimatorRequestors.size() - 1; i >= 0; --i) {
|
||||
WeakReference<Object> ref = mAnimatorRequestors.get(i);
|
||||
Object referent = ref.get();
|
||||
if (referent == requestor) {
|
||||
weakRef = ref;
|
||||
} else if (referent == null) {
|
||||
// Remove any reference that has been cleared
|
||||
mAnimatorRequestors.remove(i);
|
||||
}
|
||||
}
|
||||
if (weakRef == null) {
|
||||
weakRef = new WeakReference<>(requestor);
|
||||
mAnimatorRequestors.add(weakRef);
|
||||
}
|
||||
} else {
|
||||
for (int i = mAnimatorRequestors.size() - 1; i >= 0; --i) {
|
||||
WeakReference<Object> ref = mAnimatorRequestors.get(i);
|
||||
Object referent = ref.get();
|
||||
if (referent == requestor || referent == null) {
|
||||
// remove requested item or item that has been cleared
|
||||
mAnimatorRequestors.remove(i);
|
||||
}
|
||||
}
|
||||
// If a reference to the requestor wasn't in the list, nothing to remove
|
||||
}
|
||||
}
|
||||
if (!sAnimatorPausingEnabled) {
|
||||
// Resume any animators that have been paused in the meantime, otherwise noop
|
||||
// Leave logic above so that if pausing gets re-enabled, the state of the requestors
|
||||
// list is valid
|
||||
resumeAnimators();
|
||||
return;
|
||||
}
|
||||
boolean isEmpty = mAnimatorRequestors.isEmpty();
|
||||
if (wasEmpty != isEmpty) {
|
||||
// only paused/resume animators if there was a visibility change
|
||||
if (!isEmpty) {
|
||||
// If any requestors are enabled, resume currently paused animators
|
||||
resumeAnimators();
|
||||
} else {
|
||||
// Wait before pausing to avoid thrashing animator state for temporary backgrounding
|
||||
Choreographer.getInstance().postFrameCallbackDelayed(mPauser,
|
||||
/*Animator.getBackgroundPauseDelay()*/100);
|
||||
}
|
||||
}
|
||||
if (LOCAL_LOGV) {
|
||||
Log.v(TAG, (enable ? "enable" : "disable") + " animators for " + requestor
|
||||
+ " with pauseDelay of " + /*Animator.getBackgroundPauseDelay()*/100);
|
||||
for (int i = 0; i < mAnimatorRequestors.size(); ++i) {
|
||||
Log.v(TAG, "animatorRequestors " + i + " = "
|
||||
+ mAnimatorRequestors.get(i) + " with referent "
|
||||
+ mAnimatorRequestors.get(i).get());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void resumeAnimators() {
|
||||
Choreographer.getInstance().removeFrameCallback(mPauser);
|
||||
for (int i = mPausedAnimators.size() - 1; i >= 0; --i) {
|
||||
mPausedAnimators.get(i).resume();
|
||||
}
|
||||
mPausedAnimators.clear();
|
||||
}
|
||||
|
||||
private Choreographer.FrameCallback mPauser = new Choreographer.FrameCallback() { @Override public void doFrame(long frameTimeNanos) {
|
||||
if (mAnimatorRequestors.size() > 0) {
|
||||
// something enabled animators since this callback was scheduled - bail
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < mAnimationCallbacks.size(); ++i) {
|
||||
AnimationFrameCallback callback = mAnimationCallbacks.get(i);
|
||||
if (callback instanceof Animator) {
|
||||
Animator animator = ((Animator) callback);
|
||||
if (animator.getTotalDuration() == Animator.DURATION_INFINITE
|
||||
&& !animator.isPaused()) {
|
||||
mPausedAnimators.add(animator);
|
||||
animator.pause();
|
||||
}
|
||||
}
|
||||
}
|
||||
}};
|
||||
|
||||
/**
|
||||
* By default, the Choreographer is used to provide timing for frame callbacks. A custom
|
||||
* provider can be used here to provide different timing pulse.
|
||||
*/
|
||||
public void setProvider(AnimationFrameCallbackProvider provider) {
|
||||
if (provider == null) {
|
||||
mProvider = new MyFrameCallbackProvider();
|
||||
} else {
|
||||
mProvider = provider;
|
||||
}
|
||||
}
|
||||
|
||||
private AnimationFrameCallbackProvider getProvider() {
|
||||
if (mProvider == null) {
|
||||
mProvider = new MyFrameCallbackProvider();
|
||||
}
|
||||
return mProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register to get a callback on the next frame after the delay.
|
||||
*/
|
||||
public void addAnimationFrameCallback(final AnimationFrameCallback callback, long delay) {
|
||||
if (mAnimationCallbacks.size() == 0) {
|
||||
getProvider().postFrameCallback(mFrameCallback);
|
||||
}
|
||||
if (!mAnimationCallbacks.contains(callback)) {
|
||||
mAnimationCallbacks.add(callback);
|
||||
}
|
||||
|
||||
if (delay > 0) {
|
||||
mDelayedCallbackStartTime.put(callback, (SystemClock.uptimeMillis() + delay));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register to get a one shot callback for frame commit timing. Frame commit timing is the
|
||||
* time *after* traversals are done, as opposed to the animation frame timing, which is
|
||||
* before any traversals. This timing can be used to adjust the start time of an animation
|
||||
* when expensive traversals create big delta between the animation frame timing and the time
|
||||
* that animation is first shown on screen.
|
||||
*
|
||||
* Note this should only be called when the animation has already registered to receive
|
||||
* animation frame callbacks. This callback will be guaranteed to happen *after* the next
|
||||
* animation frame callback.
|
||||
*/
|
||||
public void addOneShotCommitCallback(final AnimationFrameCallback callback) {
|
||||
if (!mCommitCallbacks.contains(callback)) {
|
||||
mCommitCallbacks.add(callback);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the given callback from the list, so it will no longer be called for frame related
|
||||
* timing.
|
||||
*/
|
||||
public void removeCallback(AnimationFrameCallback callback) {
|
||||
mCommitCallbacks.remove(callback);
|
||||
mDelayedCallbackStartTime.remove(callback);
|
||||
int id = mAnimationCallbacks.indexOf(callback);
|
||||
if (id >= 0) {
|
||||
mAnimationCallbacks.set(id, null);
|
||||
mListDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void doAnimationFrame(long frameTime) {
|
||||
long currentTime = SystemClock.uptimeMillis();
|
||||
final int size = mAnimationCallbacks.size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
final AnimationFrameCallback callback = mAnimationCallbacks.get(i);
|
||||
if (callback == null) {
|
||||
continue;
|
||||
}
|
||||
if (isCallbackDue(callback, currentTime)) {
|
||||
callback.doAnimationFrame(frameTime);
|
||||
if (mCommitCallbacks.contains(callback)) {
|
||||
getProvider().postCommitCallback(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
commitAnimationFrame(callback, getProvider().getFrameTime());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
cleanUpList();
|
||||
}
|
||||
|
||||
private void commitAnimationFrame(AnimationFrameCallback callback, long frameTime) {
|
||||
if (!mDelayedCallbackStartTime.containsKey(callback) &&
|
||||
mCommitCallbacks.contains(callback)) {
|
||||
callback.commitAnimationFrame(frameTime);
|
||||
mCommitCallbacks.remove(callback);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the callbacks from mDelayedCallbackStartTime once they have passed the initial delay
|
||||
* so that they can start getting frame callbacks.
|
||||
*
|
||||
* @return true if they have passed the initial delay or have no delay, false otherwise.
|
||||
*/
|
||||
private boolean isCallbackDue(AnimationFrameCallback callback, long currentTime) {
|
||||
Long startTime = mDelayedCallbackStartTime.get(callback);
|
||||
if (startTime == null) {
|
||||
return true;
|
||||
}
|
||||
if (startTime < currentTime) {
|
||||
mDelayedCallbackStartTime.remove(callback);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of callbacks that have registered for frame callbacks.
|
||||
*/
|
||||
public static int getAnimationCount() {
|
||||
AnimationHandler handler = sTestHandler;
|
||||
if (handler == null) {
|
||||
handler = sAnimatorHandler.get();
|
||||
}
|
||||
if (handler == null) {
|
||||
return 0;
|
||||
}
|
||||
return handler.getCallbackSize();
|
||||
}
|
||||
|
||||
public static void setFrameDelay(long delay) {
|
||||
getInstance().getProvider().setFrameDelay(delay);
|
||||
}
|
||||
|
||||
public static long getFrameDelay() {
|
||||
return getInstance().getProvider().getFrameDelay();
|
||||
}
|
||||
|
||||
void autoCancelBasedOn(ObjectAnimator objectAnimator) {
|
||||
for (int i = mAnimationCallbacks.size() - 1; i >= 0; i--) {
|
||||
AnimationFrameCallback cb = mAnimationCallbacks.get(i);
|
||||
if (cb == null) {
|
||||
continue;
|
||||
}
|
||||
if (objectAnimator.shouldAutoCancel(cb)) {
|
||||
((Animator) mAnimationCallbacks.get(i)).cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void cleanUpList() {
|
||||
if (mListDirty) {
|
||||
for (int i = mAnimationCallbacks.size() - 1; i >= 0; i--) {
|
||||
if (mAnimationCallbacks.get(i) == null) {
|
||||
mAnimationCallbacks.remove(i);
|
||||
}
|
||||
}
|
||||
mListDirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
private int getCallbackSize() {
|
||||
int count = 0;
|
||||
int size = mAnimationCallbacks.size();
|
||||
for (int i = size - 1; i >= 0; i--) {
|
||||
if (mAnimationCallbacks.get(i) != null) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default provider of timing pulse that uses Choreographer for frame callbacks.
|
||||
*/
|
||||
private class MyFrameCallbackProvider implements AnimationFrameCallbackProvider {
|
||||
|
||||
final Choreographer mChoreographer = Choreographer.getInstance();
|
||||
|
||||
@Override
|
||||
public void postFrameCallback(Choreographer.FrameCallback callback) {
|
||||
mChoreographer.postFrameCallback(callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postCommitCallback(Runnable runnable) {
|
||||
mChoreographer.postCallback(/*Choreographer.CALLBACK_COMMIT*/4, runnable, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getFrameTime() {
|
||||
return mChoreographer.getFrameTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getFrameDelay() {
|
||||
return Choreographer.getFrameDelay();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFrameDelay(long delay) {
|
||||
Choreographer.setFrameDelay(delay);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Callbacks that receives notifications for animation timing and frame commit timing.
|
||||
* @hide
|
||||
*/
|
||||
public interface AnimationFrameCallback {
|
||||
/**
|
||||
* Run animation based on the frame time.
|
||||
* @param frameTime The frame start time, in the {@link SystemClock#uptimeMillis()} time
|
||||
* base.
|
||||
* @return if the animation has finished.
|
||||
*/
|
||||
boolean doAnimationFrame(long frameTime);
|
||||
|
||||
/**
|
||||
* This notifies the callback of frame commit time. Frame commit time is the time after
|
||||
* traversals happen, as opposed to the normal animation frame time that is before
|
||||
* traversals. This is used to compensate expensive traversals that happen as the
|
||||
* animation starts. When traversals take a long time to complete, the rendering of the
|
||||
* initial frame will be delayed (by a long time). But since the startTime of the
|
||||
* animation is set before the traversal, by the time of next frame, a lot of time would
|
||||
* have passed since startTime was set, the animation will consequently skip a few frames
|
||||
* to respect the new frameTime. By having the commit time, we can adjust the start time to
|
||||
* when the first frame was drawn (after any expensive traversals) so that no frames
|
||||
* will be skipped.
|
||||
*
|
||||
* @param frameTime The frame time after traversals happen, if any, in the
|
||||
* {@link SystemClock#uptimeMillis()} time base.
|
||||
*/
|
||||
void commitAnimationFrame(long frameTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* The intention for having this interface is to increase the testability of ValueAnimator.
|
||||
* Specifically, we can have a custom implementation of the interface below and provide
|
||||
* timing pulse without using Choreographer. That way we could use any arbitrary interval for
|
||||
* our timing pulse in the tests.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public interface AnimationFrameCallbackProvider {
|
||||
void postFrameCallback(Choreographer.FrameCallback callback);
|
||||
void postCommitCallback(Runnable runnable);
|
||||
long getFrameTime();
|
||||
long getFrameDelay();
|
||||
void setFrameDelay(long delay);
|
||||
}
|
||||
}
|
|
@ -1,55 +1,824 @@
|
|||
/*
|
||||
* Copyright (C) 2010 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 android.animation;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
public class Animator {
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.res.ConstantState;
|
||||
import android.util.LongArray;
|
||||
|
||||
public interface AnimatorListener {
|
||||
public abstract void onAnimationEnd (Animator animation);
|
||||
}
|
||||
/**
|
||||
* This is the superclass for classes which provide basic support for animations which can be
|
||||
* started, ended, and have <code>AnimatorListeners</code> added to them.
|
||||
*/
|
||||
public abstract class Animator implements Cloneable {
|
||||
|
||||
private long duration = 50;
|
||||
private AnimatorListener listener;
|
||||
/**
|
||||
* The value used to indicate infinite duration (e.g. when Animators repeat infinitely).
|
||||
*/
|
||||
public static final long DURATION_INFINITE = -1;
|
||||
/**
|
||||
* The set of listeners to be sent events through the life of an animation.
|
||||
*/
|
||||
ArrayList<AnimatorListener> mListeners = null;
|
||||
|
||||
public void setTarget(Object target) {}
|
||||
/**
|
||||
* The set of listeners to be sent pause/resume events through the life
|
||||
* of an animation.
|
||||
*/
|
||||
ArrayList<AnimatorPauseListener> mPauseListeners = null;
|
||||
|
||||
/**
|
||||
* Whether this animator is currently in a paused state.
|
||||
*/
|
||||
boolean mPaused = false;
|
||||
|
||||
/**
|
||||
* A set of flags which identify the type of configuration changes that can affect this
|
||||
* Animator. Used by the Animator cache.
|
||||
*/
|
||||
int mChangingConfigurations = 0;
|
||||
|
||||
/**
|
||||
* If this animator is inflated from a constant state, keep a reference to it so that
|
||||
* ConstantState will not be garbage collected until this animator is collected
|
||||
*/
|
||||
private AnimatorConstantState mConstantState;
|
||||
|
||||
/**
|
||||
* A cache of the values in a list. Used so that when calling the list, we have a copy
|
||||
* of it in case the list is modified while iterating. The array can be reused to avoid
|
||||
* allocation on every notification.
|
||||
*/
|
||||
private AtomicReference<Object[]> mCachedList = new AtomicReference<>();
|
||||
|
||||
/**
|
||||
* Tracks whether we've notified listeners of the onAnimationStart() event. This can be
|
||||
* complex to keep track of since we notify listeners at different times depending on
|
||||
* startDelay and whether start() was called before end().
|
||||
*/
|
||||
boolean mStartListenersCalled = false;
|
||||
|
||||
/**
|
||||
* Starts this animation. If the animation has a nonzero startDelay, the animation will start
|
||||
* running after that delay elapses. A non-delayed animation will have its initial
|
||||
* value(s) set immediately, followed by calls to
|
||||
* {@link AnimatorListener#onAnimationStart(Animator)} for any listeners of this animator.
|
||||
*
|
||||
* <p>The animation started by calling this method will be run on the thread that called
|
||||
* this method. This thread should have a Looper on it (a runtime exception will be thrown if
|
||||
* this is not the case). Also, if the animation will animate
|
||||
* properties of objects in the view hierarchy, then the calling thread should be the UI
|
||||
* thread for that view hierarchy.</p>
|
||||
*
|
||||
*/
|
||||
public void start() {
|
||||
if (listener == null)
|
||||
return;
|
||||
new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
|
||||
public void run() {
|
||||
if (listener != null)
|
||||
listener.onAnimationEnd(Animator.this);
|
||||
}
|
||||
}, duration);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels the animation. Unlike {@link #end()}, <code>cancel()</code> causes the animation to
|
||||
* stop in its tracks, sending an
|
||||
* {@link android.animation.Animator.AnimatorListener#onAnimationCancel(Animator)} to
|
||||
* its listeners, followed by an
|
||||
* {@link android.animation.Animator.AnimatorListener#onAnimationEnd(Animator)} message.
|
||||
*
|
||||
* <p>This method must be called on the thread that is running the animation.</p>
|
||||
*/
|
||||
public void cancel() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Ends the animation. This causes the animation to assign the end value of the property being
|
||||
* animated, then calling the
|
||||
* {@link android.animation.Animator.AnimatorListener#onAnimationEnd(Animator)} method on
|
||||
* its listeners.
|
||||
*
|
||||
* <p>This method must be called on the thread that is running the animation.</p>
|
||||
*/
|
||||
public void end() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Pauses a running animation. This method should only be called on the same thread on
|
||||
* which the animation was started. If the animation has not yet been {@link
|
||||
* #isStarted() started} or has since ended, then the call is ignored. Paused
|
||||
* animations can be resumed by calling {@link #resume()}.
|
||||
*
|
||||
* @see #resume()
|
||||
* @see #isPaused()
|
||||
* @see AnimatorPauseListener
|
||||
*/
|
||||
public void pause() {
|
||||
// We only want to pause started Animators or animators that setCurrentPlayTime()
|
||||
// have been called on. mStartListenerCalled will be true if seek has happened.
|
||||
if ((isStarted() || mStartListenersCalled) && !mPaused) {
|
||||
mPaused = true;
|
||||
notifyPauseListeners(AnimatorCaller.ON_PAUSE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resumes a paused animation, causing the animator to pick up where it left off
|
||||
* when it was paused. This method should only be called on the same thread on
|
||||
* which the animation was started. Calls to resume() on an animator that is
|
||||
* not currently paused will be ignored.
|
||||
*
|
||||
* @see #pause()
|
||||
* @see #isPaused()
|
||||
* @see AnimatorPauseListener
|
||||
*/
|
||||
public void resume() {
|
||||
if (mPaused) {
|
||||
mPaused = false;
|
||||
notifyPauseListeners(AnimatorCaller.ON_RESUME);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this animator is currently in a paused state.
|
||||
*
|
||||
* @return True if the animator is currently paused, false otherwise.
|
||||
*
|
||||
* @see #pause()
|
||||
* @see #resume()
|
||||
*/
|
||||
public boolean isPaused() {
|
||||
return mPaused;
|
||||
}
|
||||
|
||||
/**
|
||||
* The amount of time, in milliseconds, to delay processing the animation
|
||||
* after {@link #start()} is called.
|
||||
*
|
||||
* @return the number of milliseconds to delay running the animation
|
||||
*/
|
||||
public abstract long getStartDelay();
|
||||
|
||||
/**
|
||||
* The amount of time, in milliseconds, to delay processing the animation
|
||||
* after {@link #start()} is called.
|
||||
|
||||
* @param startDelay The amount of the delay, in milliseconds
|
||||
*/
|
||||
public abstract void setStartDelay(long startDelay);
|
||||
|
||||
/**
|
||||
* Sets the duration of the animation.
|
||||
*
|
||||
* @param duration The length of the animation, in milliseconds.
|
||||
*/
|
||||
public abstract Animator setDuration(long duration);
|
||||
|
||||
/**
|
||||
* Gets the duration of the animation.
|
||||
*
|
||||
* @return The length of the animation, in milliseconds.
|
||||
*/
|
||||
public abstract long getDuration();
|
||||
|
||||
/**
|
||||
* Gets the total duration of the animation, accounting for animation sequences, start delay,
|
||||
* and repeating. Return {@link #DURATION_INFINITE} if the duration is infinite.
|
||||
*
|
||||
* @return Total time an animation takes to finish, starting from the time {@link #start()}
|
||||
* is called. {@link #DURATION_INFINITE} will be returned if the animation or any
|
||||
* child animation repeats infinite times.
|
||||
*/
|
||||
public long getTotalDuration() {
|
||||
long duration = getDuration();
|
||||
if (duration == DURATION_INFINITE) {
|
||||
return DURATION_INFINITE;
|
||||
} else {
|
||||
return getStartDelay() + duration;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The time interpolator used in calculating the elapsed fraction of the
|
||||
* animation. The interpolator determines whether the animation runs with
|
||||
* linear or non-linear motion, such as acceleration and deceleration. The
|
||||
* default value is {@link android.view.animation.AccelerateDecelerateInterpolator}.
|
||||
*
|
||||
* @param value the interpolator to be used by this animation
|
||||
*/
|
||||
public abstract void setInterpolator(TimeInterpolator value);
|
||||
|
||||
/**
|
||||
* Returns the timing interpolator that this animation uses.
|
||||
*
|
||||
* @return The timing interpolator for this animation.
|
||||
*/
|
||||
public TimeInterpolator getInterpolator() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this Animator is currently running (having been started and gone past any
|
||||
* initial startDelay period and not yet ended).
|
||||
*
|
||||
* @return Whether the Animator is running.
|
||||
*/
|
||||
public abstract boolean isRunning();
|
||||
|
||||
/**
|
||||
* Returns whether this Animator has been started and not yet ended. For reusable
|
||||
* Animators (which most Animators are, apart from the one-shot animator produced by
|
||||
* {@link android.view.ViewAnimationUtils#createCircularReveal(
|
||||
* android.view.View, int, int, float, float) createCircularReveal()}),
|
||||
* this state is a superset of {@link #isRunning()}, because an Animator with a
|
||||
* nonzero {@link #getStartDelay() startDelay} will return true for {@link #isStarted()} during
|
||||
* the delay phase, whereas {@link #isRunning()} will return true only after the delay phase
|
||||
* is complete. Non-reusable animators will always return true after they have been
|
||||
* started, because they cannot return to a non-started state.
|
||||
*
|
||||
* @return Whether the Animator has been started and not yet ended.
|
||||
*/
|
||||
public boolean isStarted() {
|
||||
// Default method returns value for isRunning(). Subclasses should override to return a
|
||||
// real value.
|
||||
return isRunning();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a listener to the set of listeners that are sent events through the life of an
|
||||
* animation, such as start, repeat, and end.
|
||||
*
|
||||
* @param listener the listener to be added to the current set of listeners for this animation.
|
||||
*/
|
||||
public void addListener(AnimatorListener listener) {
|
||||
this.listener = listener;
|
||||
if (mListeners == null) {
|
||||
mListeners = new ArrayList<AnimatorListener>();
|
||||
}
|
||||
mListeners.add(listener);
|
||||
}
|
||||
|
||||
public void cancel() {}
|
||||
|
||||
public long getStartDelay() { return 0; }
|
||||
|
||||
public long getDuration() { return duration; }
|
||||
|
||||
public Animator setDuration(long duration) {
|
||||
this.duration = duration;
|
||||
return this;
|
||||
/**
|
||||
* Removes a listener from the set listening to this animation.
|
||||
*
|
||||
* @param listener the listener to be removed from the current set of listeners for this
|
||||
* animation.
|
||||
*/
|
||||
public void removeListener(AnimatorListener listener) {
|
||||
if (mListeners == null) {
|
||||
return;
|
||||
}
|
||||
mListeners.remove(listener);
|
||||
if (mListeners.size() == 0) {
|
||||
mListeners = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void setInterpolator(TimeInterpolator i) {}
|
||||
/**
|
||||
* Gets the set of {@link android.animation.Animator.AnimatorListener} objects that are currently
|
||||
* listening for events on this <code>Animator</code> object.
|
||||
*
|
||||
* @return ArrayList<AnimatorListener> The set of listeners.
|
||||
*/
|
||||
public ArrayList<AnimatorListener> getListeners() {
|
||||
return mListeners;
|
||||
}
|
||||
|
||||
public void setStartDelay(long startDelay) {}
|
||||
/**
|
||||
* Adds a pause listener to this animator.
|
||||
*
|
||||
* @param listener the listener to be added to the current set of pause listeners
|
||||
* for this animation.
|
||||
*/
|
||||
public void addPauseListener(AnimatorPauseListener listener) {
|
||||
if (mPauseListeners == null) {
|
||||
mPauseListeners = new ArrayList<AnimatorPauseListener>();
|
||||
}
|
||||
mPauseListeners.add(listener);
|
||||
}
|
||||
|
||||
public boolean isStarted() { return false; }
|
||||
/**
|
||||
* Removes a pause listener from the set listening to this animation.
|
||||
*
|
||||
* @param listener the listener to be removed from the current set of pause
|
||||
* listeners for this animation.
|
||||
*/
|
||||
public void removePauseListener(AnimatorPauseListener listener) {
|
||||
if (mPauseListeners == null) {
|
||||
return;
|
||||
}
|
||||
mPauseListeners.remove(listener);
|
||||
if (mPauseListeners.size() == 0) {
|
||||
mPauseListeners = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void end() {}
|
||||
/**
|
||||
* Removes all {@link #addListener(android.animation.Animator.AnimatorListener) listeners}
|
||||
* and {@link #addPauseListener(android.animation.Animator.AnimatorPauseListener)
|
||||
* pauseListeners} from this object.
|
||||
*/
|
||||
public void removeAllListeners() {
|
||||
if (mListeners != null) {
|
||||
mListeners.clear();
|
||||
mListeners = null;
|
||||
}
|
||||
if (mPauseListeners != null) {
|
||||
mPauseListeners.clear();
|
||||
mPauseListeners = null;
|
||||
}
|
||||
}
|
||||
|
||||
public TimeInterpolator getInterpolator() { return null; }
|
||||
/**
|
||||
* Return a mask of the configuration parameters for which this animator may change, requiring
|
||||
* that it should be re-created from Resources. The default implementation returns whatever
|
||||
* value was provided through setChangingConfigurations(int) or 0 by default.
|
||||
*
|
||||
* @return Returns a mask of the changing configuration parameters, as defined by
|
||||
* {@link android.content.pm.ActivityInfo}.
|
||||
* @see android.content.pm.ActivityInfo
|
||||
* @hide
|
||||
*/
|
||||
public int getChangingConfigurations() {
|
||||
return mChangingConfigurations;
|
||||
}
|
||||
|
||||
public boolean isRunning() { return false; }
|
||||
/**
|
||||
* Set a mask of the configuration parameters for which this animator may change, requiring
|
||||
* that it be re-created from resource.
|
||||
*
|
||||
* @param configs A mask of the changing configuration parameters, as
|
||||
* defined by {@link android.content.pm.ActivityInfo}.
|
||||
*
|
||||
* @see android.content.pm.ActivityInfo
|
||||
* @hide
|
||||
*/
|
||||
public void setChangingConfigurations(int configs) {
|
||||
mChangingConfigurations = configs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the changing configurations value to the union of the current changing configurations
|
||||
* and the provided configs.
|
||||
* This method is called while loading the animator.
|
||||
* @hide
|
||||
*/
|
||||
public void appendChangingConfigurations(int configs) {
|
||||
mChangingConfigurations |= configs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link android.content.res.ConstantState} instance that holds the shared state of
|
||||
* this Animator.
|
||||
* <p>
|
||||
* This constant state is used to create new instances of this animator when needed, instead
|
||||
* of re-loading it from resources. Default implementation creates a new
|
||||
* {@link AnimatorConstantState}. You can override this method to provide your custom logic or
|
||||
* return null if you don't want this animator to be cached.
|
||||
*
|
||||
* @return The ConfigurationBoundResourceCache.BaseConstantState associated to this Animator.
|
||||
* @see android.content.res.ConstantState
|
||||
* @see #clone()
|
||||
* @hide
|
||||
*/
|
||||
public ConstantState<Animator> createConstantState() {
|
||||
return new AnimatorConstantState(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Animator clone() {
|
||||
try {
|
||||
final Animator anim = (Animator) super.clone();
|
||||
if (mListeners != null) {
|
||||
anim.mListeners = new ArrayList<AnimatorListener>(mListeners);
|
||||
}
|
||||
if (mPauseListeners != null) {
|
||||
anim.mPauseListeners = new ArrayList<AnimatorPauseListener>(mPauseListeners);
|
||||
}
|
||||
anim.mCachedList.set(null);
|
||||
anim.mStartListenersCalled = false;
|
||||
return anim;
|
||||
} catch (CloneNotSupportedException e) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method tells the object to use appropriate information to extract
|
||||
* starting values for the animation. For example, a AnimatorSet object will pass
|
||||
* this call to its child objects to tell them to set up the values. A
|
||||
* ObjectAnimator object will use the information it has about its target object
|
||||
* and PropertyValuesHolder objects to get the start values for its properties.
|
||||
* A ValueAnimator object will ignore the request since it does not have enough
|
||||
* information (such as a target object) to gather these values.
|
||||
*/
|
||||
public void setupStartValues() {
|
||||
}
|
||||
|
||||
/**
|
||||
* This method tells the object to use appropriate information to extract
|
||||
* ending values for the animation. For example, a AnimatorSet object will pass
|
||||
* this call to its child objects to tell them to set up the values. A
|
||||
* ObjectAnimator object will use the information it has about its target object
|
||||
* and PropertyValuesHolder objects to get the start values for its properties.
|
||||
* A ValueAnimator object will ignore the request since it does not have enough
|
||||
* information (such as a target object) to gather these values.
|
||||
*/
|
||||
public void setupEndValues() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the target object whose property will be animated by this animation. Not all subclasses
|
||||
* operate on target objects (for example, {@link ValueAnimator}, but this method
|
||||
* is on the superclass for the convenience of dealing generically with those subclasses
|
||||
* that do handle targets.
|
||||
* <p>
|
||||
* <strong>Note:</strong> The target is stored as a weak reference internally to avoid leaking
|
||||
* resources by having animators directly reference old targets. Therefore, you should
|
||||
* ensure that animator targets always have a hard reference elsewhere.
|
||||
*
|
||||
* @param target The object being animated
|
||||
*/
|
||||
public void setTarget(@Nullable Object target) {
|
||||
}
|
||||
|
||||
// Hide reverse() and canReverse() for now since reverse() only work for simple
|
||||
// cases, like we don't support sequential, neither startDelay.
|
||||
// TODO: make reverse() works for all the Animators.
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public boolean canReverse() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public void reverse() {
|
||||
throw new IllegalStateException("Reverse is not supported");
|
||||
}
|
||||
|
||||
// Pulse an animation frame into the animation.
|
||||
boolean pulseAnimationFrame(long frameTime) {
|
||||
// TODO: Need to find a better signal than this. There's a bug in SystemUI that's preventing
|
||||
// returning !isStarted() from working.
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal use only.
|
||||
* This call starts the animation in regular or reverse direction without requiring them to
|
||||
* register frame callbacks. The caller will be responsible for all the subsequent animation
|
||||
* pulses. Specifically, the caller needs to call doAnimationFrame(...) for the animation on
|
||||
* every frame.
|
||||
*
|
||||
* @param inReverse whether the animation should play in reverse direction
|
||||
*/
|
||||
void startWithoutPulsing(boolean inReverse) {
|
||||
if (inReverse) {
|
||||
reverse();
|
||||
} else {
|
||||
start();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal use only.
|
||||
* Skips the animation value to end/start, depending on whether the play direction is forward
|
||||
* or backward.
|
||||
*
|
||||
* @param inReverse whether the end value is based on a reverse direction. If yes, this is
|
||||
* equivalent to skip to start value in a forward playing direction.
|
||||
*/
|
||||
void skipToEndValue(boolean inReverse) {}
|
||||
|
||||
/**
|
||||
* Internal use only.
|
||||
*
|
||||
* Returns whether the animation has start/end values setup. For most of the animations, this
|
||||
* should always be true. For ObjectAnimators, the start values are setup in the initialization
|
||||
* of the animation.
|
||||
*/
|
||||
boolean isInitialized() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal use only. Changes the value of the animator as if currentPlayTime has passed since
|
||||
* the start of the animation. Therefore, currentPlayTime includes the start delay, and any
|
||||
* repetition. lastPlayTime is similar and is used to calculate how many repeats have been
|
||||
* done between the two times.
|
||||
*/
|
||||
void animateValuesInRange(long currentPlayTime, long lastPlayTime) {}
|
||||
|
||||
/**
|
||||
* Internal use only. This animates any animation that has ended since lastPlayTime.
|
||||
* If an animation hasn't been finished, no change will be made.
|
||||
*/
|
||||
void animateSkipToEnds(long currentPlayTime, long lastPlayTime) {}
|
||||
|
||||
/**
|
||||
* Internal use only. Adds all start times (after delay) to and end times to times.
|
||||
* The value must include offset.
|
||||
*/
|
||||
void getStartAndEndTimes(LongArray times, long offset) {
|
||||
long startTime = offset + getStartDelay();
|
||||
if (times.indexOf(startTime) < 0) {
|
||||
times.add(startTime);
|
||||
}
|
||||
long duration = getTotalDuration();
|
||||
if (duration != DURATION_INFINITE) {
|
||||
long endTime = duration + offset;
|
||||
if (times.indexOf(endTime) < 0) {
|
||||
times.add(endTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls notification for each AnimatorListener.
|
||||
*
|
||||
* @param notification The notification method to call on each listener.
|
||||
* @param isReverse When this is used with start/end, this is the isReverse parameter. For
|
||||
* other calls, this is ignored.
|
||||
*/
|
||||
void notifyListeners(
|
||||
AnimatorCaller<AnimatorListener, Animator> notification,
|
||||
boolean isReverse
|
||||
) {
|
||||
callOnList(mListeners, notification, this, isReverse);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call pause/resume on each AnimatorPauseListener.
|
||||
*
|
||||
* @param notification Either ON_PAUSE or ON_RESUME to call onPause or onResume on each
|
||||
* listener.
|
||||
*/
|
||||
void notifyPauseListeners(AnimatorCaller<AnimatorPauseListener, Animator> notification) {
|
||||
callOnList(mPauseListeners, notification, this, false);
|
||||
}
|
||||
|
||||
void notifyStartListeners(boolean isReversing) {
|
||||
boolean startListenersCalled = mStartListenersCalled;
|
||||
mStartListenersCalled = true;
|
||||
if (mListeners != null && !startListenersCalled) {
|
||||
notifyListeners(AnimatorCaller.ON_START, isReversing);
|
||||
}
|
||||
}
|
||||
|
||||
void notifyEndListeners(boolean isReversing) {
|
||||
boolean startListenersCalled = mStartListenersCalled;
|
||||
mStartListenersCalled = false;
|
||||
if (mListeners != null && startListenersCalled) {
|
||||
notifyListeners(AnimatorCaller.ON_END, isReversing);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls <code>call</code> for every item in <code>list</code> with <code>animator</code> and
|
||||
* <code>isReverse</code> as parameters.
|
||||
*
|
||||
* @param list The list of items to make calls on.
|
||||
* @param call The method to call for each item in list.
|
||||
* @param animator The animator parameter of call.
|
||||
* @param isReverse The isReverse parameter of call.
|
||||
* @param <T> The item type of list
|
||||
* @param <A> The Animator type of animator.
|
||||
*/
|
||||
<T, A> void callOnList(
|
||||
ArrayList<T> list,
|
||||
AnimatorCaller<T, A> 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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>An animation listener receives notifications from an animation.
|
||||
* Notifications indicate animation related events, such as the end or the
|
||||
* repetition of the animation.</p>
|
||||
*/
|
||||
public static interface AnimatorListener {
|
||||
|
||||
/**
|
||||
* <p>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);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Notifies the end of the animation. This callback is not invoked
|
||||
* for animations with repeat count set to INFINITE.</p>
|
||||
*
|
||||
* <p>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);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Notifies the start of the animation.</p>
|
||||
*
|
||||
* @param animation The started animation.
|
||||
*/
|
||||
void onAnimationStart(@NonNull Animator animation);
|
||||
|
||||
/**
|
||||
* <p>Notifies the end of the animation. This callback is not invoked
|
||||
* for animations with repeat count set to INFINITE.</p>
|
||||
*
|
||||
* @param animation The animation which reached its end.
|
||||
*/
|
||||
void onAnimationEnd(@NonNull Animator animation);
|
||||
|
||||
/**
|
||||
* <p>Notifies the cancellation of the animation. This callback is not invoked
|
||||
* for animations with repeat count set to INFINITE.</p>
|
||||
*
|
||||
* @param animation The animation which was canceled.
|
||||
*/
|
||||
void onAnimationCancel(@NonNull Animator animation);
|
||||
|
||||
/**
|
||||
* <p>Notifies the repetition of the animation.</p>
|
||||
*
|
||||
* @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 {
|
||||
/**
|
||||
* <p>Notifies that the animation was paused.</p>
|
||||
*
|
||||
* @param animation The animaton being paused.
|
||||
* @see #pause()
|
||||
*/
|
||||
void onAnimationPause(@NonNull Animator animation);
|
||||
|
||||
/**
|
||||
* <p>Notifies that the animation was resumed, after being
|
||||
* previously paused.</p>
|
||||
*
|
||||
* @param animation The animation being resumed.
|
||||
* @see #resume()
|
||||
*/
|
||||
void onAnimationResume(@NonNull Animator animation);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>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.
|
||||
*
|
||||
* <p>Regardless of whether or not the animation runs asynchronously, all
|
||||
* listener callbacks will be called on the UI thread.</p>
|
||||
*
|
||||
* <p>To be able to use this hint the following must be true:</p>
|
||||
* <ol>
|
||||
* <li>The animator is immutable while {@link #isStarted()} is true. Requests
|
||||
* to change duration, delay, etc... may be ignored.</li>
|
||||
* <li>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.</li>
|
||||
* <li>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.</li>
|
||||
* </ol>
|
||||
* @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.
|
||||
* <p>
|
||||
* When {@link #newInstance()} is called, default implementation clones the Animator.
|
||||
*/
|
||||
private static class AnimatorConstantState extends ConstantState<Animator> {
|
||||
|
||||
final Animator mAnimator;
|
||||
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 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 <T> The type of listener to make the call on
|
||||
* @param <A> The type of animator that is passed as a parameter
|
||||
*/
|
||||
interface AnimatorCaller<T, A> {
|
||||
void call(T listener, A animator, boolean isReverse);
|
||||
|
||||
AnimatorCaller<AnimatorListener, Animator> ON_START = new AnimatorCaller<Animator.AnimatorListener,Animator>() {
|
||||
@Override
|
||||
public void call(AnimatorListener listener, Animator animator, boolean isReverse) {
|
||||
listener.onAnimationStart(animator);
|
||||
}
|
||||
};
|
||||
AnimatorCaller<AnimatorListener, Animator> ON_END = new AnimatorCaller<Animator.AnimatorListener,Animator>() {
|
||||
@Override
|
||||
public void call(AnimatorListener listener, Animator animator, boolean isReverse) {
|
||||
listener.onAnimationEnd(animator);
|
||||
}
|
||||
};
|
||||
AnimatorCaller<AnimatorListener, Animator> ON_CANCEL =
|
||||
new AnimatorCaller<AnimatorListener, Animator>() { @Override public void call(AnimatorListener listener, Animator animator, boolean isReverse) { listener.onAnimationCancel(animator); }};
|
||||
AnimatorCaller<AnimatorListener, Animator> ON_REPEAT =
|
||||
new AnimatorCaller<AnimatorListener, Animator>() { @Override public void call(AnimatorListener listener, Animator animator, boolean isReverse) { listener.onAnimationRepeat(animator); }};
|
||||
AnimatorCaller<AnimatorPauseListener, Animator> ON_PAUSE =
|
||||
new AnimatorCaller<AnimatorPauseListener, Animator>() { @Override public void call(AnimatorPauseListener listener, Animator animator, boolean isReverse) { listener.onAnimationPause(animator); }};
|
||||
AnimatorCaller<AnimatorPauseListener, Animator> ON_RESUME =
|
||||
new AnimatorCaller<AnimatorPauseListener, Animator>() { @Override public void call(AnimatorPauseListener listener, Animator animator, boolean isReverse) { listener.onAnimationResume(animator); }};
|
||||
AnimatorCaller<ValueAnimator.AnimatorUpdateListener, ValueAnimator> ON_UPDATE =
|
||||
new AnimatorCaller<ValueAnimator.AnimatorUpdateListener, ValueAnimator>() {
|
||||
@Override
|
||||
public void call(ValueAnimator.AnimatorUpdateListener listener, ValueAnimator animator, boolean isReverse) {
|
||||
listener.onAnimationUpdate(animator);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,5 +1,9 @@
|
|||
package android.animation;
|
||||
|
||||
public class ArgbEvaluator {
|
||||
public class ArgbEvaluator implements TypeEvaluator {
|
||||
|
||||
public static ArgbEvaluator getInstance() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,46 +1,648 @@
|
|||
/*
|
||||
* Copyright (C) 2010 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 android.animation;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.PointF;
|
||||
import android.util.Log;
|
||||
import android.util.Property;
|
||||
import android.view.animation.AccelerateDecelerateInterpolator;
|
||||
|
||||
public class ObjectAnimator extends ValueAnimator {
|
||||
/**
|
||||
* This subclass of {@link ValueAnimator} provides support for animating properties on target objects.
|
||||
* The constructors of this class take parameters to define the target object that will be animated
|
||||
* as well as the name of the property that will be animated. Appropriate set/get functions
|
||||
* are then determined internally and the animation will call these functions as necessary to
|
||||
* animate the property.
|
||||
*
|
||||
* <p>Animators can be created from either code or resource files, as shown here:</p>
|
||||
*
|
||||
* {@sample development/samples/ApiDemos/res/anim/object_animator.xml ObjectAnimatorResources}
|
||||
*
|
||||
* <p>Starting from API 23, it is possible to use {@link PropertyValuesHolder} and
|
||||
* {@link Keyframe} in resource files to create more complex animations. Using PropertyValuesHolders
|
||||
* allows animators to animate several properties in parallel, as shown in this sample:</p>
|
||||
*
|
||||
* {@sample development/samples/ApiDemos/res/anim/object_animator_pvh.xml
|
||||
* PropertyValuesHolderResources}
|
||||
*
|
||||
* <p>Using Keyframes allows animations to follow more complex paths from the start
|
||||
* to the end values. Note that you can specify explicit fractional values (from 0 to 1) for
|
||||
* each keyframe to determine when, in the overall duration, the animation should arrive at that
|
||||
* value. Alternatively, you can leave the fractions off and the keyframes will be equally
|
||||
* distributed within the total duration. Also, a keyframe with no value will derive its value
|
||||
* from the target object when the animator starts, just like animators with only one
|
||||
* value specified. In addition, an optional interpolator can be specified. The interpolator will
|
||||
* be applied on the interval between the keyframe that the interpolator is set on and the previous
|
||||
* keyframe. When no interpolator is supplied, the default {@link AccelerateDecelerateInterpolator}
|
||||
* will be used. </p>
|
||||
*
|
||||
* {@sample development/samples/ApiDemos/res/anim/object_animator_pvh_kf_interpolated.xml KeyframeResources}
|
||||
*
|
||||
* <div class="special reference">
|
||||
* <h3>Developer Guides</h3>
|
||||
* <p>For more information about animating with {@code ObjectAnimator}, read the
|
||||
* <a href="{@docRoot}guide/topics/graphics/prop-animation.html#object-animator">Property
|
||||
* Animation</a> developer guide.</p>
|
||||
* </div>
|
||||
*
|
||||
* @see #setPropertyName(String)
|
||||
*
|
||||
*/
|
||||
public final class ObjectAnimator extends ValueAnimator {
|
||||
private static final String LOG_TAG = "ObjectAnimator";
|
||||
|
||||
private static final boolean DBG = false;
|
||||
|
||||
private Object mTarget;
|
||||
|
||||
private String mPropertyName;
|
||||
|
||||
private Property mProperty;
|
||||
|
||||
private boolean mAutoCancel = false;
|
||||
|
||||
/**
|
||||
* Sets the name of the property that will be animated. This name is used to derive
|
||||
* a setter function that will be called to set animated values.
|
||||
* For example, a property name of <code>foo</code> will result
|
||||
* in a call to the function <code>setFoo()</code> on the target object. If either
|
||||
* <code>valueFrom</code> or <code>valueTo</code> is null, then a getter function will
|
||||
* also be derived and called.
|
||||
*
|
||||
* <p>For best performance of the mechanism that calls the setter function determined by the
|
||||
* name of the property being animated, use <code>float</code> or <code>int</code> typed values,
|
||||
* and make the setter function for those properties have a <code>void</code> return value. This
|
||||
* will cause the code to take an optimized path for these constrained circumstances. Other
|
||||
* property types and return types will work, but will have more overhead in processing
|
||||
* the requests due to normal reflection mechanisms.</p>
|
||||
*
|
||||
* <p>Note that the setter function derived from this property name
|
||||
* must take the same parameter type as the
|
||||
* <code>valueFrom</code> and <code>valueTo</code> properties, otherwise the call to
|
||||
* the setter function will fail.</p>
|
||||
*
|
||||
* <p>If this ObjectAnimator has been set up to animate several properties together,
|
||||
* using more than one PropertyValuesHolder objects, then setting the propertyName simply
|
||||
* sets the propertyName in the first of those PropertyValuesHolder objects.</p>
|
||||
*
|
||||
* @param propertyName The name of the property being animated. Should not be null.
|
||||
*/
|
||||
public void setPropertyName(@NonNull String propertyName) {
|
||||
// mValues could be null if this is being constructed piecemeal. Just record the
|
||||
// propertyName to be used later when setValues() is called if so.
|
||||
if (mValues != null) {
|
||||
PropertyValuesHolder valuesHolder = mValues[0];
|
||||
String oldName = valuesHolder.getProperty_name();
|
||||
valuesHolder.setProperty_name(propertyName);
|
||||
mValuesMap.remove(oldName);
|
||||
mValuesMap.put(propertyName, valuesHolder);
|
||||
}
|
||||
mPropertyName = propertyName;
|
||||
// New property/values/target should cause re-initialization prior to starting
|
||||
mInitialized = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the property that will be animated. Property objects will take precedence over
|
||||
* properties specified by the {@link #setPropertyName(String)} method. Animations should
|
||||
* be set up to use one or the other, not both.
|
||||
*
|
||||
* @param property The property being animated. Should not be null.
|
||||
*/
|
||||
public void setProperty(@NonNull Property property) {
|
||||
// mValues could be null if this is being constructed piecemeal. Just record the
|
||||
// propertyName to be used later when setValues() is called if so.
|
||||
if (mValues != null) {
|
||||
PropertyValuesHolder valuesHolder = mValues[0];
|
||||
String oldName = valuesHolder.getProperty_name();
|
||||
valuesHolder.setProperty(property);
|
||||
mValuesMap.remove(oldName);
|
||||
mValuesMap.put(mPropertyName, valuesHolder);
|
||||
}
|
||||
if (mProperty != null) {
|
||||
mPropertyName = property.getName();
|
||||
}
|
||||
mProperty = property;
|
||||
// New property/values/target should cause re-initialization prior to starting
|
||||
mInitialized = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of the property that will be animated. This name will be used to derive
|
||||
* a setter function that will be called to set animated values.
|
||||
* For example, a property name of <code>foo</code> will result
|
||||
* in a call to the function <code>setFoo()</code> on the target object. If either
|
||||
* <code>valueFrom</code> or <code>valueTo</code> is null, then a getter function will
|
||||
* also be derived and called.
|
||||
*
|
||||
* <p>If this animator was created with a {@link Property} object instead of the
|
||||
* string name of a property, then this method will return the {@link
|
||||
* Property#getName() name} of that Property object instead. If this animator was
|
||||
* created with one or more {@link PropertyValuesHolder} objects, then this method
|
||||
* will return the {@link PropertyValuesHolder#getProperty_name() name} of that
|
||||
* object (if there was just one) or a comma-separated list of all of the
|
||||
* names (if there are more than one).</p>
|
||||
*/
|
||||
@Nullable
|
||||
public String getPropertyName() {
|
||||
return null;
|
||||
String propertyName = null;
|
||||
if (mPropertyName != null) {
|
||||
propertyName = mPropertyName;
|
||||
} else if (mProperty != null) {
|
||||
propertyName = mProperty.getName();
|
||||
} else if (mValues != null && mValues.length > 0) {
|
||||
for (int i = 0; i < mValues.length; ++i) {
|
||||
if (i == 0) {
|
||||
propertyName = "";
|
||||
} else {
|
||||
propertyName += ",";
|
||||
}
|
||||
propertyName += mValues[i].getProperty_name();
|
||||
}
|
||||
}
|
||||
return propertyName;
|
||||
}
|
||||
|
||||
public static ObjectAnimator ofFloat(Object target, String xPropertyName, String yPropertyName, Path path) {
|
||||
return new ObjectAnimator();
|
||||
@Override
|
||||
String getNameForTrace() {
|
||||
return "animator:" + getPropertyName();
|
||||
}
|
||||
|
||||
public static <T> ObjectAnimator ofFloat(T target, Property<T, Float> xProperty, Property<T, Float> yProperty, Path path) {
|
||||
return new ObjectAnimator();
|
||||
/**
|
||||
* Creates a new ObjectAnimator object. This default constructor is primarily for
|
||||
* use internally; the other constructors which take parameters are more generally
|
||||
* useful.
|
||||
*/
|
||||
public ObjectAnimator() {
|
||||
}
|
||||
|
||||
public static <T> ObjectAnimator ofFloat(T target, Property<T, Float> property, float... values) {
|
||||
return new ObjectAnimator();
|
||||
/**
|
||||
* Private utility constructor that initializes the target object and name of the
|
||||
* property being animated.
|
||||
*
|
||||
* @param target The object whose property is to be animated. This object should
|
||||
* have a public method on it called <code>setName()</code>, where <code>name</code> is
|
||||
* the value of the <code>propertyName</code> parameter.
|
||||
* @param propertyName The name of the property being animated.
|
||||
*/
|
||||
private ObjectAnimator(Object target, String propertyName) {
|
||||
setTarget(target);
|
||||
setPropertyName(propertyName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Private utility constructor that initializes the target object and property being animated.
|
||||
*
|
||||
* @param target The object whose property is to be animated.
|
||||
* @param property The property being animated.
|
||||
*/
|
||||
private <T> ObjectAnimator(T target, Property<T, ?> property) {
|
||||
setTarget(target);
|
||||
setProperty(property);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs and returns an ObjectAnimator that animates between int values. A single
|
||||
* value implies that that value is the one being animated to, in which case the start value
|
||||
* will be derived from the property being animated and the target object when {@link #start()}
|
||||
* is called for the first time. Two values imply starting and ending values. More than two
|
||||
* values imply a starting value, values to animate through along the way, and an ending value
|
||||
* (these values will be distributed evenly across the duration of the animation).
|
||||
*
|
||||
* @param target The object whose property is to be animated. This object should
|
||||
* have a public method on it called <code>setName()</code>, where <code>name</code> is
|
||||
* the value of the <code>propertyName</code> parameter.
|
||||
* @param propertyName The name of the property being animated.
|
||||
* @param values A set of values that the animation will animate between over time.
|
||||
* @return An ObjectAnimator object that is set up to animate between the given values.
|
||||
*/
|
||||
public static ObjectAnimator ofInt(Object target, String propertyName, int... values) {
|
||||
ObjectAnimator anim = new ObjectAnimator(target, propertyName);
|
||||
anim.setIntValues(values);
|
||||
return anim;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs and returns an ObjectAnimator that animates between int values. A single
|
||||
* value implies that that value is the one being animated to, in which case the start value
|
||||
* will be derived from the property being animated and the target object when {@link #start()}
|
||||
* is called for the first time. Two values imply starting and ending values. More than two
|
||||
* values imply a starting value, values to animate through along the way, and an ending value
|
||||
* (these values will be distributed evenly across the duration of the animation).
|
||||
*
|
||||
* @param target The object whose property is to be animated.
|
||||
* @param property The property being animated.
|
||||
* @param values A set of values that the animation will animate between over time.
|
||||
* @return An ObjectAnimator object that is set up to animate between the given values.
|
||||
*/
|
||||
public static <T> ObjectAnimator ofInt(T target, Property<T, Integer> property, int... values) {
|
||||
ObjectAnimator anim = new ObjectAnimator(target, property);
|
||||
anim.setIntValues(values);
|
||||
return anim;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs and returns an ObjectAnimator that animates between color values. A single
|
||||
* value implies that that value is the one being animated to, in which case the start value
|
||||
* will be derived from the property being animated and the target object when {@link #start()}
|
||||
* is called for the first time. Two values imply starting and ending values. More than two
|
||||
* values imply a starting value, values to animate through along the way, and an ending value
|
||||
* (these values will be distributed evenly across the duration of the animation).
|
||||
*
|
||||
* @param target The object whose property is to be animated. This object should
|
||||
* have a public method on it called <code>setName()</code>, where <code>name</code> is
|
||||
* the value of the <code>propertyName</code> parameter.
|
||||
* @param propertyName The name of the property being animated.
|
||||
* @param values A set of values that the animation will animate between over time.
|
||||
* @return An ObjectAnimator object that is set up to animate between the given values.
|
||||
*/
|
||||
public static ObjectAnimator ofArgb(Object target, String propertyName, int... values) {
|
||||
ObjectAnimator animator = ofInt(target, propertyName, values);
|
||||
animator.setEvaluator(ArgbEvaluator.getInstance());
|
||||
return animator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs and returns an ObjectAnimator that animates between color values. A single
|
||||
* value implies that that value is the one being animated to, in which case the start value
|
||||
* will be derived from the property being animated and the target object when {@link #start()}
|
||||
* is called for the first time. Two values imply starting and ending values. More than two
|
||||
* values imply a starting value, values to animate through along the way, and an ending value
|
||||
* (these values will be distributed evenly across the duration of the animation).
|
||||
*
|
||||
* @param target The object whose property is to be animated.
|
||||
* @param property The property being animated.
|
||||
* @param values A set of values that the animation will animate between over time.
|
||||
* @return An ObjectAnimator object that is set up to animate between the given values.
|
||||
*/
|
||||
public static <T> ObjectAnimator ofArgb(T target, Property<T, Integer> property,
|
||||
int... values) {
|
||||
ObjectAnimator animator = ofInt(target, property, values);
|
||||
animator.setEvaluator(ArgbEvaluator.getInstance());
|
||||
return animator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs and returns an ObjectAnimator that animates between float values. A single
|
||||
* value implies that that value is the one being animated to, in which case the start value
|
||||
* will be derived from the property being animated and the target object when {@link #start()}
|
||||
* is called for the first time. Two values imply starting and ending values. More than two
|
||||
* values imply a starting value, values to animate through along the way, and an ending value
|
||||
* (these values will be distributed evenly across the duration of the animation).
|
||||
*
|
||||
* @param target The object whose property is to be animated. This object should
|
||||
* have a public method on it called <code>setName()</code>, where <code>name</code> is
|
||||
* the value of the <code>propertyName</code> parameter.
|
||||
* @param propertyName The name of the property being animated.
|
||||
* @param values A set of values that the animation will animate between over time.
|
||||
* @return An ObjectAnimator object that is set up to animate between the given values.
|
||||
*/
|
||||
public static ObjectAnimator ofFloat(Object target, String propertyName, float... values) {
|
||||
return new ObjectAnimator();
|
||||
ObjectAnimator anim = new ObjectAnimator(target, propertyName);
|
||||
anim.setFloatValues(values);
|
||||
return anim;
|
||||
}
|
||||
|
||||
public static <T> ObjectAnimator ofInt(T target, String propertyName, int... values) throws ReflectiveOperationException {
|
||||
Method setter = target.getClass().getMethod("set" + Character.toUpperCase(propertyName.charAt(0)) + propertyName.substring(1), int.class);
|
||||
setter.invoke(target, values[values.length - 1]);
|
||||
return new ObjectAnimator();
|
||||
/**
|
||||
* Constructs and returns an ObjectAnimator that animates between float values. A single
|
||||
* value implies that that value is the one being animated to, in which case the start value
|
||||
* will be derived from the property being animated and the target object when {@link #start()}
|
||||
* is called for the first time. Two values imply starting and ending values. More than two
|
||||
* values imply a starting value, values to animate through along the way, and an ending value
|
||||
* (these values will be distributed evenly across the duration of the animation).
|
||||
*
|
||||
* @param target The object whose property is to be animated.
|
||||
* @param property The property being animated.
|
||||
* @param values A set of values that the animation will animate between over time.
|
||||
* @return An ObjectAnimator object that is set up to animate between the given values.
|
||||
*/
|
||||
public static <T> ObjectAnimator ofFloat(T target, Property<T, Float> property,
|
||||
float... values) {
|
||||
ObjectAnimator anim = new ObjectAnimator(target, property);
|
||||
anim.setFloatValues(values);
|
||||
return anim;
|
||||
}
|
||||
|
||||
public ObjectAnimator setDuration(long duration) {return this;}
|
||||
|
||||
public void setAutoCancel(boolean autoCancel) {}
|
||||
|
||||
public void setPropertyName(String propertyName) {}
|
||||
|
||||
public static ObjectAnimator ofPropertyValuesHolder(Object target, PropertyValuesHolder... values) {
|
||||
return new ObjectAnimator();
|
||||
/**
|
||||
* Constructs and returns an ObjectAnimator that animates between Object values. A single
|
||||
* value implies that that value is the one being animated to, in which case the start value
|
||||
* will be derived from the property being animated and the target object when {@link #start()}
|
||||
* is called for the first time. Two values imply starting and ending values. More than two
|
||||
* values imply a starting value, values to animate through along the way, and an ending value
|
||||
* (these values will be distributed evenly across the duration of the animation).
|
||||
*
|
||||
* <p><strong>Note:</strong> The values are stored as references to the original
|
||||
* objects, which means that changes to those objects after this method is called will
|
||||
* affect the values on the animator. If the objects will be mutated externally after
|
||||
* this method is called, callers should pass a copy of those objects instead.
|
||||
*
|
||||
* @param target The object whose property is to be animated. This object should
|
||||
* have a public method on it called <code>setName()</code>, where <code>name</code> is
|
||||
* the value of the <code>propertyName</code> parameter.
|
||||
* @param propertyName The name of the property being animated.
|
||||
* @param evaluator A TypeEvaluator that will be called on each animation frame to
|
||||
* provide the necessary interpolation between the Object values to derive the animated
|
||||
* value.
|
||||
* @param values A set of values that the animation will animate between over time.
|
||||
* @return An ObjectAnimator object that is set up to animate between the given values.
|
||||
*/
|
||||
public static ObjectAnimator ofObject(Object target, String propertyName,
|
||||
TypeEvaluator evaluator, Object... values) {
|
||||
ObjectAnimator anim = new ObjectAnimator(target, propertyName);
|
||||
anim.setObjectValues(values);
|
||||
anim.setEvaluator(evaluator);
|
||||
return anim;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs and returns an ObjectAnimator that animates between the sets of values specified
|
||||
* in <code>PropertyValueHolder</code> objects. This variant should be used when animating
|
||||
* several properties at once with the same ObjectAnimator, since PropertyValuesHolder allows
|
||||
* you to associate a set of animation values with a property name.
|
||||
*
|
||||
* @param target The object whose property is to be animated. Depending on how the
|
||||
* PropertyValuesObjects were constructed, the target object should either have the {@link
|
||||
* android.util.Property} objects used to construct the PropertyValuesHolder objects or (if the
|
||||
* PropertyValuesHOlder objects were created with property names) the target object should have
|
||||
* public methods on it called <code>setName()</code>, where <code>name</code> is the name of
|
||||
* the property passed in as the <code>propertyName</code> parameter for each of the
|
||||
* PropertyValuesHolder objects.
|
||||
* @param values A set of PropertyValuesHolder objects whose values will be animated between
|
||||
* over time.
|
||||
* @return An ObjectAnimator object that is set up to animate between the given values.
|
||||
*/
|
||||
@NonNull
|
||||
public static ObjectAnimator ofPropertyValuesHolder(Object target,
|
||||
PropertyValuesHolder... values) {
|
||||
ObjectAnimator anim = new ObjectAnimator();
|
||||
anim.setTarget(target);
|
||||
anim.setValues(values);
|
||||
return anim;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setIntValues(int... values) {
|
||||
if (mValues == null || mValues.length == 0) {
|
||||
// No values yet - this animator is being constructed piecemeal. Init the values with
|
||||
// whatever the current propertyName is
|
||||
if (mProperty != null) {
|
||||
setValues(PropertyValuesHolder.ofInt(mProperty, values));
|
||||
} else {
|
||||
setValues(PropertyValuesHolder.ofInt(mPropertyName, values));
|
||||
}
|
||||
} else {
|
||||
super.setIntValues(values);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFloatValues(float... values) {
|
||||
if (mValues == null || mValues.length == 0) {
|
||||
// No values yet - this animator is being constructed piecemeal. Init the values with
|
||||
// whatever the current propertyName is
|
||||
if (mProperty != null) {
|
||||
setValues(PropertyValuesHolder.ofFloat(mProperty, values));
|
||||
} else {
|
||||
setValues(PropertyValuesHolder.ofFloat(mPropertyName, values));
|
||||
}
|
||||
} else {
|
||||
super.setFloatValues(values);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setObjectValues(Object... values) {
|
||||
if (mValues == null || mValues.length == 0) {
|
||||
// No values yet - this animator is being constructed piecemeal. Init the values with
|
||||
// whatever the current propertyName is
|
||||
if (mProperty != null) {
|
||||
setValues(PropertyValuesHolder.ofObject(mProperty, (TypeEvaluator) null, values));
|
||||
} else {
|
||||
setValues(PropertyValuesHolder.ofObject(mPropertyName,
|
||||
(TypeEvaluator) null, values));
|
||||
}
|
||||
} else {
|
||||
super.setObjectValues(values);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* autoCancel controls whether an ObjectAnimator will be canceled automatically
|
||||
* when any other ObjectAnimator with the same target and properties is started.
|
||||
* Setting this flag may make it easier to run different animators on the same target
|
||||
* object without having to keep track of whether there are conflicting animators that
|
||||
* need to be manually canceled. Canceling animators must have the same exact set of
|
||||
* target properties, in the same order.
|
||||
*
|
||||
* @param cancel Whether future ObjectAnimators with the same target and properties
|
||||
* as this ObjectAnimator will cause this ObjectAnimator to be canceled.
|
||||
*/
|
||||
public void setAutoCancel(boolean cancel) {
|
||||
mAutoCancel = cancel;
|
||||
}
|
||||
|
||||
private boolean hasSameTargetAndProperties(@Nullable Animator anim) {
|
||||
if (anim instanceof ObjectAnimator) {
|
||||
PropertyValuesHolder[] theirValues = ((ObjectAnimator) anim).getValues();
|
||||
if (((ObjectAnimator) anim).getTarget() == getTarget() &&
|
||||
mValues.length == theirValues.length) {
|
||||
for (int i = 0; i < mValues.length; ++i) {
|
||||
PropertyValuesHolder pvhMine = mValues[i];
|
||||
PropertyValuesHolder pvhTheirs = theirValues[i];
|
||||
if (pvhMine.getProperty_name() == null ||
|
||||
!pvhMine.getProperty_name().equals(pvhTheirs.getProperty_name())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
AnimationHandler.getInstance().autoCancelBasedOn(this);
|
||||
if (DBG) {
|
||||
Log.d(LOG_TAG, "Anim target, duration: " + getTarget() + ", " + getDuration());
|
||||
for (int i = 0; i < mValues.length; ++i) {
|
||||
PropertyValuesHolder pvh = mValues[i];
|
||||
// Log.d(LOG_TAG, " Values[" + i + "]: " +
|
||||
// pvh.getPropertyName() + ", " + pvh.mKeyframes.getValue(0) + ", " +
|
||||
// pvh.mKeyframes.getValue(1));
|
||||
}
|
||||
}
|
||||
super.start();
|
||||
}
|
||||
|
||||
boolean shouldAutoCancel(AnimationHandler.AnimationFrameCallback anim) {
|
||||
if (anim == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (anim instanceof ObjectAnimator) {
|
||||
ObjectAnimator objAnim = (ObjectAnimator) anim;
|
||||
if (objAnim.mAutoCancel && hasSameTargetAndProperties(objAnim)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is called immediately before processing the first animation
|
||||
* frame of an animation. If there is a nonzero <code>startDelay</code>, the
|
||||
* function is called after that delay ends.
|
||||
* It takes care of the final initialization steps for the
|
||||
* animation. This includes setting mEvaluator, if the user has not yet
|
||||
* set it up, and the setter/getter methods, if the user did not supply
|
||||
* them.
|
||||
*
|
||||
* <p>Overriders of this method should call the superclass method to cause
|
||||
* internal mechanisms to be set up correctly.</p>
|
||||
*/
|
||||
@Override
|
||||
void initAnimation() {
|
||||
if (!mInitialized) {
|
||||
// mValueType may change due to setter/getter setup; do this before calling super.init(),
|
||||
// which uses mValueType to set up the default type evaluator.
|
||||
final Object target = getTarget();
|
||||
if (target != null) {
|
||||
final int numValues = mValues.length;
|
||||
for (int i = 0; i < numValues; ++i) {
|
||||
mValues[i].setupSetterAndGetter(target);
|
||||
}
|
||||
}
|
||||
super.initAnimation();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the length of the animation. The default duration is 300 milliseconds.
|
||||
*
|
||||
* @param duration The length of the animation, in milliseconds.
|
||||
* @return ObjectAnimator The object called with setDuration(). This return
|
||||
* value makes it easier to compose statements together that construct and then set the
|
||||
* duration, as in
|
||||
* <code>ObjectAnimator.ofInt(target, propertyName, 0, 10).setDuration(500).start()</code>.
|
||||
*/
|
||||
@Override
|
||||
@NonNull
|
||||
public ObjectAnimator setDuration(long duration) {
|
||||
super.setDuration(duration);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The target object whose property will be animated by this animation
|
||||
*
|
||||
* @return The object being animated
|
||||
*/
|
||||
@Nullable
|
||||
public Object getTarget() {
|
||||
return mTarget;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTarget(@Nullable Object target) {
|
||||
final Object oldTarget = getTarget();
|
||||
if (oldTarget != target) {
|
||||
if (isStarted()) {
|
||||
cancel();
|
||||
}
|
||||
mTarget = target;
|
||||
// New target should cause re-initialization prior to starting
|
||||
mInitialized = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setupStartValues() {
|
||||
initAnimation();
|
||||
|
||||
final Object target = getTarget();
|
||||
if (target != null) {
|
||||
final int numValues = mValues.length;
|
||||
for (int i = 0; i < numValues; ++i) {
|
||||
mValues[i].setupStartValue(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setupEndValues() {
|
||||
initAnimation();
|
||||
|
||||
final Object target = getTarget();
|
||||
if (target != null) {
|
||||
final int numValues = mValues.length;
|
||||
for (int i = 0; i < numValues; ++i) {
|
||||
mValues[i].setupEndValue(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called with the elapsed fraction of the animation during every
|
||||
* animation frame. This function turns the elapsed fraction into an interpolated fraction
|
||||
* and then into an animated value (from the evaluator. The function is called mostly during
|
||||
* animation updates, but it is also called when the <code>end()</code>
|
||||
* function is called, to set the final value on the property.
|
||||
*
|
||||
* <p>Overrides of this method must call the superclass to perform the calculation
|
||||
* of the animated value.</p>
|
||||
*
|
||||
* @param fraction The elapsed fraction of the animation.
|
||||
*/
|
||||
@Override
|
||||
void animateValue(float fraction) {
|
||||
final Object target = getTarget();
|
||||
super.animateValue(fraction);
|
||||
int numValues = mValues.length;
|
||||
for (int i = 0; i < numValues; ++i) {
|
||||
mValues[i].setAnimatedValue(target);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isInitialized() {
|
||||
return mInitialized;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObjectAnimator clone() {
|
||||
final ObjectAnimator anim = (ObjectAnimator) super.clone();
|
||||
return anim;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public String toString() {
|
||||
String returnVal = "ObjectAnimator@" + Integer.toHexString(hashCode()) + ", target " +
|
||||
getTarget();
|
||||
if (mValues != null) {
|
||||
for (int i = 0; i < mValues.length; ++i) {
|
||||
returnVal += "\n " + mValues[i].toString();
|
||||
}
|
||||
}
|
||||
return returnVal;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,141 @@
|
|||
package android.animation;
|
||||
|
||||
public class PropertyValuesHolder{
|
||||
import java.lang.reflect.Method;
|
||||
import android.util.Log;
|
||||
import android.util.Property;
|
||||
|
||||
public class PropertyValuesHolder {
|
||||
|
||||
private float values_float[];
|
||||
private int values_int[];
|
||||
private Object values_object[];
|
||||
private Object value;
|
||||
private String property_name;
|
||||
private Method setter;
|
||||
|
||||
public static PropertyValuesHolder ofFloat(String propertyName, float... values) {
|
||||
return null;
|
||||
PropertyValuesHolder propertyValuesHolder = new PropertyValuesHolder();
|
||||
propertyValuesHolder.values_float = values;
|
||||
propertyValuesHolder.property_name = propertyName;
|
||||
return propertyValuesHolder;
|
||||
}
|
||||
|
||||
public static PropertyValuesHolder ofObject(String propertyName, TypeEvaluator evaluator, Object... values) {
|
||||
PropertyValuesHolder propertyValuesHolder = new PropertyValuesHolder();
|
||||
propertyValuesHolder.values_object = values;
|
||||
propertyValuesHolder.property_name = propertyName;
|
||||
return propertyValuesHolder;
|
||||
}
|
||||
|
||||
public static PropertyValuesHolder ofInt(String propertyName, int... values) {
|
||||
PropertyValuesHolder propertyValuesHolder = new PropertyValuesHolder();
|
||||
propertyValuesHolder.values_int = values;
|
||||
propertyValuesHolder.property_name = propertyName;
|
||||
return propertyValuesHolder;
|
||||
}
|
||||
|
||||
public static PropertyValuesHolder ofFloat(Property property, float... values) {
|
||||
PropertyValuesHolder propertyValuesHolder = new PropertyValuesHolder();
|
||||
propertyValuesHolder.values_float = values;
|
||||
propertyValuesHolder.property_name = property.getName();
|
||||
return propertyValuesHolder;
|
||||
}
|
||||
|
||||
public static PropertyValuesHolder ofObject(Property property, TypeEvaluator evaluator, Object... values) {
|
||||
PropertyValuesHolder propertyValuesHolder = new PropertyValuesHolder();
|
||||
propertyValuesHolder.values_object = values;
|
||||
propertyValuesHolder.property_name = property.getName();
|
||||
return propertyValuesHolder;
|
||||
}
|
||||
|
||||
public static PropertyValuesHolder ofInt(Property property, int... values) {
|
||||
PropertyValuesHolder propertyValuesHolder = new PropertyValuesHolder();
|
||||
propertyValuesHolder.values_int = values;
|
||||
propertyValuesHolder.property_name = property.getName();
|
||||
return propertyValuesHolder;
|
||||
}
|
||||
|
||||
public void setIntValues(int... values) {
|
||||
values_int = values;
|
||||
}
|
||||
|
||||
public void setFloatValues(float... values) {
|
||||
values_float = values;
|
||||
}
|
||||
|
||||
public void setObjectValues(Object... values) {
|
||||
values_object = values;
|
||||
}
|
||||
|
||||
public String getProperty_name() {
|
||||
return property_name;
|
||||
}
|
||||
|
||||
public void setProperty_name(String propertyName) {
|
||||
this.property_name = propertyName;
|
||||
}
|
||||
|
||||
public void setProperty(Property property) {
|
||||
property_name = property.getName();
|
||||
}
|
||||
|
||||
public void init() {}
|
||||
|
||||
public Object getAnimatedValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setEvaluator(TypeEvaluator value) {}
|
||||
|
||||
public void calculateValue(float fraction) {
|
||||
if (values_object != null) {
|
||||
value = values_object[(int) (fraction * (values_object.length - 1) + 0.5f)];
|
||||
} else if (values_float != null) {
|
||||
int i = (int) (fraction * (values_float.length - 1));
|
||||
float f = fraction * (values_float.length - 1) - i;
|
||||
value = values_float[i] * (1 - f) + ((f!=0.f) ? values_float[i + 1] * f : 0.f);
|
||||
} else if (values_int != null) {
|
||||
int i = (int) (fraction * (values_int.length - 1));
|
||||
float f = fraction * (values_int.length - 1) - i;
|
||||
value = (int)(values_int[i] * (1 - f) + ((f!=0.f) ? values_int[i + 1] * f : 0.f) + 0.5f);
|
||||
} else {
|
||||
Log.e("PropertyValuesHolder", "No values set");
|
||||
}
|
||||
}
|
||||
|
||||
public PropertyValuesHolder clone() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void setupSetterAndGetter(Object target) {
|
||||
try {
|
||||
Class<?> clazz;
|
||||
if (values_float != null) {
|
||||
clazz = float.class;
|
||||
} else {
|
||||
clazz = values_object[0].getClass();
|
||||
}
|
||||
setter = target.getClass().getMethod("set" + property_name.substring(0, 1).toUpperCase() + property_name.substring(1), clazz);
|
||||
} catch (NoSuchMethodException e) {
|
||||
Log.e("PropertyValuesHolder", "failed to find setter", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void setupStartValue(Object target) {
|
||||
}
|
||||
|
||||
public void setupEndValue(Object target) {
|
||||
}
|
||||
|
||||
public void setAnimatedValue(Object target) {
|
||||
if (setter != null && value != null) {
|
||||
try {
|
||||
setter.invoke(target, value);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
Log.e("PropertyValuesHolder", "failed to invoke setter", e);
|
||||
}
|
||||
} else {
|
||||
Log.e("PropertyValuesHolder", "no setter or value set");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
229
src/api-impl/android/util/LongArray.java
Normal file
229
src/api-impl/android/util/LongArray.java
Normal file
|
@ -0,0 +1,229 @@
|
|||
/*
|
||||
* Copyright (C) 2013 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 android.util;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import com.android.internal.util.ArrayUtils;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.os.Build;
|
||||
|
||||
/**
|
||||
* Implements a growing array of long primitives.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class LongArray implements Cloneable {
|
||||
private static final int MIN_CAPACITY_INCREMENT = 12;
|
||||
|
||||
private long[] mValues;
|
||||
private int mSize;
|
||||
|
||||
private LongArray(long[] array, int size) {
|
||||
mValues = array;
|
||||
mSize = size; //Preconditions.checkArgumentInRange(size, 0, array.length, "size");
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an empty LongArray with the default initial capacity.
|
||||
*/
|
||||
public LongArray() {
|
||||
this(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an empty LongArray with the specified initial capacity.
|
||||
*/
|
||||
public LongArray(int initialCapacity) {
|
||||
if (initialCapacity == 0) {
|
||||
mValues = new long[0];
|
||||
} else {
|
||||
mValues = ArrayUtils.newUnpaddedLongArray(initialCapacity);
|
||||
}
|
||||
mSize = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an LongArray wrapping the given primitive long array.
|
||||
*/
|
||||
public static LongArray wrap(long[] array) {
|
||||
return new LongArray(array, array.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an LongArray from the given primitive long array, copying it.
|
||||
*/
|
||||
public static LongArray fromArray(long[] array, int size) {
|
||||
return wrap(Arrays.copyOf(array, size));
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the size of this LongArray. If this LongArray is shrinked, the backing array capacity
|
||||
* is unchanged. If the new size is larger than backing array capacity, a new backing array is
|
||||
* created from the current content of this LongArray padded with 0s.
|
||||
*/
|
||||
public void resize(int newSize) {
|
||||
// Preconditions.checkArgumentNonnegative(newSize);
|
||||
if (newSize <= mValues.length) {
|
||||
Arrays.fill(mValues, newSize, mValues.length, 0);
|
||||
} else {
|
||||
ensureCapacity(newSize - mSize);
|
||||
}
|
||||
mSize = newSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends the specified value to the end of this array.
|
||||
*/
|
||||
public void add(long value) {
|
||||
add(mSize, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a value at the specified position in this array. If the specified index is equal to
|
||||
* the length of the array, the value is added at the end.
|
||||
*
|
||||
* @throws IndexOutOfBoundsException when index < 0 || index > size()
|
||||
*/
|
||||
public void add(int index, long value) {
|
||||
ensureCapacity(1);
|
||||
int rightSegment = mSize - index;
|
||||
mSize++;
|
||||
// ArrayUtils.checkBounds(mSize, index);
|
||||
|
||||
if (rightSegment != 0) {
|
||||
// Move by 1 all values from the right of 'index'
|
||||
System.arraycopy(mValues, index, mValues, index + 1, rightSegment);
|
||||
}
|
||||
|
||||
mValues[index] = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the values in the specified array to this array.
|
||||
*/
|
||||
public void addAll(LongArray values) {
|
||||
final int count = values.mSize;
|
||||
ensureCapacity(count);
|
||||
|
||||
System.arraycopy(values.mValues, 0, mValues, mSize, count);
|
||||
mSize += count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures capacity to append at least <code>count</code> values.
|
||||
*/
|
||||
private void ensureCapacity(int count) {
|
||||
final int currentSize = mSize;
|
||||
final int minCapacity = currentSize + count;
|
||||
if (minCapacity >= mValues.length) {
|
||||
final int targetCap = currentSize + (currentSize < (MIN_CAPACITY_INCREMENT / 2) ?
|
||||
MIN_CAPACITY_INCREMENT : currentSize >> 1);
|
||||
final int newCapacity = targetCap > minCapacity ? targetCap : minCapacity;
|
||||
final long[] newValues = ArrayUtils.newUnpaddedLongArray(newCapacity);
|
||||
System.arraycopy(mValues, 0, newValues, 0, currentSize);
|
||||
mValues = newValues;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all values from this array.
|
||||
*/
|
||||
public void clear() {
|
||||
mSize = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LongArray clone() {
|
||||
LongArray clone = null;
|
||||
try {
|
||||
clone = (LongArray) super.clone();
|
||||
clone.mValues = mValues.clone();
|
||||
} catch (CloneNotSupportedException cnse) {
|
||||
/* ignore */
|
||||
}
|
||||
return clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value at the specified position in this array.
|
||||
*/
|
||||
public long get(int index) {
|
||||
// ArrayUtils.checkBounds(mSize, index);
|
||||
return mValues[index];
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value at the specified position in this array.
|
||||
*/
|
||||
public void set(int index, long value) {
|
||||
// ArrayUtils.checkBounds(mSize, index);
|
||||
mValues[index] = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the index of the first occurrence of the specified value in this
|
||||
* array, or -1 if this array does not contain the value.
|
||||
*/
|
||||
public int indexOf(long value) {
|
||||
final int n = mSize;
|
||||
for (int i = 0; i < n; i++) {
|
||||
if (mValues[i] == value) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the value at the specified index from this array.
|
||||
*/
|
||||
public void remove(int index) {
|
||||
// ArrayUtils.checkBounds(mSize, index);
|
||||
System.arraycopy(mValues, index + 1, mValues, index, mSize - index - 1);
|
||||
mSize--;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of values in this array.
|
||||
*/
|
||||
public int size() {
|
||||
return mSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new array with the contents of this LongArray.
|
||||
*/
|
||||
public long[] toArray() {
|
||||
return Arrays.copyOf(mValues, mSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if each element of {@code a} equals corresponding element from {@code b}
|
||||
*/
|
||||
public static boolean elementsEqual(@Nullable LongArray a, @Nullable LongArray b) {
|
||||
if (a == null || b == null) return a == b;
|
||||
if (a.mSize != b.mSize) return false;
|
||||
for (int i = 0; i < a.mSize; i++) {
|
||||
if (a.get(i) != b.get(i)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -1,10 +1,11 @@
|
|||
package android.view;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.ValueAnimator;
|
||||
|
||||
public class ViewAnimationUtils {
|
||||
|
||||
public static Animator createCircularReveal(View view, int centerX, int centerY, float startRadius, float endRadius) {
|
||||
return new Animator();
|
||||
return new ValueAnimator();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package android.view;
|
|||
|
||||
import android.animation.Animator;
|
||||
import android.animation.TimeInterpolator;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.os.Handler;
|
||||
|
||||
public class ViewPropertyAnimator {
|
||||
|
@ -71,7 +72,7 @@ public class ViewPropertyAnimator {
|
|||
@Override
|
||||
public void run() {
|
||||
if (listener != null)
|
||||
listener.onAnimationEnd(new Animator());
|
||||
listener.onAnimationEnd(new ValueAnimator());
|
||||
}
|
||||
}, startDelay+duration);
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ srcs = [
|
|||
'android/R.java',
|
||||
'android/accounts/Account.java',
|
||||
'android/accounts/AccountManager.java',
|
||||
'android/animation/AnimationHandler.java',
|
||||
'android/animation/Animator.java',
|
||||
'android/animation/AnimatorInflater.java',
|
||||
'android/animation/AnimatorListenerAdapter.java',
|
||||
|
@ -436,6 +437,7 @@ srcs = [
|
|||
'android/util/MathUtils.java',
|
||||
'android/util/LayoutDirection.java',
|
||||
'android/util/Log.java',
|
||||
'android/util/LongArray.java',
|
||||
'android/util/LongSparseArray.java',
|
||||
'android/util/LruCache.java',
|
||||
'android/util/MapCollections.java',
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue