/*
* 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;
}
}
}