Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0cc6f8f3 authored by Chet Haase's avatar Chet Haase Committed by Android (Google) Code Review
Browse files

Merge "Add keyframes to animation framework."

parents 9511ab83 3dd207a6
Loading
Loading
Loading
Loading
+33 −1
Original line number Diff line number Diff line
@@ -189,6 +189,11 @@ public class Animator extends Animatable {
     */
    private Object mAnimatedValue = null;

    /**
     * The set of keyframes (time/value pairs) that define this animation.
     */
    private KeyframeSet mKeyframeSet = null;

    /**
     * The type of the values, as determined by the valueFrom/valueTo properties.
     */
@@ -221,6 +226,20 @@ public class Animator extends Animatable {
        this.mValueType = valueType;
    }

    /**
     * This constructor takes a set of {@link Keyframe} objects that define the values
     * for the animation, along with the times at which those values will hold true during
     * the animation.
     *
     * @param duration The length of the animation, in milliseconds.
     * @param keyframes The set of keyframes that define the time/value pairs for the animation.
     */
    public Animator(long duration, Keyframe...keyframes) {
        mDuration = duration;
        mKeyframeSet = new KeyframeSet(keyframes);
        mValueType = keyframes[0].getType();
    }

    /**
     * This function is called immediately before processing the first animation
     * frame of an animation. If there is a nonzero <code>startDelay</code>, the
@@ -420,6 +439,15 @@ public class Animator extends Animatable {
        return sFrameDelay;
    }

    /**
     * Gets the set of keyframes that define this animation.
     *
     * @return KeyframeSet The set of keyframes for this animation.
     */
    KeyframeSet getKeyframes() {
        return mKeyframeSet;
    }

    /**
     * Gets the value that this animation will start from.
     *
@@ -751,7 +779,11 @@ public class Animator extends Animatable {
     */
    void animateValue(float fraction) {
        fraction = mInterpolator.getInterpolation(fraction);
        if (mKeyframeSet != null) {
            mAnimatedValue = mKeyframeSet.getValue(fraction, mEvaluator);
        } else {
            mAnimatedValue = mEvaluator.evaluate(fraction, mValueFrom, mValueTo);
        }
        if (mUpdateListeners != null) {
            int numListeners = mUpdateListeners.size();
            for (int i = 0; i < numListeners; ++i) {
+185 −0
Original line number Diff line number Diff line
/*
 * 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.view.animation.Interpolator;

/**
 * This class represents timea time/value pair for an animation. The Keyframe class is used
 * by {@link Animator} 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 option {@link android.view.animation.Interpolator}
 * object, which defines the time interpolation over the intervalue preceding the keyframe.
 */
public class Keyframe {
    /**
     * The time at which mValue will hold true.
     */
    private float mFraction;

    /**
     * The value of the animation at the time mFraction.
     */
    private Object mValue;

    /**
     * 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.
     */
    private 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 Interpolator mInterpolator = null;

    /**
     * Private constructor, called from the public constructors with the additional
     * <code>valueType</code> parameter.
     *
     * @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.
     * @param valueType The type of the <code>value</code> object. This is used by the
     * {@link #getValue()} functionm, which is queried by {@link Animator} to determine
     * the type of {@link TypeEvaluator} to use to interpolate between values.
     */
    private Keyframe(float fraction, Object value, Class valueType) {
        mFraction = fraction;
        mValue = value;
        mValueType = valueType;
    }

    /**
     * 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 Keyframe(float fraction, Object value) {
        this(fraction, value, Object.class);
    }

    /**
     * Constructs a Keyframe object with the given time and integer 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 Keyframe(float fraction, int value) {
        this(fraction, value, int.class);
    }

    /**
     * Constructs a Keyframe object with the given time and float 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 Keyframe(float fraction, float value) {
        this(fraction, value, float.class);
    }

    /**
     * Constructs a Keyframe object with the given time and double 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 Keyframe(float fraction, double value) {
        this(fraction, value, double.class);
    }

    /**
     * Gets the value for this Keyframe.
     *
     * @return The value for this Keyframe.
     */
    public Object getValue() {
        return mValue;
    }

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

    /**
     * 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 Interpolator 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(Interpolator interpolator) {
        mInterpolator = interpolator;
    }

    /**
     * Gets the type of keyframe. This information is used by Animator to determine the type of
     * {@linke TypeEvaluator} to use when calculating values between keyframes. The type is based
     * on the type of Keyframe created. For example, {@link IntKeyframe} returns a value of
     * <code>int.class</code>. This superclass returns a value of <code>Object.class</code>.
     *
     * @return The type of the value stored in the Keyframe.
     */
    public Class getType() {
        return mValueType;
    }
}
+99 −0
Original line number Diff line number Diff line
/*
 * 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.util.ArrayList;

import android.view.animation.Interpolator;

/**
 * This class holds a collection of Keyframe objects and is called by Animator to calculate
 * values between those keyframes for a given animation. The class internal to the animation
 * package because it is an implementation detail of how Keyframes are stored and used.
 */
class KeyframeSet {

    private int mNumKeyframes;

    private ArrayList<Keyframe> mKeyframes;

    public KeyframeSet(Keyframe... keyframes) {
        mKeyframes = new ArrayList<Keyframe>();
        for (Keyframe keyframe : keyframes) {
            mKeyframes.add(keyframe);
        }
        mNumKeyframes = mKeyframes.size();
    }

    /**
     * Gets the animated value, given the elapsed fraction of the animation (interpolated by the
     * animation's interpolator) and the evaluator used to calculate in-between values. This
     * function maps the input fraction to the appropriate keyframe interval and a fraction
     * between them and returns the interpolated value. Note that the input fraction may fall
     * outside the [0-1] bounds, if the animation's interpolator made that happen (e.g., a
     * spring interpolation that might send the fraction past 1.0). We handle this situation by
     * just using the two keyframes at the appropriate end when the value is outside those bounds.
     *
     * @param fraction The elapsed fraction of the animation
     * @param evaluator The type evaluator to use when calculating the interpolated values.
     * @return The animated value.
     */
    public Object getValue(float fraction, TypeEvaluator evaluator) {
        // TODO: special-case 2-keyframe common case

        if (fraction <= 0f) {
            final Keyframe prevKeyframe = mKeyframes.get(0);
            final Keyframe nextKeyframe = mKeyframes.get(1);
            final Interpolator interpolator = nextKeyframe.getInterpolator();
            if (interpolator != null) {
                fraction = interpolator.getInterpolation(fraction);
            }
            float intervalFraction = (fraction - prevKeyframe.getFraction()) /
                (nextKeyframe.getFraction() - prevKeyframe.getFraction());
            return evaluator.evaluate(intervalFraction, prevKeyframe.getValue(),
                    nextKeyframe.getValue());
        } else if (fraction >= 1f) {
            final Keyframe prevKeyframe = mKeyframes.get(mNumKeyframes - 2);
            final Keyframe nextKeyframe = mKeyframes.get(mNumKeyframes - 1);
            final Interpolator interpolator = nextKeyframe.getInterpolator();
            if (interpolator != null) {
                fraction = interpolator.getInterpolation(fraction);
            }
            float intervalFraction = (fraction - prevKeyframe.getFraction()) /
                (nextKeyframe.getFraction() - prevKeyframe.getFraction());
            return evaluator.evaluate(intervalFraction, prevKeyframe.getValue(),
                    nextKeyframe.getValue());
        }
        Keyframe prevKeyframe = mKeyframes.get(0);
        for (int i = 1; i < mNumKeyframes; ++i) {
            Keyframe nextKeyframe = mKeyframes.get(i);
            if (fraction < nextKeyframe.getFraction()) {
                final Interpolator interpolator = nextKeyframe.getInterpolator();
                if (interpolator != null) {
                    fraction = interpolator.getInterpolation(fraction);
                }
                float intervalFraction = (fraction - prevKeyframe.getFraction()) /
                    (nextKeyframe.getFraction() - prevKeyframe.getFraction());
                return evaluator.evaluate(intervalFraction, prevKeyframe.getValue(),
                        nextKeyframe.getValue());
            }
            prevKeyframe = nextKeyframe;
        }
        // shouldn't get here
        return mKeyframes.get(mNumKeyframes - 1).getValue();
    }
}
+33 −2
Original line number Diff line number Diff line
@@ -55,6 +55,9 @@ public final class PropertyAnimator extends Animator {
    // at a time.
    private ReentrantReadWriteLock propertyMapLock = new ReentrantReadWriteLock();

    // Used to pass single value to varargs parameter in setter invocation
    private Object[] mTmpValueArray = new Object[1];


    /**
     * Sets the name of the property that will be animated. This name is used to derive
@@ -271,6 +274,33 @@ public final class PropertyAnimator extends Animator {
        mPropertyName = propertyName;
    }

    /**
     * A constructor that takes <code>Keyframe</code>s. When this constructor
     * is called, the system expects to find a setter for <code>propertyName</code> on
     * the target object that takes a value of the same type as that returned from
     * {@link Keyframe#getType()}.
     * .
     *
     * @param duration The length of the animation, in milliseconds.
     * @param target The object whose property is to be animated. This object should
     * have a public function on it called <code>setName()</code>, where <code>name</code> is
     * the name of the property passed in as the <code>propertyName</code> parameter.
     * @param propertyName The name of the property on the <code>target</code> object
     * that will be animated. Given this name, the constructor will search for a
     * setter on the target object with the name <code>setPropertyName</code>. For example,
     * if the constructor is called with <code>propertyName = "foo"</code>, then the
     * target object should have a setter function with the name <code>setFoo()</code>.
     * @param keyframes The set of keyframes that define the times and values for the animation.
     * These keyframes should be ordered in increasing time value, should have a starting
     * keyframe with a fraction of 0 and and ending keyframe with a fraction of 1.
     */
    public PropertyAnimator(int duration, Object target, String propertyName,
            Keyframe...keyframes) {
        super(duration, keyframes);
        mTarget = target;
        mPropertyName = propertyName;
    }

    /**
     * This function is called immediately before processing the first animation
     * frame of an animation. If there is a nonzero <code>startDelay</code>, the
@@ -309,7 +339,7 @@ public final class PropertyAnimator extends Animator {
                propertyMapLock.writeLock().unlock();
            }
        }
        if (getValueFrom() == null || getValueTo() == null) {
        if (getKeyframes() == null && (getValueFrom() == null || getValueTo() == null)) {
            // Need to set up the getter if not set by the user, then call it
            // to get the initial values
            if (mGetter == null) {
@@ -376,7 +406,8 @@ public final class PropertyAnimator extends Animator {
        super.animateValue(fraction);
        if (mSetter != null) {
            try {
                mSetter.invoke(mTarget, getAnimatedValue());
                mTmpValueArray[0] = getAnimatedValue();
                mSetter.invoke(mTarget, mTmpValueArray);
            } catch (InvocationTargetException e) {
                Log.e("PropertyAnimator", e.toString());
            } catch (IllegalAccessException e) {