From a00f3865f55c5c9cb74510ee2b239d101230133c Mon Sep 17 00:00:00 2001 From: Chet Haase Date: Tue, 22 Feb 2011 06:34:40 -0800 Subject: Add ViewPropertyAnimator for easy animation of View properties Change-Id: I2bc52ca16507d8d20004d2d6823e587791272aac --- api/current.xml | 332 +++++++++- core/java/android/animation/ValueAnimator.java | 16 + core/java/android/view/View.java | 60 +- core/java/android/view/ViewPropertyAnimator.java | 745 +++++++++++++++++++++++ 4 files changed, 1141 insertions(+), 12 deletions(-) create mode 100644 core/java/android/view/ViewPropertyAnimator.java diff --git a/api/current.xml b/api/current.xml index 5ceca12..1089604 100644 --- a/api/current.xml +++ b/api/current.xml @@ -21153,6 +21153,17 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java index b963117..f562851 100755 --- a/core/java/android/animation/ValueAnimator.java +++ b/core/java/android/animation/ValueAnimator.java @@ -160,6 +160,11 @@ public class ValueAnimator extends Animator { private int mCurrentIteration = 0; /** + * Tracks current elapsed/eased fraction, for querying in getAnimatedFraction(). + */ + private float mCurrentFraction = 0f; + + /** * Tracks whether a startDelay'd animation has begun playing through the startDelay. */ private boolean mStartedDelay = false; @@ -1111,6 +1116,16 @@ public class ValueAnimator extends Animator { } /** + * Returns the current animation fraction, which is the elapsed/interpolated fraction used in + * the most recent frame update on the animation. + * + * @return Elapsed/interpolated fraction of the animation. + */ + public float getAnimatedFraction() { + return mCurrentFraction; + } + + /** * 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 @@ -1124,6 +1139,7 @@ public class ValueAnimator extends Animator { */ void animateValue(float fraction) { fraction = mInterpolator.getInterpolation(fraction); + mCurrentFraction = fraction; int numValues = mValues.length; for (int i = 0; i < numValues; ++i) { mValues[i].calculateValue(fraction); diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index e18c5e0..65d93a6 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -1866,7 +1866,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility * transform matrix, based on whether the rotation or scaleX/Y properties * have changed since the matrix was last calculated. */ - private boolean mMatrixDirty = false; + boolean mMatrixDirty = false; /** * An internal variable that tracks whether we need to recalculate the @@ -1914,66 +1914,66 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility * The degrees rotation around the vertical axis through the pivot point. */ @ViewDebug.ExportedProperty - private float mRotationY = 0f; + float mRotationY = 0f; /** * The degrees rotation around the horizontal axis through the pivot point. */ @ViewDebug.ExportedProperty - private float mRotationX = 0f; + float mRotationX = 0f; /** * The degrees rotation around the pivot point. */ @ViewDebug.ExportedProperty - private float mRotation = 0f; + float mRotation = 0f; /** * The amount of translation of the object away from its left property (post-layout). */ @ViewDebug.ExportedProperty - private float mTranslationX = 0f; + float mTranslationX = 0f; /** * The amount of translation of the object away from its top property (post-layout). */ @ViewDebug.ExportedProperty - private float mTranslationY = 0f; + float mTranslationY = 0f; /** * The amount of scale in the x direction around the pivot point. A * value of 1 means no scaling is applied. */ @ViewDebug.ExportedProperty - private float mScaleX = 1f; + float mScaleX = 1f; /** * The amount of scale in the y direction around the pivot point. A * value of 1 means no scaling is applied. */ @ViewDebug.ExportedProperty - private float mScaleY = 1f; + float mScaleY = 1f; /** * The amount of scale in the x direction around the pivot point. A * value of 1 means no scaling is applied. */ @ViewDebug.ExportedProperty - private float mPivotX = 0f; + float mPivotX = 0f; /** * The amount of scale in the y direction around the pivot point. A * value of 1 means no scaling is applied. */ @ViewDebug.ExportedProperty - private float mPivotY = 0f; + float mPivotY = 0f; /** * The opacity of the View. This is a value from 0 to 1, where 0 means * completely transparent and 1 means completely opaque. */ @ViewDebug.ExportedProperty - private float mAlpha = 1f; + float mAlpha = 1f; /** * The distance in pixels from the left edge of this view's parent @@ -2237,6 +2237,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility private int mTouchSlop; /** + * Object that handles automatic animation of view properties. + */ + private ViewPropertyAnimator mAnimator = null; + + /** * Cache drag/drop state * */ @@ -6157,6 +6162,26 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility } /** + * Faster version of setAlpha() which performs the same steps except there are + * no calls to invalidate(). The caller of this function should perform proper invalidation + * on the parent and this object. The return value indicates whether the subclass handles + * alpha (the return value for onSetAlpha()). + * + * @param alpha The new value for the alpha property + * @return true if the View subclass handles alpha (the return value for onSetAlpha()) + */ + boolean setAlphaNoInvalidation(float alpha) { + mAlpha = alpha; + boolean subclassHandlesAlpha = onSetAlpha((int) (alpha * 255)); + if (subclassHandlesAlpha) { + mPrivateFlags |= ALPHA_SET; + } else { + mPrivateFlags &= ~ALPHA_SET; + } + return subclassHandlesAlpha; + } + + /** * Top position of this view relative to its parent. * * @return The top of this view, in pixels. @@ -11785,6 +11810,19 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility } /** + * This method returns a ViewPropertyAnimator object, which can be used to animate + * specific properties on this View. + * + * @return ViewPropertyAnimator The ViewPropertyAnimator associated with this View. + */ + public ViewPropertyAnimator animate() { + if (mAnimator == null) { + mAnimator = new ViewPropertyAnimator(this); + } + return mAnimator; + } + + /** * Interface definition for a callback to be invoked when a key event is * dispatched to this view. The callback will be invoked before the key * event is given to the view. diff --git a/core/java/android/view/ViewPropertyAnimator.java b/core/java/android/view/ViewPropertyAnimator.java new file mode 100644 index 0000000..cc00f94 --- /dev/null +++ b/core/java/android/view/ViewPropertyAnimator.java @@ -0,0 +1,745 @@ +/* + * 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.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 could provide better performance for several simultaneous animations, because + * it will optimize invalidatesionto take place only once for several properties instead of each + * aniamted 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 calss 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 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 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 + */ + private Animator.AnimatorListener mListener = null; + + /** + * 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. + */ + ArrayList mPendingAnimations = new ArrayList(); + + /** + * Constants used to associate a property being requested and the mechanism used to set + * the property (this calss calls directly into View to set the properties in question). + */ + private static final int NONE = 0x0000; + private static final int TRANSLATION_X = 0x0001; + private static final int TRANSLATION_Y = 0x0002; + private static final int SCALE_X = 0x0004; + private static final int SCALE_Y = 0x0008; + private static final int ROTATION = 0x0010; + private static final int ROTATION_X = 0x0020; + private static final int ROTATION_Y = 0x0040; + private static final int X = 0x0080; + private static final int Y = 0x0100; + private static final int ALPHA = 0x0200; + + private static final int TRANSFORM_MASK = TRANSLATION_X | TRANSLATION_Y | SCALE_X | SCALE_Y | + ROTATION | ROTATION_X | ROTATION_Y | X | Y; + + /** + * The mechanism by which the user can request several properties that are then animated + * together works by posting this Runnable to start the underlying Animator. Every time + * a property animation is requested, we cancel any previous postings of the Runnable + * and re-post it. This means that we will only ever run the Runnable (and thus start the + * underlying animator) after the caller is done setting the properties that should be + * animated together. + */ + private Runnable mAnimationStarter = new Runnable() { + @Override + public void run() { + startAnimation(); + } + }; + + /** + * This class holds information about the overall animation being run on the set of + * properties. The mask describes which properties are being animated and the + * values holder is the list of all property/value objects. + */ + private static class PropertyBundle { + int mPropertyMask; + ArrayList mNameValuesHolder; + PropertyBundle(int propertyMask, ArrayList nameValuesHolder) { + mPropertyMask = propertyMask; + mNameValuesHolder = nameValuesHolder; + } + } + + /** + * This list tracks the list of properties being animated by any particular animator. + * In most situations, there would only ever be one animator running at a time. But it is + * possible to request some properties to animate together, then while those properties + * are animating, to request some other properties to animate together. The way that + * works is by having this map associate the group of properties being animated with the + * animator handling the animation. On every update event for an Animator, we ask the + * map for the associated properties and set them accordingly. + */ + private HashMap mAnimatorMap = + new HashMap(); + + /** + * This is the information we need to set each property during the animation. + * mNameConstant is used to set the appropriate field in View, and the from/delta + * values are used to calculate the animated value for a given animation fraction + * during the animation. + */ + private static class NameValuesHolder { + int mNameConstant; + float mFromValue; + float mDeltaValue; + NameValuesHolder(int nameConstant, float fromValue, float deltaValue) { + mNameConstant = nameConstant; + mFromValue = fromValue; + mDeltaValue = deltaValue; + } + } + + /** + * Constructor, called by View. This is private by design, as the user should only + * get a ViewPropertyAnimator by calling View.animate(). + * + * @param view The View associated with this ViewPropertyAnimator + */ + ViewPropertyAnimator(View view) { + mView = view; + } + + /** + * Sets the duration for the underlying animator that animates the requested properties. + * By default, the animator uses the default value for ValueAnimator. Calling this method + * will cause the declared value to be used instead. + * @param duration The length of ensuing property animations, in milliseconds. The value + * cannot be negative. + * @return This object, allowing calls to methods in this class to be chained. + */ + public ViewPropertyAnimator setDuration(long duration) { + if (duration < 0) { + throw new IllegalArgumentException("Animators cannot have negative duration: " + + duration); + } + mDurationSet = true; + mDuration = duration; + return this; + } + + /** + * Sets the interpolator for the underlying animator that animates the requested properties. + * By default, the animator uses the default interpolator for ValueAnimator. Calling this method + * will cause the declared object to be used instead. + * + * @param interpolator The TimeInterpolator to be used for ensuing property animations. + * @return This object, allowing calls to methods in this class to be chained. + */ + public ViewPropertyAnimator setInterpolator(TimeInterpolator interpolator) { + mInterpolatorSet = true; + mInterpolator = interpolator; + return this; + } + + /** + * Sets a listener for events in the underlying Animators that run the property + * animations. + * + * @param listener The listener to be called with AnimatorListener events. + * @return This object, allowing calls to methods in this class to be chained. + */ + public ViewPropertyAnimator setListener(Animator.AnimatorListener listener) { + mListener = listener; + return this; + } + + /** + * This method will cause the View's x property to be animated to the + * specifed value. + * + * @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 + * specifed value. + * + * @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 + * specifed value. + * + * @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 + * specifed value. + * + * @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 + * specifed value. + * + * @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 + * specifed value. + * + * @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 + * specifed value. + * + * @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 + * specifed value. + * + * @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 + * specifed value. + * + * @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 + * specifed value. + * + * @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 + * specifed value. + * + * @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 + * specifed value. + * + * @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 + * specifed value. + * + * @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 + * specifed value. + * + * @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 + * specifed value. + * + * @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 + * specifed value. + * + * @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 + * specifed value. + * + * @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 + * specifed value. + * + * @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. + * + * @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. + * + * @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; + } + + /** + * Starts the underlying Animator for a set of properties. We use a single animator that + * simply runs from 0 to 1, and then use that fractional value to set each property + * value accordingly. + */ + private void startAnimation() { + ValueAnimator animator = ValueAnimator.ofFloat(1.0f); + ArrayList nameValueList = + (ArrayList) mPendingAnimations.clone(); + mPendingAnimations.clear(); + int propertyMask = 0; + int propertyCount = nameValueList.size(); + for (int i = 0; i < propertyCount; ++i) { + NameValuesHolder nameValuesHolder = nameValueList.get(i); + propertyMask |= nameValuesHolder.mNameConstant; + } + // First, cancel any running animation on the same property set + if (mAnimatorMap.size() > 0) { + Animator animatorToCancel = null; + Set animatorSet = mAnimatorMap.keySet(); + for (Animator runningAnim : animatorSet) { + PropertyBundle bundle = mAnimatorMap.get(runningAnim); + if (bundle.mPropertyMask == propertyMask) { + // There can be only one such duplicate, because that animation would + // have caused previous ones to cancel prior to starting. So break when we + // find one. + animatorToCancel = runningAnim; + break; + } + } + if (animatorToCancel != null) { + animatorToCancel.cancel(); + } + } + mAnimatorMap.put(animator, new PropertyBundle(propertyMask, nameValueList)); + animator.addUpdateListener(mAnimatorEventListener); + animator.addListener(mAnimatorEventListener); + if (mDurationSet) { + animator.setDuration(mDuration); + } + if (mInterpolatorSet) { + animator.setInterpolator(mInterpolator); + } + animator.start(); + } + + /** + * Utility function, called by the various x(), y(), etc. methods. This stores the + * constnat name for the property along with the from/delta values that will be used to + * calculate and set the property during the animation. This structure is added to the + * pending animations, awaiting the eventual start() of the underlying animator. A + * Runnable is posted to start the animation, and any pending such Runnable is canceled + * (which enables us to end up starting just one animator for all of the properties + * specified at one time). + * + * @param constantName The specifier for the property being animated + * @param toValue The value to which the property will animate + */ + private void animateProperty(int constantName, float toValue) { + float fromValue = getValue(constantName); + float deltaValue = toValue - fromValue; + animatePropertyBy(constantName, fromValue, deltaValue); + } + + /** + * Utility function, called by the various xBy(), yBy(), etc. methods. This method is + * just like animateProperty(), except the value is an offset from the property's + * current value, instead of an absolute "to" value. + * + * @param constantName The specifier for the property being animated + * @param byValue The amount by which the property will change + */ + private void animatePropertyBy(int constantName, float byValue) { + float fromValue = getValue(constantName); + animatePropertyBy(constantName, fromValue, byValue); + } + + /** + * Utility function, called by animatePropert() and animatePropertyBy(), which handles the + * details of adding a pending animation and posting the request to start the animation. + * + * @param constantName The specifier for the property being animated + * @param fromValue The starting value of the property + * @param byValue The amount by which the property will change + */ + private void animatePropertyBy(int constantName, float fromValue, float byValue) { + float startValue = getValue(constantName); + NameValuesHolder nameValuePair = new NameValuesHolder(constantName, startValue, byValue); + mPendingAnimations.add(nameValuePair); + mView.getHandler().removeCallbacks(mAnimationStarter); + mView.post(mAnimationStarter); + } + + /** + * This method handles setting the property values directly in the View object's fields. + * propertyConstant tells it which property should be set, value is the value to set + * the property to. + * + * @param propertyConstant The property to be set + * @param value The value to set the property to + */ + private void setValue(int propertyConstant, float value) { + switch (propertyConstant) { + case TRANSLATION_X: + mView.mTranslationX = value; + break; + case TRANSLATION_Y: + mView.mTranslationY = value; + break; + case ROTATION: + mView.mRotation = value; + break; + case ROTATION_X: + mView.mRotationX = value; + break; + case ROTATION_Y: + mView.mRotationY = value; + break; + case SCALE_X: + mView.mScaleX = value; + break; + case SCALE_Y: + mView.mScaleY = value; + break; + case X: + mView.mTranslationX = value - mView.mLeft; + break; + case Y: + mView.mTranslationY = value - mView.mTop; + break; + case ALPHA: + mView.mAlpha = value; + break; + } + } + + /** + * This method gets the value of the named property from the View object. + * + * @param propertyConstant The property whose value should be returned + * @return float The value of the named property + */ + private float getValue(int propertyConstant) { + switch (propertyConstant) { + case TRANSLATION_X: + return mView.mTranslationX; + case TRANSLATION_Y: + return mView.mTranslationY; + case ROTATION: + return mView.mRotation; + case ROTATION_X: + return mView.mRotationX; + case ROTATION_Y: + return mView.mRotationY; + case SCALE_X: + return mView.mScaleX; + case SCALE_Y: + return mView.mScaleY; + case X: + return mView.mLeft + mView.mTranslationX; + case Y: + return mView.mTop + mView.mTranslationY; + case ALPHA: + return mView.mAlpha; + } + return 0; + } + + /** + * Utility class that handles the various Animator events. The only ones we care + * about are the end event (which we use to clean up the animator map when an animator + * finishes) and the update event (which we use to calculate the current value of each + * property and then set it on the view object). + */ + private class AnimatorEventListener + implements Animator.AnimatorListener, ValueAnimator.AnimatorUpdateListener { + @Override + public void onAnimationStart(Animator animation) { + if (mListener != null) { + mListener.onAnimationStart(animation); + } + } + + @Override + public void onAnimationCancel(Animator animation) { + if (mListener != null) { + mListener.onAnimationCancel(animation); + } + } + + @Override + public void onAnimationRepeat(Animator animation) { + if (mListener != null) { + mListener.onAnimationRepeat(animation); + } + } + + @Override + public void onAnimationEnd(Animator animation) { + if (mListener != null) { + mListener.onAnimationEnd(animation); + } + mAnimatorMap.remove(animation); + } + + /** + * Calculate the current value for each property and set it on the view. Invalidate + * the view object appropriately, depending on which properties are being animated. + * + * @param animation The animator associated with the properties that need to be + * set. This animator holds the animation fraction which we will use to calculate + * the current value of each property. + */ + @Override + public void onAnimationUpdate(ValueAnimator animation) { + // alpha requires slightly different treatment than the other (transform) properties. + // The logic in setAlpha() is not simply setting mAlpha, plus the invalidation + // logic is dependent on how the view handles an internal call to onSetAlpha(). + // We track what kinds of properties are set, and how alpha is handled when it is + // set, and perform the invalidation steps appropriately. + boolean alphaHandled = false; + mView.invalidateParentCaches(); + float fraction = animation.getAnimatedFraction(); + PropertyBundle propertyBundle = mAnimatorMap.get(animation); + int propertyMask = propertyBundle.mPropertyMask; + if ((propertyMask & TRANSFORM_MASK) != 0) { + mView.invalidate(false); + } + ArrayList valueList = propertyBundle.mNameValuesHolder; + if (valueList != null) { + int count = valueList.size(); + for (int i = 0; i < count; ++i) { + NameValuesHolder values = valueList.get(i); + float value = values.mFromValue + fraction * values.mDeltaValue; + if (values.mNameConstant == ALPHA) { + alphaHandled = mView.setAlphaNoInvalidation(value); + } else { + setValue(values.mNameConstant, value); + } + } + } + if ((propertyMask & TRANSFORM_MASK) != 0) { + mView.mMatrixDirty = true; + mView.mPrivateFlags |= View.DRAWN; // force another invalidation + } + // invalidate(false) in all cases except if alphaHandled gets set to true + // via the call to setAlphaNoInvalidation(), above + mView.invalidate(alphaHandled); + } + } +} -- cgit v1.1