/* * Copyright (C) 2011 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.view; import android.animation.Animator; import android.animation.ValueAnimator; import android.animation.TimeInterpolator; import java.util.ArrayList; import java.util.HashMap; import java.util.Set; /** * This class enables automatic and optimized animation of select properties on View objects. * If only one or two properties on a View object are being animated, then using an * {@link android.animation.ObjectAnimator} is fine; the property setters called by ObjectAnimator * are well equipped to do the right thing to set the property and invalidate the view * appropriately. But if several properties are animated simultaneously, or if you just want a * more convenient syntax to animate a specific property, then ViewPropertyAnimator might be * more well-suited to the task. * *
This class may provide better performance for several simultaneous animations, because * it will optimize invalidate calls to take place only once for several properties instead of each * animated property independently causing its own invalidation. Also, the syntax of using this * class could be easier to use because the caller need only tell the View object which * property to animate, and the value to animate either to or by, and this class handles the * details of configuring the underlying Animator class and starting it.
* *This class is not constructed by the caller, but rather by the View whose properties * it will animate. Calls to {@link android.view.View#animate()} will return a reference * to the appropriate ViewPropertyAnimator object for that View.
* */ public class ViewPropertyAnimator { /** * The View whose properties are being animated by this class. This is set at * construction time. */ private final View mView; /** * The duration of the underlying Animator object. By default, we don't set the duration * on the Animator and just use its default duration. If the duration is ever set on this * Animator, then we use the duration that it was set to. */ private long mDuration; /** * A flag indicating whether the duration has been set on this object. If not, we don't set * the duration on the underlying Animator, but instead just use its default duration. */ private boolean mDurationSet = false; /** * The startDelay of the underlying Animator object. By default, we don't set the startDelay * on the Animator and just use its default startDelay. If the startDelay is ever set on this * Animator, then we use the startDelay that it was set to. */ private long mStartDelay = 0; /** * A flag indicating whether the startDelay has been set on this object. If not, we don't set * the startDelay on the underlying Animator, but instead just use its default startDelay. */ private boolean mStartDelaySet = false; /** * The interpolator of the underlying Animator object. By default, we don't set the interpolator * on the Animator and just use its default interpolator. If the interpolator is ever set on * this Animator, then we use the interpolator that it was set to. */ private TimeInterpolator mInterpolator; /** * A flag indicating whether the interpolator has been set on this object. If not, we don't set * the interpolator on the underlying Animator, but instead just use its default interpolator. */ private boolean mInterpolatorSet = false; /** * Listener for the lifecycle events of the underlying ValueAnimator object. */ private Animator.AnimatorListener mListener = null; /** * Listener for the update events of the underlying ValueAnimator object. */ private ValueAnimator.AnimatorUpdateListener mUpdateListener = null; /** * A lazily-created ValueAnimator used in order to get some default animator properties * (duration, start delay, interpolator, etc.). */ private ValueAnimator mTempValueAnimator; /** * This listener is the mechanism by which the underlying Animator causes changes to the * properties currently being animated, as well as the cleanup after an animation is * complete. */ private AnimatorEventListener mAnimatorEventListener = new AnimatorEventListener(); /** * This list holds the properties that have been asked to animate. We allow the caller to * request several animations prior to actually starting the underlying animator. This * enables us to run one single animator to handle several properties in parallel. Each * property is tossed onto the pending list until the animation actually starts (which is * done by posting it onto mView), at which time the pending list is cleared and the properties * on that list are added to the list of properties associated with that animator. */ ArrayListnull
removes any existing listener.
* @return This object, allowing calls to methods in this class to be chained.
*/
public ViewPropertyAnimator setListener(Animator.AnimatorListener listener) {
mListener = listener;
return this;
}
/**
* Sets a listener for update events in the underlying ValueAnimator that runs
* the property animations. Note that the underlying animator is animating between
* 0 and 1 (these values are then turned into the actual property values internally
* by ViewPropertyAnimator). So the animator cannot give information on the current
* values of the properties being animated by this ViewPropertyAnimator, although
* the view object itself can be queried to get the current values.
*
* @see android.animation.ValueAnimator.AnimatorUpdateListener
*
* @param listener The listener to be called with update events. A value of
* null
removes any existing listener.
* @return This object, allowing calls to methods in this class to be chained.
*/
public ViewPropertyAnimator setUpdateListener(ValueAnimator.AnimatorUpdateListener listener) {
mUpdateListener = listener;
return this;
}
/**
* Starts the currently pending property animations immediately. Calling start()
* is optional because all animations start automatically at the next opportunity. However,
* if the animations are needed to start immediately and synchronously (not at the time when
* the next event is processed by the hierarchy, which is when the animations would begin
* otherwise), then this method can be used.
*/
public void start() {
mView.removeCallbacks(mAnimationStarter);
startAnimation();
}
/**
* Cancels all property animations that are currently running or pending.
*/
public void cancel() {
if (mAnimatorMap.size() > 0) {
HashMapx
property to be animated to the
* specified value. Animations already running on the property will be canceled.
*
* @param value The value to be animated to.
* @see View#setX(float)
* @return This object, allowing calls to methods in this class to be chained.
*/
public ViewPropertyAnimator x(float value) {
animateProperty(X, value);
return this;
}
/**
* This method will cause the View's x
property to be animated by the
* specified value. Animations already running on the property will be canceled.
*
* @param value The amount to be animated by, as an offset from the current value.
* @see View#setX(float)
* @return This object, allowing calls to methods in this class to be chained.
*/
public ViewPropertyAnimator xBy(float value) {
animatePropertyBy(X, value);
return this;
}
/**
* This method will cause the View's y
property to be animated to the
* specified value. Animations already running on the property will be canceled.
*
* @param value The value to be animated to.
* @see View#setY(float)
* @return This object, allowing calls to methods in this class to be chained.
*/
public ViewPropertyAnimator y(float value) {
animateProperty(Y, value);
return this;
}
/**
* This method will cause the View's y
property to be animated by the
* specified value. Animations already running on the property will be canceled.
*
* @param value The amount to be animated by, as an offset from the current value.
* @see View#setY(float)
* @return This object, allowing calls to methods in this class to be chained.
*/
public ViewPropertyAnimator yBy(float value) {
animatePropertyBy(Y, value);
return this;
}
/**
* This method will cause the View's rotation
property to be animated to the
* specified value. Animations already running on the property will be canceled.
*
* @param value The value to be animated to.
* @see View#setRotation(float)
* @return This object, allowing calls to methods in this class to be chained.
*/
public ViewPropertyAnimator rotation(float value) {
animateProperty(ROTATION, value);
return this;
}
/**
* This method will cause the View's rotation
property to be animated by the
* specified value. Animations already running on the property will be canceled.
*
* @param value The amount to be animated by, as an offset from the current value.
* @see View#setRotation(float)
* @return This object, allowing calls to methods in this class to be chained.
*/
public ViewPropertyAnimator rotationBy(float value) {
animatePropertyBy(ROTATION, value);
return this;
}
/**
* This method will cause the View's rotationX
property to be animated to the
* specified value. Animations already running on the property will be canceled.
*
* @param value The value to be animated to.
* @see View#setRotationX(float)
* @return This object, allowing calls to methods in this class to be chained.
*/
public ViewPropertyAnimator rotationX(float value) {
animateProperty(ROTATION_X, value);
return this;
}
/**
* This method will cause the View's rotationX
property to be animated by the
* specified value. Animations already running on the property will be canceled.
*
* @param value The amount to be animated by, as an offset from the current value.
* @see View#setRotationX(float)
* @return This object, allowing calls to methods in this class to be chained.
*/
public ViewPropertyAnimator rotationXBy(float value) {
animatePropertyBy(ROTATION_X, value);
return this;
}
/**
* This method will cause the View's rotationY
property to be animated to the
* specified value. Animations already running on the property will be canceled.
*
* @param value The value to be animated to.
* @see View#setRotationY(float)
* @return This object, allowing calls to methods in this class to be chained.
*/
public ViewPropertyAnimator rotationY(float value) {
animateProperty(ROTATION_Y, value);
return this;
}
/**
* This method will cause the View's rotationY
property to be animated by the
* specified value. Animations already running on the property will be canceled.
*
* @param value The amount to be animated by, as an offset from the current value.
* @see View#setRotationY(float)
* @return This object, allowing calls to methods in this class to be chained.
*/
public ViewPropertyAnimator rotationYBy(float value) {
animatePropertyBy(ROTATION_Y, value);
return this;
}
/**
* This method will cause the View's translationX
property to be animated to the
* specified value. Animations already running on the property will be canceled.
*
* @param value The value to be animated to.
* @see View#setTranslationX(float)
* @return This object, allowing calls to methods in this class to be chained.
*/
public ViewPropertyAnimator translationX(float value) {
animateProperty(TRANSLATION_X, value);
return this;
}
/**
* This method will cause the View's translationX
property to be animated by the
* specified value. Animations already running on the property will be canceled.
*
* @param value The amount to be animated by, as an offset from the current value.
* @see View#setTranslationX(float)
* @return This object, allowing calls to methods in this class to be chained.
*/
public ViewPropertyAnimator translationXBy(float value) {
animatePropertyBy(TRANSLATION_X, value);
return this;
}
/**
* This method will cause the View's translationY
property to be animated to the
* specified value. Animations already running on the property will be canceled.
*
* @param value The value to be animated to.
* @see View#setTranslationY(float)
* @return This object, allowing calls to methods in this class to be chained.
*/
public ViewPropertyAnimator translationY(float value) {
animateProperty(TRANSLATION_Y, value);
return this;
}
/**
* This method will cause the View's translationY
property to be animated by the
* specified value. Animations already running on the property will be canceled.
*
* @param value The amount to be animated by, as an offset from the current value.
* @see View#setTranslationY(float)
* @return This object, allowing calls to methods in this class to be chained.
*/
public ViewPropertyAnimator translationYBy(float value) {
animatePropertyBy(TRANSLATION_Y, value);
return this;
}
/**
* This method will cause the View's scaleX
property to be animated to the
* specified value. Animations already running on the property will be canceled.
*
* @param value The value to be animated to.
* @see View#setScaleX(float)
* @return This object, allowing calls to methods in this class to be chained.
*/
public ViewPropertyAnimator scaleX(float value) {
animateProperty(SCALE_X, value);
return this;
}
/**
* This method will cause the View's scaleX
property to be animated by the
* specified value. Animations already running on the property will be canceled.
*
* @param value The amount to be animated by, as an offset from the current value.
* @see View#setScaleX(float)
* @return This object, allowing calls to methods in this class to be chained.
*/
public ViewPropertyAnimator scaleXBy(float value) {
animatePropertyBy(SCALE_X, value);
return this;
}
/**
* This method will cause the View's scaleY
property to be animated to the
* specified value. Animations already running on the property will be canceled.
*
* @param value The value to be animated to.
* @see View#setScaleY(float)
* @return This object, allowing calls to methods in this class to be chained.
*/
public ViewPropertyAnimator scaleY(float value) {
animateProperty(SCALE_Y, value);
return this;
}
/**
* This method will cause the View's scaleY
property to be animated by the
* specified value. Animations already running on the property will be canceled.
*
* @param value The amount to be animated by, as an offset from the current value.
* @see View#setScaleY(float)
* @return This object, allowing calls to methods in this class to be chained.
*/
public ViewPropertyAnimator scaleYBy(float value) {
animatePropertyBy(SCALE_Y, value);
return this;
}
/**
* This method will cause the View's alpha
property to be animated to the
* specified value. Animations already running on the property will be canceled.
*
* @param value The value to be animated to.
* @see View#setAlpha(float)
* @return This object, allowing calls to methods in this class to be chained.
*/
public ViewPropertyAnimator alpha(float value) {
animateProperty(ALPHA, value);
return this;
}
/**
* This method will cause the View's alpha
property to be animated by the
* specified value. Animations already running on the property will be canceled.
*
* @param value The amount to be animated by, as an offset from the current value.
* @see View#setAlpha(float)
* @return This object, allowing calls to methods in this class to be chained.
*/
public ViewPropertyAnimator alphaBy(float value) {
animatePropertyBy(ALPHA, value);
return this;
}
/**
* The View associated with this ViewPropertyAnimator will have its
* {@link View#setLayerType(int, android.graphics.Paint) layer type} set to
* {@link View#LAYER_TYPE_HARDWARE} for the duration of the next animation.
* As stated in the documentation for {@link View#LAYER_TYPE_HARDWARE},
* the actual type of layer used internally depends on the runtime situation of the
* view. If the activity and this view are hardware-accelerated, then the layer will be
* accelerated as well. If the activity or the view is not accelerated, then the layer will
* effectively be the same as {@link View#LAYER_TYPE_SOFTWARE}.
*
* This state is not persistent, either on the View or on this ViewPropertyAnimator: the * layer type of the View will be restored when the animation ends to what it was when this * method was called, and this setting on ViewPropertyAnimator is only valid for the next * animation. Note that calling this method and then independently setting the layer type of * the View (by a direct call to {@link View#setLayerType(int, android.graphics.Paint)}) will * result in some inconsistency, including having the layer type restored to its pre-withLayer() * value when the animation ends.
* * @see View#setLayerType(int, android.graphics.Paint) * @return This object, allowing calls to methods in this class to be chained. */ public ViewPropertyAnimator withLayer() { mPendingSetupAction= new Runnable() { @Override public void run() { mView.setLayerType(View.LAYER_TYPE_HARDWARE, null); if (mView.isAttachedToWindow()) { mView.buildLayer(); } } }; final int currentLayerType = mView.getLayerType(); mPendingCleanupAction = new Runnable() { @Override public void run() { mView.setLayerType(currentLayerType, null); } }; if (mAnimatorSetupMap == null) { mAnimatorSetupMap = new HashMapFor example, the following code animates a view to x=200 and then back to 0:
** Runnable endAction = new Runnable() { * public void run() { * view.animate().x(0); * } * }; * view.animate().x(200).withEndAction(endAction); ** * @param runnable The action to run when the next animation ends. * @return This object, allowing calls to methods in this class to be chained. */ public ViewPropertyAnimator withEndAction(Runnable runnable) { mPendingOnEndAction = runnable; if (runnable != null && mAnimatorOnEndMap == null) { mAnimatorOnEndMap = new HashMap