/* * 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; /** * This class holds a time/value pair for an animation. The Keyframe class is used * by {@link ValueAnimator} to define the values that the animation target will have over the course * of the animation. As the time proceeds from one keyframe to the other, the value of the * target object will animate between the value at the previous keyframe and the value at the * next keyframe. Each keyframe also holds an optional {@link TimeInterpolator} * object, which defines the time interpolation over the intervalue preceding the keyframe. * * <p>The Keyframe class itself is abstract. The type-specific factory methods will return * a subclass of Keyframe specific to the type of value being stored. This is done to improve * performance when dealing with the most common cases (e.g., <code>float</code> and * <code>int</code> values). Other types will fall into a more general Keyframe class that * treats its values as Objects. Unless your animation requires dealing with a custom type * or a data structure that needs to be animated directly (and evaluated using an implementation * of {@link TypeEvaluator}), you should stick to using float and int as animations using those * types have lower runtime overhead than other types.</p> */ public abstract class Keyframe implements Cloneable { /** * Flag to indicate whether this keyframe has a valid value. This flag is used when an * animation first starts, to populate placeholder keyframes with real values derived * from the target object. */ boolean mHasValue; /** * Flag to indicate whether the value in the keyframe was read from the target object or not. * If so, its value will be recalculated if target changes. */ boolean mValueWasSetOnStart; /** * The time at which mValue will hold true. */ float mFraction; /** * The type of the value in this Keyframe. This type is determined at construction time, * based on the type of the <code>value</code> object passed into the constructor. */ Class mValueType; /** * The optional time interpolator for the interval preceding this keyframe. A null interpolator * (the default) results in linear interpolation over the interval. */ private TimeInterpolator mInterpolator = null; /** * Constructs a Keyframe object with the given time and value. The time defines the * time, as a proportion of an overall animation's duration, at which the value will hold true * for the animation. The value for the animation between keyframes will be calculated as * an interpolation between the values at those keyframes. * * @param fraction The time, expressed as a value between 0 and 1, representing the fraction * of time elapsed of the overall animation duration. * @param value The value that the object will animate to as the animation time approaches * the time in this keyframe, and the the value animated from as the time passes the time in * this keyframe. */ public static Keyframe ofInt(float fraction, int value) { return new IntKeyframe(fraction, value); } /** * Constructs a Keyframe object with the given time. The value at this time will be derived * from the target object when the animation first starts (note that this implies that keyframes * with no initial value must be used as part of an {@link ObjectAnimator}). * The time defines the * time, as a proportion of an overall animation's duration, at which the value will hold true * for the animation. The value for the animation between keyframes will be calculated as * an interpolation between the values at those keyframes. * * @param fraction The time, expressed as a value between 0 and 1, representing the fraction * of time elapsed of the overall animation duration. */ public static Keyframe ofInt(float fraction) { return new IntKeyframe(fraction); } /** * Constructs a Keyframe object with the given time and value. The time defines the * time, as a proportion of an overall animation's duration, at which the value will hold true * for the animation. The value for the animation between keyframes will be calculated as * an interpolation between the values at those keyframes. * * @param fraction The time, expressed as a value between 0 and 1, representing the fraction * of time elapsed of the overall animation duration. * @param value The value that the object will animate to as the animation time approaches * the time in this keyframe, and the the value animated from as the time passes the time in * this keyframe. */ public static Keyframe ofFloat(float fraction, float value) { return new FloatKeyframe(fraction, value); } /** * Constructs a Keyframe object with the given time. The value at this time will be derived * from the target object when the animation first starts (note that this implies that keyframes * with no initial value must be used as part of an {@link ObjectAnimator}). * The time defines the * time, as a proportion of an overall animation's duration, at which the value will hold true * for the animation. The value for the animation between keyframes will be calculated as * an interpolation between the values at those keyframes. * * @param fraction The time, expressed as a value between 0 and 1, representing the fraction * of time elapsed of the overall animation duration. */ public static Keyframe ofFloat(float fraction) { return new FloatKeyframe(fraction); } /** * Constructs a Keyframe object with the given time and value. The time defines the * time, as a proportion of an overall animation's duration, at which the value will hold true * for the animation. The value for the animation between keyframes will be calculated as * an interpolation between the values at those keyframes. * * @param fraction The time, expressed as a value between 0 and 1, representing the fraction * of time elapsed of the overall animation duration. * @param value The value that the object will animate to as the animation time approaches * the time in this keyframe, and the the value animated from as the time passes the time in * this keyframe. */ public static Keyframe ofObject(float fraction, Object value) { return new ObjectKeyframe(fraction, value); } /** * Constructs a Keyframe object with the given time. The value at this time will be derived * from the target object when the animation first starts (note that this implies that keyframes * with no initial value must be used as part of an {@link ObjectAnimator}). * The time defines the * time, as a proportion of an overall animation's duration, at which the value will hold true * for the animation. The value for the animation between keyframes will be calculated as * an interpolation between the values at those keyframes. * * @param fraction The time, expressed as a value between 0 and 1, representing the fraction * of time elapsed of the overall animation duration. */ public static Keyframe ofObject(float fraction) { return new ObjectKeyframe(fraction, null); } /** * Indicates whether this keyframe has a valid value. This method is called internally when * an {@link ObjectAnimator} first starts; keyframes without values are assigned values at * that time by deriving the value for the property from the target object. * * @return boolean Whether this object has a value assigned. */ public boolean hasValue() { return mHasValue; } /** * If the Keyframe's value was acquired from the target object, this flag should be set so that, * if target changes, value will be reset. * * @return boolean Whether this Keyframe's value was retieved from the target object or not. */ boolean valueWasSetOnStart() { return mValueWasSetOnStart; } void setValueWasSetOnStart(boolean valueWasSetOnStart) { mValueWasSetOnStart = valueWasSetOnStart; } /** * Gets the value for this Keyframe. * * @return The value for this Keyframe. */ public abstract Object getValue(); /** * Sets the value for this Keyframe. * * @param value value for this Keyframe. */ public abstract void setValue(Object value); /** * Gets the time for this keyframe, as a fraction of the overall animation duration. * * @return The time associated with this keyframe, as a fraction of the overall animation * duration. This should be a value between 0 and 1. */ public float getFraction() { return mFraction; } /** * Sets the time for this keyframe, as a fraction of the overall animation duration. * * @param fraction time associated with this keyframe, as a fraction of the overall animation * duration. This should be a value between 0 and 1. */ public void setFraction(float fraction) { mFraction = fraction; } /** * Gets the optional interpolator for this Keyframe. A value of <code>null</code> indicates * that there is no interpolation, which is the same as linear interpolation. * * @return The optional interpolator for this Keyframe. */ public TimeInterpolator getInterpolator() { return mInterpolator; } /** * Sets the optional interpolator for this Keyframe. A value of <code>null</code> indicates * that there is no interpolation, which is the same as linear interpolation. * * @return The optional interpolator for this Keyframe. */ public void setInterpolator(TimeInterpolator interpolator) { mInterpolator = interpolator; } /** * Gets the type of keyframe. This information is used by ValueAnimator to determine the type of * {@link TypeEvaluator} to use when calculating values between keyframes. The type is based * on the type of Keyframe created. * * @return The type of the value stored in the Keyframe. */ public Class getType() { return mValueType; } @Override public abstract Keyframe clone(); /** * This internal subclass is used for all types which are not int or float. */ static class ObjectKeyframe extends Keyframe { /** * The value of the animation at the time mFraction. */ Object mValue; ObjectKeyframe(float fraction, Object value) { mFraction = fraction; mValue = value; mHasValue = (value != null); mValueType = mHasValue ? value.getClass() : Object.class; } public Object getValue() { return mValue; } public void setValue(Object value) { mValue = value; mHasValue = (value != null); } @Override public ObjectKeyframe clone() { ObjectKeyframe kfClone = new ObjectKeyframe(getFraction(), hasValue() ? mValue : null); kfClone.mValueWasSetOnStart = mValueWasSetOnStart; kfClone.setInterpolator(getInterpolator()); return kfClone; } } /** * Internal subclass used when the keyframe value is of type int. */ static class IntKeyframe extends Keyframe { /** * The value of the animation at the time mFraction. */ int mValue; IntKeyframe(float fraction, int value) { mFraction = fraction; mValue = value; mValueType = int.class; mHasValue = true; } IntKeyframe(float fraction) { mFraction = fraction; mValueType = int.class; } public int getIntValue() { return mValue; } public Object getValue() { return mValue; } public void setValue(Object value) { if (value != null && value.getClass() == Integer.class) { mValue = ((Integer)value).intValue(); mHasValue = true; } } @Override public IntKeyframe clone() { IntKeyframe kfClone = mHasValue ? new IntKeyframe(getFraction(), mValue) : new IntKeyframe(getFraction()); kfClone.setInterpolator(getInterpolator()); kfClone.mValueWasSetOnStart = mValueWasSetOnStart; return kfClone; } } /** * Internal subclass used when the keyframe value is of type float. */ static class FloatKeyframe extends Keyframe { /** * The value of the animation at the time mFraction. */ float mValue; FloatKeyframe(float fraction, float value) { mFraction = fraction; mValue = value; mValueType = float.class; mHasValue = true; } FloatKeyframe(float fraction) { mFraction = fraction; mValueType = float.class; } public float getFloatValue() { return mValue; } public Object getValue() { return mValue; } public void setValue(Object value) { if (value != null && value.getClass() == Float.class) { mValue = ((Float)value).floatValue(); mHasValue = true; } } @Override public FloatKeyframe clone() { FloatKeyframe kfClone = mHasValue ? new FloatKeyframe(getFraction(), mValue) : new FloatKeyframe(getFraction()); kfClone.setInterpolator(getInterpolator()); kfClone.mValueWasSetOnStart = mValueWasSetOnStart; return kfClone; } } }