diff options
Diffstat (limited to 'android/view/animation')
25 files changed, 5103 insertions, 0 deletions
diff --git a/android/view/animation/AccelerateDecelerateInterpolator.java b/android/view/animation/AccelerateDecelerateInterpolator.java new file mode 100644 index 00000000..21d5a5b8 --- /dev/null +++ b/android/view/animation/AccelerateDecelerateInterpolator.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2007 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.animation; + +import android.content.Context; +import android.util.AttributeSet; + +import com.android.internal.view.animation.HasNativeInterpolator; +import com.android.internal.view.animation.NativeInterpolatorFactory; +import com.android.internal.view.animation.NativeInterpolatorFactoryHelper; + +/** + * An interpolator where the rate of change starts and ends slowly but + * accelerates through the middle. + */ +@HasNativeInterpolator +public class AccelerateDecelerateInterpolator extends BaseInterpolator + implements NativeInterpolatorFactory { + public AccelerateDecelerateInterpolator() { + } + + @SuppressWarnings({"UnusedDeclaration"}) + public AccelerateDecelerateInterpolator(Context context, AttributeSet attrs) { + } + + public float getInterpolation(float input) { + return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f; + } + + /** @hide */ + @Override + public long createNativeInterpolator() { + return NativeInterpolatorFactoryHelper.createAccelerateDecelerateInterpolator(); + } +} diff --git a/android/view/animation/AccelerateInterpolator.java b/android/view/animation/AccelerateInterpolator.java new file mode 100644 index 00000000..6c8d7b19 --- /dev/null +++ b/android/view/animation/AccelerateInterpolator.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2006 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.animation; + +import android.content.Context; +import android.content.res.Resources; +import android.content.res.Resources.Theme; +import android.content.res.TypedArray; +import android.util.AttributeSet; + +import com.android.internal.R; +import com.android.internal.view.animation.HasNativeInterpolator; +import com.android.internal.view.animation.NativeInterpolatorFactory; +import com.android.internal.view.animation.NativeInterpolatorFactoryHelper; + +/** + * An interpolator where the rate of change starts out slowly and + * and then accelerates. + * + */ +@HasNativeInterpolator +public class AccelerateInterpolator extends BaseInterpolator implements NativeInterpolatorFactory { + private final float mFactor; + private final double mDoubleFactor; + + public AccelerateInterpolator() { + mFactor = 1.0f; + mDoubleFactor = 2.0; + } + + /** + * Constructor + * + * @param factor Degree to which the animation should be eased. Seting + * factor to 1.0f produces a y=x^2 parabola. Increasing factor above + * 1.0f exaggerates the ease-in effect (i.e., it starts even + * slower and ends evens faster) + */ + public AccelerateInterpolator(float factor) { + mFactor = factor; + mDoubleFactor = 2 * mFactor; + } + + public AccelerateInterpolator(Context context, AttributeSet attrs) { + this(context.getResources(), context.getTheme(), attrs); + } + + /** @hide */ + public AccelerateInterpolator(Resources res, Theme theme, AttributeSet attrs) { + TypedArray a; + if (theme != null) { + a = theme.obtainStyledAttributes(attrs, R.styleable.AccelerateInterpolator, 0, 0); + } else { + a = res.obtainAttributes(attrs, R.styleable.AccelerateInterpolator); + } + + mFactor = a.getFloat(R.styleable.AccelerateInterpolator_factor, 1.0f); + mDoubleFactor = 2 * mFactor; + setChangingConfiguration(a.getChangingConfigurations()); + a.recycle(); + } + + public float getInterpolation(float input) { + if (mFactor == 1.0f) { + return input * input; + } else { + return (float)Math.pow(input, mDoubleFactor); + } + } + + /** @hide */ + @Override + public long createNativeInterpolator() { + return NativeInterpolatorFactoryHelper.createAccelerateInterpolator(mFactor); + } +} diff --git a/android/view/animation/AlphaAnimation.java b/android/view/animation/AlphaAnimation.java new file mode 100644 index 00000000..c4d9afcc --- /dev/null +++ b/android/view/animation/AlphaAnimation.java @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2006 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.animation; + +import android.content.Context; +import android.content.res.TypedArray; +import android.util.AttributeSet; + +/** + * An animation that controls the alpha level of an object. + * Useful for fading things in and out. This animation ends up + * changing the alpha property of a {@link Transformation} + * + */ +public class AlphaAnimation extends Animation { + private float mFromAlpha; + private float mToAlpha; + + /** + * Constructor used when an AlphaAnimation is loaded from a resource. + * + * @param context Application context to use + * @param attrs Attribute set from which to read values + */ + public AlphaAnimation(Context context, AttributeSet attrs) { + super(context, attrs); + + TypedArray a = + context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.AlphaAnimation); + + mFromAlpha = a.getFloat(com.android.internal.R.styleable.AlphaAnimation_fromAlpha, 1.0f); + mToAlpha = a.getFloat(com.android.internal.R.styleable.AlphaAnimation_toAlpha, 1.0f); + + a.recycle(); + } + + /** + * Constructor to use when building an AlphaAnimation from code + * + * @param fromAlpha Starting alpha value for the animation, where 1.0 means + * fully opaque and 0.0 means fully transparent. + * @param toAlpha Ending alpha value for the animation. + */ + public AlphaAnimation(float fromAlpha, float toAlpha) { + mFromAlpha = fromAlpha; + mToAlpha = toAlpha; + } + + /** + * Changes the alpha property of the supplied {@link Transformation} + */ + @Override + protected void applyTransformation(float interpolatedTime, Transformation t) { + final float alpha = mFromAlpha; + t.setAlpha(alpha + ((mToAlpha - alpha) * interpolatedTime)); + } + + @Override + public boolean willChangeTransformationMatrix() { + return false; + } + + @Override + public boolean willChangeBounds() { + return false; + } + + /** + * @hide + */ + @Override + public boolean hasAlpha() { + return true; + } +} diff --git a/android/view/animation/Animation.java b/android/view/animation/Animation.java new file mode 100644 index 00000000..474db128 --- /dev/null +++ b/android/view/animation/Animation.java @@ -0,0 +1,1169 @@ +/* + * Copyright (C) 2006 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.animation; + +import android.annotation.AnimRes; +import android.annotation.ColorInt; +import android.annotation.InterpolatorRes; +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.RectF; +import android.os.Handler; +import android.os.SystemProperties; +import android.util.AttributeSet; +import android.util.TypedValue; + +import dalvik.system.CloseGuard; + +/** + * Abstraction for an Animation that can be applied to Views, Surfaces, or + * other objects. See the {@link android.view.animation animation package + * description file}. + */ +public abstract class Animation implements Cloneable { + /** + * Repeat the animation indefinitely. + */ + public static final int INFINITE = -1; + + /** + * When the animation reaches the end and the repeat count is INFINTE_REPEAT + * or a positive value, the animation restarts from the beginning. + */ + public static final int RESTART = 1; + + /** + * When the animation reaches the end and the repeat count is INFINTE_REPEAT + * or a positive value, the animation plays backward (and then forward again). + */ + public static final int REVERSE = 2; + + /** + * Can be used as the start time to indicate the start time should be the current + * time when {@link #getTransformation(long, Transformation)} is invoked for the + * first animation frame. This can is useful for short animations. + */ + public static final int START_ON_FIRST_FRAME = -1; + + /** + * The specified dimension is an absolute number of pixels. + */ + public static final int ABSOLUTE = 0; + + /** + * The specified dimension holds a float and should be multiplied by the + * height or width of the object being animated. + */ + public static final int RELATIVE_TO_SELF = 1; + + /** + * The specified dimension holds a float and should be multiplied by the + * height or width of the parent of the object being animated. + */ + public static final int RELATIVE_TO_PARENT = 2; + + /** + * Requests that the content being animated be kept in its current Z + * order. + */ + public static final int ZORDER_NORMAL = 0; + + /** + * Requests that the content being animated be forced on top of all other + * content for the duration of the animation. + */ + public static final int ZORDER_TOP = 1; + + /** + * Requests that the content being animated be forced under all other + * content for the duration of the animation. + */ + public static final int ZORDER_BOTTOM = -1; + + // Use a preload holder to isolate static initialization into inner class, which allows + // Animation and its subclasses to be compile-time initialized. + private static class NoImagePreloadHolder { + public static final boolean USE_CLOSEGUARD + = SystemProperties.getBoolean("log.closeguard.Animation", false); + } + + /** + * Set by {@link #getTransformation(long, Transformation)} when the animation ends. + */ + boolean mEnded = false; + + /** + * Set by {@link #getTransformation(long, Transformation)} when the animation starts. + */ + boolean mStarted = false; + + /** + * Set by {@link #getTransformation(long, Transformation)} when the animation repeats + * in REVERSE mode. + */ + boolean mCycleFlip = false; + + /** + * This value must be set to true by {@link #initialize(int, int, int, int)}. It + * indicates the animation was successfully initialized and can be played. + */ + boolean mInitialized = false; + + /** + * Indicates whether the animation transformation should be applied before the + * animation starts. The value of this variable is only relevant if mFillEnabled is true; + * otherwise it is assumed to be true. + */ + boolean mFillBefore = true; + + /** + * Indicates whether the animation transformation should be applied after the + * animation ends. + */ + boolean mFillAfter = false; + + /** + * Indicates whether fillBefore should be taken into account. + */ + boolean mFillEnabled = false; + + /** + * The time in milliseconds at which the animation must start; + */ + long mStartTime = -1; + + /** + * The delay in milliseconds after which the animation must start. When the + * start offset is > 0, the start time of the animation is startTime + startOffset. + */ + long mStartOffset; + + /** + * The duration of one animation cycle in milliseconds. + */ + long mDuration; + + /** + * The number of times the animation must repeat. By default, an animation repeats + * indefinitely. + */ + int mRepeatCount = 0; + + /** + * Indicates how many times the animation was repeated. + */ + int mRepeated = 0; + + /** + * The behavior of the animation when it repeats. The repeat mode is either + * {@link #RESTART} or {@link #REVERSE}. + * + */ + int mRepeatMode = RESTART; + + /** + * The interpolator used by the animation to smooth the movement. + */ + Interpolator mInterpolator; + + /** + * The animation listener to be notified when the animation starts, ends or repeats. + */ + AnimationListener mListener; + + /** + * Desired Z order mode during animation. + */ + private int mZAdjustment; + + /** + * Desired background color behind animation. + */ + private int mBackgroundColor; + + /** + * scalefactor to apply to pivot points, etc. during animation. Subclasses retrieve the + * value via getScaleFactor(). + */ + private float mScaleFactor = 1f; + + /** + * Don't animate the wallpaper. + */ + private boolean mDetachWallpaper = false; + + private boolean mMore = true; + private boolean mOneMoreTime = true; + + RectF mPreviousRegion = new RectF(); + RectF mRegion = new RectF(); + Transformation mTransformation = new Transformation(); + Transformation mPreviousTransformation = new Transformation(); + + private final CloseGuard guard = CloseGuard.get(); + + private Handler mListenerHandler; + private Runnable mOnStart; + private Runnable mOnRepeat; + private Runnable mOnEnd; + + /** + * Creates a new animation with a duration of 0ms, the default interpolator, with + * fillBefore set to true and fillAfter set to false + */ + public Animation() { + ensureInterpolator(); + } + + /** + * Creates a new animation whose parameters come from the specified context and + * attributes set. + * + * @param context the application environment + * @param attrs the set of attributes holding the animation parameters + */ + public Animation(Context context, AttributeSet attrs) { + TypedArray a = context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.Animation); + + setDuration((long) a.getInt(com.android.internal.R.styleable.Animation_duration, 0)); + setStartOffset((long) a.getInt(com.android.internal.R.styleable.Animation_startOffset, 0)); + + setFillEnabled(a.getBoolean(com.android.internal.R.styleable.Animation_fillEnabled, mFillEnabled)); + setFillBefore(a.getBoolean(com.android.internal.R.styleable.Animation_fillBefore, mFillBefore)); + setFillAfter(a.getBoolean(com.android.internal.R.styleable.Animation_fillAfter, mFillAfter)); + + setRepeatCount(a.getInt(com.android.internal.R.styleable.Animation_repeatCount, mRepeatCount)); + setRepeatMode(a.getInt(com.android.internal.R.styleable.Animation_repeatMode, RESTART)); + + setZAdjustment(a.getInt(com.android.internal.R.styleable.Animation_zAdjustment, ZORDER_NORMAL)); + + setBackgroundColor(a.getInt(com.android.internal.R.styleable.Animation_background, 0)); + + setDetachWallpaper(a.getBoolean(com.android.internal.R.styleable.Animation_detachWallpaper, false)); + + final int resID = a.getResourceId(com.android.internal.R.styleable.Animation_interpolator, 0); + + a.recycle(); + + if (resID > 0) { + setInterpolator(context, resID); + } + + ensureInterpolator(); + } + + @Override + protected Animation clone() throws CloneNotSupportedException { + final Animation animation = (Animation) super.clone(); + animation.mPreviousRegion = new RectF(); + animation.mRegion = new RectF(); + animation.mTransformation = new Transformation(); + animation.mPreviousTransformation = new Transformation(); + return animation; + } + + /** + * Reset the initialization state of this animation. + * + * @see #initialize(int, int, int, int) + */ + public void reset() { + mPreviousRegion.setEmpty(); + mPreviousTransformation.clear(); + mInitialized = false; + mCycleFlip = false; + mRepeated = 0; + mMore = true; + mOneMoreTime = true; + mListenerHandler = null; + } + + /** + * Cancel the animation. Cancelling an animation invokes the animation + * listener, if set, to notify the end of the animation. + * + * If you cancel an animation manually, you must call {@link #reset()} + * before starting the animation again. + * + * @see #reset() + * @see #start() + * @see #startNow() + */ + public void cancel() { + if (mStarted && !mEnded) { + fireAnimationEnd(); + mEnded = true; + guard.close(); + } + // Make sure we move the animation to the end + mStartTime = Long.MIN_VALUE; + mMore = mOneMoreTime = false; + } + + /** + * @hide + */ + public void detach() { + if (mStarted && !mEnded) { + mEnded = true; + guard.close(); + fireAnimationEnd(); + } + } + + /** + * Whether or not the animation has been initialized. + * + * @return Has this animation been initialized. + * @see #initialize(int, int, int, int) + */ + public boolean isInitialized() { + return mInitialized; + } + + /** + * Initialize this animation with the dimensions of the object being + * animated as well as the objects parents. (This is to support animation + * sizes being specified relative to these dimensions.) + * + * <p>Objects that interpret Animations should call this method when + * the sizes of the object being animated and its parent are known, and + * before calling {@link #getTransformation}. + * + * + * @param width Width of the object being animated + * @param height Height of the object being animated + * @param parentWidth Width of the animated object's parent + * @param parentHeight Height of the animated object's parent + */ + public void initialize(int width, int height, int parentWidth, int parentHeight) { + reset(); + mInitialized = true; + } + + /** + * Sets the handler used to invoke listeners. + * + * @hide + */ + public void setListenerHandler(Handler handler) { + if (mListenerHandler == null) { + mOnStart = new Runnable() { + public void run() { + if (mListener != null) { + mListener.onAnimationStart(Animation.this); + } + } + }; + mOnRepeat = new Runnable() { + public void run() { + if (mListener != null) { + mListener.onAnimationRepeat(Animation.this); + } + } + }; + mOnEnd = new Runnable() { + public void run() { + if (mListener != null) { + mListener.onAnimationEnd(Animation.this); + } + } + }; + } + mListenerHandler = handler; + } + + /** + * Sets the acceleration curve for this animation. The interpolator is loaded as + * a resource from the specified context. + * + * @param context The application environment + * @param resID The resource identifier of the interpolator to load + * @attr ref android.R.styleable#Animation_interpolator + */ + public void setInterpolator(Context context, @AnimRes @InterpolatorRes int resID) { + setInterpolator(AnimationUtils.loadInterpolator(context, resID)); + } + + /** + * Sets the acceleration curve for this animation. Defaults to a linear + * interpolation. + * + * @param i The interpolator which defines the acceleration curve + * @attr ref android.R.styleable#Animation_interpolator + */ + public void setInterpolator(Interpolator i) { + mInterpolator = i; + } + + /** + * When this animation should start relative to the start time. This is most + * useful when composing complex animations using an {@link AnimationSet } + * where some of the animations components start at different times. + * + * @param startOffset When this Animation should start, in milliseconds from + * the start time of the root AnimationSet. + * @attr ref android.R.styleable#Animation_startOffset + */ + public void setStartOffset(long startOffset) { + mStartOffset = startOffset; + } + + /** + * How long this animation should last. The duration cannot be negative. + * + * @param durationMillis Duration in milliseconds + * + * @throws java.lang.IllegalArgumentException if the duration is < 0 + * + * @attr ref android.R.styleable#Animation_duration + */ + public void setDuration(long durationMillis) { + if (durationMillis < 0) { + throw new IllegalArgumentException("Animation duration cannot be negative"); + } + mDuration = durationMillis; + } + + /** + * Ensure that the duration that this animation will run is not longer + * than <var>durationMillis</var>. In addition to adjusting the duration + * itself, this ensures that the repeat count also will not make it run + * longer than the given time. + * + * @param durationMillis The maximum duration the animation is allowed + * to run. + */ + public void restrictDuration(long durationMillis) { + // If we start after the duration, then we just won't run. + if (mStartOffset > durationMillis) { + mStartOffset = durationMillis; + mDuration = 0; + mRepeatCount = 0; + return; + } + + long dur = mDuration + mStartOffset; + if (dur > durationMillis) { + mDuration = durationMillis-mStartOffset; + dur = durationMillis; + } + // If the duration is 0 or less, then we won't run. + if (mDuration <= 0) { + mDuration = 0; + mRepeatCount = 0; + return; + } + // Reduce the number of repeats to keep below the maximum duration. + // The comparison between mRepeatCount and duration is to catch + // overflows after multiplying them. + if (mRepeatCount < 0 || mRepeatCount > durationMillis + || (dur*mRepeatCount) > durationMillis) { + // Figure out how many times to do the animation. Subtract 1 since + // repeat count is the number of times to repeat so 0 runs once. + mRepeatCount = (int)(durationMillis/dur) - 1; + if (mRepeatCount < 0) { + mRepeatCount = 0; + } + } + } + + /** + * How much to scale the duration by. + * + * @param scale The amount to scale the duration. + */ + public void scaleCurrentDuration(float scale) { + mDuration = (long) (mDuration * scale); + mStartOffset = (long) (mStartOffset * scale); + } + + /** + * When this animation should start. When the start time is set to + * {@link #START_ON_FIRST_FRAME}, the animation will start the first time + * {@link #getTransformation(long, Transformation)} is invoked. The time passed + * to this method should be obtained by calling + * {@link AnimationUtils#currentAnimationTimeMillis()} instead of + * {@link System#currentTimeMillis()}. + * + * @param startTimeMillis the start time in milliseconds + */ + public void setStartTime(long startTimeMillis) { + mStartTime = startTimeMillis; + mStarted = mEnded = false; + mCycleFlip = false; + mRepeated = 0; + mMore = true; + } + + /** + * Convenience method to start the animation the first time + * {@link #getTransformation(long, Transformation)} is invoked. + */ + public void start() { + setStartTime(-1); + } + + /** + * Convenience method to start the animation at the current time in + * milliseconds. + */ + public void startNow() { + setStartTime(AnimationUtils.currentAnimationTimeMillis()); + } + + /** + * Defines what this animation should do when it reaches the end. This + * setting is applied only when the repeat count is either greater than + * 0 or {@link #INFINITE}. Defaults to {@link #RESTART}. + * + * @param repeatMode {@link #RESTART} or {@link #REVERSE} + * @attr ref android.R.styleable#Animation_repeatMode + */ + public void setRepeatMode(int repeatMode) { + mRepeatMode = repeatMode; + } + + /** + * Sets how many times the animation should be repeated. If the repeat + * count is 0, the animation is never repeated. If the repeat count is + * greater than 0 or {@link #INFINITE}, the repeat mode will be taken + * into account. The repeat count is 0 by default. + * + * @param repeatCount the number of times the animation should be repeated + * @attr ref android.R.styleable#Animation_repeatCount + */ + public void setRepeatCount(int repeatCount) { + if (repeatCount < 0) { + repeatCount = INFINITE; + } + mRepeatCount = repeatCount; + } + + /** + * If fillEnabled is true, this animation will apply the value of fillBefore. + * + * @return true if the animation will take fillBefore into account + * @attr ref android.R.styleable#Animation_fillEnabled + */ + public boolean isFillEnabled() { + return mFillEnabled; + } + + /** + * If fillEnabled is true, the animation will apply the value of fillBefore. + * Otherwise, fillBefore is ignored and the animation + * transformation is always applied until the animation ends. + * + * @param fillEnabled true if the animation should take the value of fillBefore into account + * @attr ref android.R.styleable#Animation_fillEnabled + * + * @see #setFillBefore(boolean) + * @see #setFillAfter(boolean) + */ + public void setFillEnabled(boolean fillEnabled) { + mFillEnabled = fillEnabled; + } + + /** + * If fillBefore is true, this animation will apply its transformation + * before the start time of the animation. Defaults to true if + * {@link #setFillEnabled(boolean)} is not set to true. + * Note that this applies when using an {@link + * android.view.animation.AnimationSet AnimationSet} to chain + * animations. The transformation is not applied before the AnimationSet + * itself starts. + * + * @param fillBefore true if the animation should apply its transformation before it starts + * @attr ref android.R.styleable#Animation_fillBefore + * + * @see #setFillEnabled(boolean) + */ + public void setFillBefore(boolean fillBefore) { + mFillBefore = fillBefore; + } + + /** + * If fillAfter is true, the transformation that this animation performed + * will persist when it is finished. Defaults to false if not set. + * Note that this applies to individual animations and when using an {@link + * android.view.animation.AnimationSet AnimationSet} to chain + * animations. + * + * @param fillAfter true if the animation should apply its transformation after it ends + * @attr ref android.R.styleable#Animation_fillAfter + * + * @see #setFillEnabled(boolean) + */ + public void setFillAfter(boolean fillAfter) { + mFillAfter = fillAfter; + } + + /** + * Set the Z ordering mode to use while running the animation. + * + * @param zAdjustment The desired mode, one of {@link #ZORDER_NORMAL}, + * {@link #ZORDER_TOP}, or {@link #ZORDER_BOTTOM}. + * @attr ref android.R.styleable#Animation_zAdjustment + */ + public void setZAdjustment(int zAdjustment) { + mZAdjustment = zAdjustment; + } + + /** + * Set background behind animation. + * + * @param bg The background color. If 0, no background. Currently must + * be black, with any desired alpha level. + */ + public void setBackgroundColor(@ColorInt int bg) { + mBackgroundColor = bg; + } + + /** + * The scale factor is set by the call to <code>getTransformation</code>. Overrides of + * {@link #getTransformation(long, Transformation, float)} will get this value + * directly. Overrides of {@link #applyTransformation(float, Transformation)} can + * call this method to get the value. + * + * @return float The scale factor that should be applied to pre-scaled values in + * an Animation such as the pivot points in {@link ScaleAnimation} and {@link RotateAnimation}. + */ + protected float getScaleFactor() { + return mScaleFactor; + } + + /** + * If detachWallpaper is true, and this is a window animation of a window + * that has a wallpaper background, then the window will be detached from + * the wallpaper while it runs. That is, the animation will only be applied + * to the window, and the wallpaper behind it will remain static. + * + * @param detachWallpaper true if the wallpaper should be detached from the animation + * @attr ref android.R.styleable#Animation_detachWallpaper + */ + public void setDetachWallpaper(boolean detachWallpaper) { + mDetachWallpaper = detachWallpaper; + } + + /** + * Gets the acceleration curve type for this animation. + * + * @return the {@link Interpolator} associated to this animation + * @attr ref android.R.styleable#Animation_interpolator + */ + public Interpolator getInterpolator() { + return mInterpolator; + } + + /** + * When this animation should start. If the animation has not startet yet, + * this method might return {@link #START_ON_FIRST_FRAME}. + * + * @return the time in milliseconds when the animation should start or + * {@link #START_ON_FIRST_FRAME} + */ + public long getStartTime() { + return mStartTime; + } + + /** + * How long this animation should last + * + * @return the duration in milliseconds of the animation + * @attr ref android.R.styleable#Animation_duration + */ + public long getDuration() { + return mDuration; + } + + /** + * When this animation should start, relative to StartTime + * + * @return the start offset in milliseconds + * @attr ref android.R.styleable#Animation_startOffset + */ + public long getStartOffset() { + return mStartOffset; + } + + /** + * Defines what this animation should do when it reaches the end. + * + * @return either one of {@link #REVERSE} or {@link #RESTART} + * @attr ref android.R.styleable#Animation_repeatMode + */ + public int getRepeatMode() { + return mRepeatMode; + } + + /** + * Defines how many times the animation should repeat. The default value + * is 0. + * + * @return the number of times the animation should repeat, or {@link #INFINITE} + * @attr ref android.R.styleable#Animation_repeatCount + */ + public int getRepeatCount() { + return mRepeatCount; + } + + /** + * If fillBefore is true, this animation will apply its transformation + * before the start time of the animation. If fillBefore is false and + * {@link #isFillEnabled() fillEnabled} is true, the transformation will not be applied until + * the start time of the animation. + * + * @return true if the animation applies its transformation before it starts + * @attr ref android.R.styleable#Animation_fillBefore + */ + public boolean getFillBefore() { + return mFillBefore; + } + + /** + * If fillAfter is true, this animation will apply its transformation + * after the end time of the animation. + * + * @return true if the animation applies its transformation after it ends + * @attr ref android.R.styleable#Animation_fillAfter + */ + public boolean getFillAfter() { + return mFillAfter; + } + + /** + * Returns the Z ordering mode to use while running the animation as + * previously set by {@link #setZAdjustment}. + * + * @return Returns one of {@link #ZORDER_NORMAL}, + * {@link #ZORDER_TOP}, or {@link #ZORDER_BOTTOM}. + * @attr ref android.R.styleable#Animation_zAdjustment + */ + public int getZAdjustment() { + return mZAdjustment; + } + + /** + * Returns the background color behind the animation. + */ + @ColorInt + public int getBackgroundColor() { + return mBackgroundColor; + } + + /** + * Return value of {@link #setDetachWallpaper(boolean)}. + * @attr ref android.R.styleable#Animation_detachWallpaper + */ + public boolean getDetachWallpaper() { + return mDetachWallpaper; + } + + /** + * <p>Indicates whether or not this animation will affect the transformation + * matrix. For instance, a fade animation will not affect the matrix whereas + * a scale animation will.</p> + * + * @return true if this animation will change the transformation matrix + */ + public boolean willChangeTransformationMatrix() { + // assume we will change the matrix + return true; + } + + /** + * <p>Indicates whether or not this animation will affect the bounds of the + * animated view. For instance, a fade animation will not affect the bounds + * whereas a 200% scale animation will.</p> + * + * @return true if this animation will change the view's bounds + */ + public boolean willChangeBounds() { + // assume we will change the bounds + return true; + } + + /** + * <p>Binds an animation listener to this animation. The animation listener + * is notified of animation events such as the end of the animation or the + * repetition of the animation.</p> + * + * @param listener the animation listener to be notified + */ + public void setAnimationListener(AnimationListener listener) { + mListener = listener; + } + + /** + * Gurantees that this animation has an interpolator. Will use + * a AccelerateDecelerateInterpolator is nothing else was specified. + */ + protected void ensureInterpolator() { + if (mInterpolator == null) { + mInterpolator = new AccelerateDecelerateInterpolator(); + } + } + + /** + * Compute a hint at how long the entire animation may last, in milliseconds. + * Animations can be written to cause themselves to run for a different + * duration than what is computed here, but generally this should be + * accurate. + */ + public long computeDurationHint() { + return (getStartOffset() + getDuration()) * (getRepeatCount() + 1); + } + + /** + * Gets the transformation to apply at a specified point in time. Implementations of this + * method should always replace the specified Transformation or document they are doing + * otherwise. + * + * @param currentTime Where we are in the animation. This is wall clock time. + * @param outTransformation A transformation object that is provided by the + * caller and will be filled in by the animation. + * @return True if the animation is still running + */ + public boolean getTransformation(long currentTime, Transformation outTransformation) { + if (mStartTime == -1) { + mStartTime = currentTime; + } + + final long startOffset = getStartOffset(); + final long duration = mDuration; + float normalizedTime; + if (duration != 0) { + normalizedTime = ((float) (currentTime - (mStartTime + startOffset))) / + (float) duration; + } else { + // time is a step-change with a zero duration + normalizedTime = currentTime < mStartTime ? 0.0f : 1.0f; + } + + final boolean expired = normalizedTime >= 1.0f || isCanceled(); + mMore = !expired; + + if (!mFillEnabled) normalizedTime = Math.max(Math.min(normalizedTime, 1.0f), 0.0f); + + if ((normalizedTime >= 0.0f || mFillBefore) && (normalizedTime <= 1.0f || mFillAfter)) { + if (!mStarted) { + fireAnimationStart(); + mStarted = true; + if (NoImagePreloadHolder.USE_CLOSEGUARD) { + guard.open("cancel or detach or getTransformation"); + } + } + + if (mFillEnabled) normalizedTime = Math.max(Math.min(normalizedTime, 1.0f), 0.0f); + + if (mCycleFlip) { + normalizedTime = 1.0f - normalizedTime; + } + + final float interpolatedTime = mInterpolator.getInterpolation(normalizedTime); + applyTransformation(interpolatedTime, outTransformation); + } + + if (expired) { + if (mRepeatCount == mRepeated || isCanceled()) { + if (!mEnded) { + mEnded = true; + guard.close(); + fireAnimationEnd(); + } + } else { + if (mRepeatCount > 0) { + mRepeated++; + } + + if (mRepeatMode == REVERSE) { + mCycleFlip = !mCycleFlip; + } + + mStartTime = -1; + mMore = true; + + fireAnimationRepeat(); + } + } + + if (!mMore && mOneMoreTime) { + mOneMoreTime = false; + return true; + } + + return mMore; + } + + private boolean isCanceled() { + return mStartTime == Long.MIN_VALUE; + } + + private void fireAnimationStart() { + if (mListener != null) { + if (mListenerHandler == null) mListener.onAnimationStart(this); + else mListenerHandler.postAtFrontOfQueue(mOnStart); + } + } + + private void fireAnimationRepeat() { + if (mListener != null) { + if (mListenerHandler == null) mListener.onAnimationRepeat(this); + else mListenerHandler.postAtFrontOfQueue(mOnRepeat); + } + } + + private void fireAnimationEnd() { + if (mListener != null) { + if (mListenerHandler == null) mListener.onAnimationEnd(this); + else mListenerHandler.postAtFrontOfQueue(mOnEnd); + } + } + + /** + * Gets the transformation to apply at a specified point in time. Implementations of this + * method should always replace the specified Transformation or document they are doing + * otherwise. + * + * @param currentTime Where we are in the animation. This is wall clock time. + * @param outTransformation A transformation object that is provided by the + * caller and will be filled in by the animation. + * @param scale Scaling factor to apply to any inputs to the transform operation, such + * pivot points being rotated or scaled around. + * @return True if the animation is still running + */ + public boolean getTransformation(long currentTime, Transformation outTransformation, + float scale) { + mScaleFactor = scale; + return getTransformation(currentTime, outTransformation); + } + + /** + * <p>Indicates whether this animation has started or not.</p> + * + * @return true if the animation has started, false otherwise + */ + public boolean hasStarted() { + return mStarted; + } + + /** + * <p>Indicates whether this animation has ended or not.</p> + * + * @return true if the animation has ended, false otherwise + */ + public boolean hasEnded() { + return mEnded; + } + + /** + * Helper for getTransformation. Subclasses should implement this to apply + * their transforms given an interpolation value. Implementations of this + * method should always replace the specified Transformation or document + * they are doing otherwise. + * + * @param interpolatedTime The value of the normalized time (0.0 to 1.0) + * after it has been run through the interpolation function. + * @param t The Transformation object to fill in with the current + * transforms. + */ + protected void applyTransformation(float interpolatedTime, Transformation t) { + } + + /** + * Convert the information in the description of a size to an actual + * dimension + * + * @param type One of Animation.ABSOLUTE, Animation.RELATIVE_TO_SELF, or + * Animation.RELATIVE_TO_PARENT. + * @param value The dimension associated with the type parameter + * @param size The size of the object being animated + * @param parentSize The size of the parent of the object being animated + * @return The dimension to use for the animation + */ + protected float resolveSize(int type, float value, int size, int parentSize) { + switch (type) { + case ABSOLUTE: + return value; + case RELATIVE_TO_SELF: + return size * value; + case RELATIVE_TO_PARENT: + return parentSize * value; + default: + return value; + } + } + + /** + * @param left + * @param top + * @param right + * @param bottom + * @param invalidate + * @param transformation + * + * @hide + */ + public void getInvalidateRegion(int left, int top, int right, int bottom, + RectF invalidate, Transformation transformation) { + + final RectF tempRegion = mRegion; + final RectF previousRegion = mPreviousRegion; + + invalidate.set(left, top, right, bottom); + transformation.getMatrix().mapRect(invalidate); + // Enlarge the invalidate region to account for rounding errors + invalidate.inset(-1.0f, -1.0f); + tempRegion.set(invalidate); + invalidate.union(previousRegion); + + previousRegion.set(tempRegion); + + final Transformation tempTransformation = mTransformation; + final Transformation previousTransformation = mPreviousTransformation; + + tempTransformation.set(transformation); + transformation.set(previousTransformation); + previousTransformation.set(tempTransformation); + } + + /** + * @param left + * @param top + * @param right + * @param bottom + * + * @hide + */ + public void initializeInvalidateRegion(int left, int top, int right, int bottom) { + final RectF region = mPreviousRegion; + region.set(left, top, right, bottom); + // Enlarge the invalidate region to account for rounding errors + region.inset(-1.0f, -1.0f); + if (mFillBefore) { + final Transformation previousTransformation = mPreviousTransformation; + applyTransformation(mInterpolator.getInterpolation(0.0f), previousTransformation); + } + } + + protected void finalize() throws Throwable { + try { + if (guard != null) { + guard.warnIfOpen(); + } + } finally { + super.finalize(); + } + } + + /** + * Return true if this animation changes the view's alpha property. + * + * @hide + */ + public boolean hasAlpha() { + return false; + } + + /** + * Utility class to parse a string description of a size. + */ + protected static class Description { + /** + * One of Animation.ABSOLUTE, Animation.RELATIVE_TO_SELF, or + * Animation.RELATIVE_TO_PARENT. + */ + public int type; + + /** + * The absolute or relative dimension for this Description. + */ + public float value; + + /** + * Size descriptions can appear inthree forms: + * <ol> + * <li>An absolute size. This is represented by a number.</li> + * <li>A size relative to the size of the object being animated. This + * is represented by a number followed by "%".</li> * + * <li>A size relative to the size of the parent of object being + * animated. This is represented by a number followed by "%p".</li> + * </ol> + * @param value The typed value to parse + * @return The parsed version of the description + */ + static Description parseValue(TypedValue value) { + Description d = new Description(); + if (value == null) { + d.type = ABSOLUTE; + d.value = 0; + } else { + if (value.type == TypedValue.TYPE_FRACTION) { + d.type = (value.data & TypedValue.COMPLEX_UNIT_MASK) == + TypedValue.COMPLEX_UNIT_FRACTION_PARENT ? + RELATIVE_TO_PARENT : RELATIVE_TO_SELF; + d.value = TypedValue.complexToFloat(value.data); + return d; + } else if (value.type == TypedValue.TYPE_FLOAT) { + d.type = ABSOLUTE; + d.value = value.getFloat(); + return d; + } else if (value.type >= TypedValue.TYPE_FIRST_INT && + value.type <= TypedValue.TYPE_LAST_INT) { + d.type = ABSOLUTE; + d.value = value.data; + return d; + } + } + + d.type = ABSOLUTE; + d.value = 0.0f; + + return d; + } + } + + /** + * <p>An animation listener receives notifications from an animation. + * Notifications indicate animation related events, such as the end or the + * repetition of the animation.</p> + */ + public static interface AnimationListener { + /** + * <p>Notifies the start of the animation.</p> + * + * @param animation The started animation. + */ + void onAnimationStart(Animation animation); + + /** + * <p>Notifies the end of the animation. This callback is not invoked + * for animations with repeat count set to INFINITE.</p> + * + * @param animation The animation which reached its end. + */ + void onAnimationEnd(Animation animation); + + /** + * <p>Notifies the repetition of the animation.</p> + * + * @param animation The animation which was repeated. + */ + void onAnimationRepeat(Animation animation); + } +} diff --git a/android/view/animation/AnimationSet.java b/android/view/animation/AnimationSet.java new file mode 100644 index 00000000..767024ec --- /dev/null +++ b/android/view/animation/AnimationSet.java @@ -0,0 +1,524 @@ +/* + * Copyright (C) 2006 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.animation; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.RectF; +import android.os.Build; +import android.util.AttributeSet; + +import java.util.ArrayList; +import java.util.List; + +/** + * Represents a group of Animations that should be played together. + * The transformation of each individual animation are composed + * together into a single transform. + * If AnimationSet sets any properties that its children also set + * (for example, duration or fillBefore), the values of AnimationSet + * override the child values. + * + * <p>The way that AnimationSet inherits behavior from Animation is important to + * understand. Some of the Animation attributes applied to AnimationSet affect the + * AnimationSet itself, some are pushed down to the children, and some are ignored, + * as follows: + * <ul> + * <li>duration, repeatMode, fillBefore, fillAfter: These properties, when set + * on an AnimationSet object, will be pushed down to all child animations.</li> + * <li>repeatCount, fillEnabled: These properties are ignored for AnimationSet.</li> + * <li>startOffset, shareInterpolator: These properties apply to the AnimationSet itself.</li> + * </ul> + * Starting with {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH}, + * the behavior of these properties is the same in XML resources and at runtime (prior to that + * release, the values set in XML were ignored for AnimationSet). That is, calling + * <code>setDuration(500)</code> on an AnimationSet has the same effect as declaring + * <code>android:duration="500"</code> in an XML resource for an AnimationSet object.</p> + */ +public class AnimationSet extends Animation { + private static final int PROPERTY_FILL_AFTER_MASK = 0x1; + private static final int PROPERTY_FILL_BEFORE_MASK = 0x2; + private static final int PROPERTY_REPEAT_MODE_MASK = 0x4; + private static final int PROPERTY_START_OFFSET_MASK = 0x8; + private static final int PROPERTY_SHARE_INTERPOLATOR_MASK = 0x10; + private static final int PROPERTY_DURATION_MASK = 0x20; + private static final int PROPERTY_MORPH_MATRIX_MASK = 0x40; + private static final int PROPERTY_CHANGE_BOUNDS_MASK = 0x80; + + private int mFlags = 0; + private boolean mDirty; + private boolean mHasAlpha; + + private ArrayList<Animation> mAnimations = new ArrayList<Animation>(); + + private Transformation mTempTransformation = new Transformation(); + + private long mLastEnd; + + private long[] mStoredOffsets; + + /** + * Constructor used when an AnimationSet is loaded from a resource. + * + * @param context Application context to use + * @param attrs Attribute set from which to read values + */ + public AnimationSet(Context context, AttributeSet attrs) { + super(context, attrs); + + TypedArray a = + context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.AnimationSet); + + setFlag(PROPERTY_SHARE_INTERPOLATOR_MASK, + a.getBoolean(com.android.internal.R.styleable.AnimationSet_shareInterpolator, true)); + init(); + + if (context.getApplicationInfo().targetSdkVersion >= + Build.VERSION_CODES.ICE_CREAM_SANDWICH) { + if (a.hasValue(com.android.internal.R.styleable.AnimationSet_duration)) { + mFlags |= PROPERTY_DURATION_MASK; + } + if (a.hasValue(com.android.internal.R.styleable.AnimationSet_fillBefore)) { + mFlags |= PROPERTY_FILL_BEFORE_MASK; + } + if (a.hasValue(com.android.internal.R.styleable.AnimationSet_fillAfter)) { + mFlags |= PROPERTY_FILL_AFTER_MASK; + } + if (a.hasValue(com.android.internal.R.styleable.AnimationSet_repeatMode)) { + mFlags |= PROPERTY_REPEAT_MODE_MASK; + } + if (a.hasValue(com.android.internal.R.styleable.AnimationSet_startOffset)) { + mFlags |= PROPERTY_START_OFFSET_MASK; + } + } + + a.recycle(); + } + + + /** + * Constructor to use when building an AnimationSet from code + * + * @param shareInterpolator Pass true if all of the animations in this set + * should use the interpolator associated with this AnimationSet. + * Pass false if each animation should use its own interpolator. + */ + public AnimationSet(boolean shareInterpolator) { + setFlag(PROPERTY_SHARE_INTERPOLATOR_MASK, shareInterpolator); + init(); + } + + @Override + protected AnimationSet clone() throws CloneNotSupportedException { + final AnimationSet animation = (AnimationSet) super.clone(); + animation.mTempTransformation = new Transformation(); + animation.mAnimations = new ArrayList<Animation>(); + + final int count = mAnimations.size(); + final ArrayList<Animation> animations = mAnimations; + + for (int i = 0; i < count; i++) { + animation.mAnimations.add(animations.get(i).clone()); + } + + return animation; + } + + private void setFlag(int mask, boolean value) { + if (value) { + mFlags |= mask; + } else { + mFlags &= ~mask; + } + } + + private void init() { + mStartTime = 0; + } + + @Override + public void setFillAfter(boolean fillAfter) { + mFlags |= PROPERTY_FILL_AFTER_MASK; + super.setFillAfter(fillAfter); + } + + @Override + public void setFillBefore(boolean fillBefore) { + mFlags |= PROPERTY_FILL_BEFORE_MASK; + super.setFillBefore(fillBefore); + } + + @Override + public void setRepeatMode(int repeatMode) { + mFlags |= PROPERTY_REPEAT_MODE_MASK; + super.setRepeatMode(repeatMode); + } + + @Override + public void setStartOffset(long startOffset) { + mFlags |= PROPERTY_START_OFFSET_MASK; + super.setStartOffset(startOffset); + } + + /** + * @hide + */ + @Override + public boolean hasAlpha() { + if (mDirty) { + mDirty = mHasAlpha = false; + + final int count = mAnimations.size(); + final ArrayList<Animation> animations = mAnimations; + + for (int i = 0; i < count; i++) { + if (animations.get(i).hasAlpha()) { + mHasAlpha = true; + break; + } + } + } + + return mHasAlpha; + } + + /** + * <p>Sets the duration of every child animation.</p> + * + * @param durationMillis the duration of the animation, in milliseconds, for + * every child in this set + */ + @Override + public void setDuration(long durationMillis) { + mFlags |= PROPERTY_DURATION_MASK; + super.setDuration(durationMillis); + mLastEnd = mStartOffset + mDuration; + } + + /** + * Add a child animation to this animation set. + * The transforms of the child animations are applied in the order + * that they were added + * @param a Animation to add. + */ + public void addAnimation(Animation a) { + mAnimations.add(a); + + boolean noMatrix = (mFlags & PROPERTY_MORPH_MATRIX_MASK) == 0; + if (noMatrix && a.willChangeTransformationMatrix()) { + mFlags |= PROPERTY_MORPH_MATRIX_MASK; + } + + boolean changeBounds = (mFlags & PROPERTY_CHANGE_BOUNDS_MASK) == 0; + + + if (changeBounds && a.willChangeBounds()) { + mFlags |= PROPERTY_CHANGE_BOUNDS_MASK; + } + + if ((mFlags & PROPERTY_DURATION_MASK) == PROPERTY_DURATION_MASK) { + mLastEnd = mStartOffset + mDuration; + } else { + if (mAnimations.size() == 1) { + mDuration = a.getStartOffset() + a.getDuration(); + mLastEnd = mStartOffset + mDuration; + } else { + mLastEnd = Math.max(mLastEnd, mStartOffset + a.getStartOffset() + a.getDuration()); + mDuration = mLastEnd - mStartOffset; + } + } + + mDirty = true; + } + + /** + * Sets the start time of this animation and all child animations + * + * @see android.view.animation.Animation#setStartTime(long) + */ + @Override + public void setStartTime(long startTimeMillis) { + super.setStartTime(startTimeMillis); + + final int count = mAnimations.size(); + final ArrayList<Animation> animations = mAnimations; + + for (int i = 0; i < count; i++) { + Animation a = animations.get(i); + a.setStartTime(startTimeMillis); + } + } + + @Override + public long getStartTime() { + long startTime = Long.MAX_VALUE; + + final int count = mAnimations.size(); + final ArrayList<Animation> animations = mAnimations; + + for (int i = 0; i < count; i++) { + Animation a = animations.get(i); + startTime = Math.min(startTime, a.getStartTime()); + } + + return startTime; + } + + @Override + public void restrictDuration(long durationMillis) { + super.restrictDuration(durationMillis); + + final ArrayList<Animation> animations = mAnimations; + int count = animations.size(); + + for (int i = 0; i < count; i++) { + animations.get(i).restrictDuration(durationMillis); + } + } + + /** + * The duration of an AnimationSet is defined to be the + * duration of the longest child animation. + * + * @see android.view.animation.Animation#getDuration() + */ + @Override + public long getDuration() { + final ArrayList<Animation> animations = mAnimations; + final int count = animations.size(); + long duration = 0; + + boolean durationSet = (mFlags & PROPERTY_DURATION_MASK) == PROPERTY_DURATION_MASK; + if (durationSet) { + duration = mDuration; + } else { + for (int i = 0; i < count; i++) { + duration = Math.max(duration, animations.get(i).getDuration()); + } + } + + return duration; + } + + /** + * The duration hint of an animation set is the maximum of the duration + * hints of all of its component animations. + * + * @see android.view.animation.Animation#computeDurationHint + */ + public long computeDurationHint() { + long duration = 0; + final int count = mAnimations.size(); + final ArrayList<Animation> animations = mAnimations; + for (int i = count - 1; i >= 0; --i) { + final long d = animations.get(i).computeDurationHint(); + if (d > duration) duration = d; + } + return duration; + } + + /** + * @hide + */ + public void initializeInvalidateRegion(int left, int top, int right, int bottom) { + final RectF region = mPreviousRegion; + region.set(left, top, right, bottom); + region.inset(-1.0f, -1.0f); + + if (mFillBefore) { + final int count = mAnimations.size(); + final ArrayList<Animation> animations = mAnimations; + final Transformation temp = mTempTransformation; + + final Transformation previousTransformation = mPreviousTransformation; + + for (int i = count - 1; i >= 0; --i) { + final Animation a = animations.get(i); + if (!a.isFillEnabled() || a.getFillBefore() || a.getStartOffset() == 0) { + temp.clear(); + final Interpolator interpolator = a.mInterpolator; + a.applyTransformation(interpolator != null ? interpolator.getInterpolation(0.0f) + : 0.0f, temp); + previousTransformation.compose(temp); + } + } + } + } + + /** + * The transformation of an animation set is the concatenation of all of its + * component animations. + * + * @see android.view.animation.Animation#getTransformation + */ + @Override + public boolean getTransformation(long currentTime, Transformation t) { + final int count = mAnimations.size(); + final ArrayList<Animation> animations = mAnimations; + final Transformation temp = mTempTransformation; + + boolean more = false; + boolean started = false; + boolean ended = true; + + t.clear(); + + for (int i = count - 1; i >= 0; --i) { + final Animation a = animations.get(i); + + temp.clear(); + more = a.getTransformation(currentTime, temp, getScaleFactor()) || more; + t.compose(temp); + + started = started || a.hasStarted(); + ended = a.hasEnded() && ended; + } + + if (started && !mStarted) { + if (mListener != null) { + mListener.onAnimationStart(this); + } + mStarted = true; + } + + if (ended != mEnded) { + if (mListener != null) { + mListener.onAnimationEnd(this); + } + mEnded = ended; + } + + return more; + } + + /** + * @see android.view.animation.Animation#scaleCurrentDuration(float) + */ + @Override + public void scaleCurrentDuration(float scale) { + final ArrayList<Animation> animations = mAnimations; + int count = animations.size(); + for (int i = 0; i < count; i++) { + animations.get(i).scaleCurrentDuration(scale); + } + } + + /** + * @see android.view.animation.Animation#initialize(int, int, int, int) + */ + @Override + public void initialize(int width, int height, int parentWidth, int parentHeight) { + super.initialize(width, height, parentWidth, parentHeight); + + boolean durationSet = (mFlags & PROPERTY_DURATION_MASK) == PROPERTY_DURATION_MASK; + boolean fillAfterSet = (mFlags & PROPERTY_FILL_AFTER_MASK) == PROPERTY_FILL_AFTER_MASK; + boolean fillBeforeSet = (mFlags & PROPERTY_FILL_BEFORE_MASK) == PROPERTY_FILL_BEFORE_MASK; + boolean repeatModeSet = (mFlags & PROPERTY_REPEAT_MODE_MASK) == PROPERTY_REPEAT_MODE_MASK; + boolean shareInterpolator = (mFlags & PROPERTY_SHARE_INTERPOLATOR_MASK) + == PROPERTY_SHARE_INTERPOLATOR_MASK; + boolean startOffsetSet = (mFlags & PROPERTY_START_OFFSET_MASK) + == PROPERTY_START_OFFSET_MASK; + + if (shareInterpolator) { + ensureInterpolator(); + } + + final ArrayList<Animation> children = mAnimations; + final int count = children.size(); + + final long duration = mDuration; + final boolean fillAfter = mFillAfter; + final boolean fillBefore = mFillBefore; + final int repeatMode = mRepeatMode; + final Interpolator interpolator = mInterpolator; + final long startOffset = mStartOffset; + + + long[] storedOffsets = mStoredOffsets; + if (startOffsetSet) { + if (storedOffsets == null || storedOffsets.length != count) { + storedOffsets = mStoredOffsets = new long[count]; + } + } else if (storedOffsets != null) { + storedOffsets = mStoredOffsets = null; + } + + for (int i = 0; i < count; i++) { + Animation a = children.get(i); + if (durationSet) { + a.setDuration(duration); + } + if (fillAfterSet) { + a.setFillAfter(fillAfter); + } + if (fillBeforeSet) { + a.setFillBefore(fillBefore); + } + if (repeatModeSet) { + a.setRepeatMode(repeatMode); + } + if (shareInterpolator) { + a.setInterpolator(interpolator); + } + if (startOffsetSet) { + long offset = a.getStartOffset(); + a.setStartOffset(offset + startOffset); + storedOffsets[i] = offset; + } + a.initialize(width, height, parentWidth, parentHeight); + } + } + + @Override + public void reset() { + super.reset(); + restoreChildrenStartOffset(); + } + + /** + * @hide + */ + void restoreChildrenStartOffset() { + final long[] offsets = mStoredOffsets; + if (offsets == null) return; + + final ArrayList<Animation> children = mAnimations; + final int count = children.size(); + + for (int i = 0; i < count; i++) { + children.get(i).setStartOffset(offsets[i]); + } + } + + /** + * @return All the child animations in this AnimationSet. Note that + * this may include other AnimationSets, which are not expanded. + */ + public List<Animation> getAnimations() { + return mAnimations; + } + + @Override + public boolean willChangeTransformationMatrix() { + return (mFlags & PROPERTY_MORPH_MATRIX_MASK) == PROPERTY_MORPH_MATRIX_MASK; + } + + @Override + public boolean willChangeBounds() { + return (mFlags & PROPERTY_CHANGE_BOUNDS_MASK) == PROPERTY_CHANGE_BOUNDS_MASK; + } +} diff --git a/android/view/animation/AnimationUtils.java b/android/view/animation/AnimationUtils.java new file mode 100644 index 00000000..f5c36139 --- /dev/null +++ b/android/view/animation/AnimationUtils.java @@ -0,0 +1,402 @@ +/* + * Copyright (C) 2007 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.animation; + +import android.annotation.AnimRes; +import android.annotation.InterpolatorRes; +import android.content.Context; +import android.content.res.Resources; +import android.content.res.Resources.NotFoundException; +import android.content.res.Resources.Theme; +import android.content.res.XmlResourceParser; +import android.os.SystemClock; +import android.util.AttributeSet; +import android.util.Xml; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; + +/** + * Defines common utilities for working with animations. + * + */ +public class AnimationUtils { + + /** + * These flags are used when parsing AnimatorSet objects + */ + private static final int TOGETHER = 0; + private static final int SEQUENTIALLY = 1; + + private static class AnimationState { + boolean animationClockLocked; + long currentVsyncTimeMillis; + long lastReportedTimeMillis; + }; + + private static ThreadLocal<AnimationState> sAnimationState + = new ThreadLocal<AnimationState>() { + @Override + protected AnimationState initialValue() { + return new AnimationState(); + } + }; + + /** @hide */ + public static void lockAnimationClock(long vsyncMillis) { + AnimationState state = sAnimationState.get(); + state.animationClockLocked = true; + state.currentVsyncTimeMillis = vsyncMillis; + } + + /** @hide */ + public static void unlockAnimationClock() { + sAnimationState.get().animationClockLocked = false; + } + + /** + * Returns the current animation time in milliseconds. This time should be used when invoking + * {@link Animation#setStartTime(long)}. Refer to {@link android.os.SystemClock} for more + * information about the different available clocks. The clock used by this method is + * <em>not</em> the "wall" clock (it is not {@link System#currentTimeMillis}). + * + * @return the current animation time in milliseconds + * + * @see android.os.SystemClock + */ + public static long currentAnimationTimeMillis() { + AnimationState state = sAnimationState.get(); + if (state.animationClockLocked) { + // It's important that time never rewinds + return Math.max(state.currentVsyncTimeMillis, + state.lastReportedTimeMillis); + } + state.lastReportedTimeMillis = SystemClock.uptimeMillis(); + return state.lastReportedTimeMillis; + } + + /** + * Loads an {@link Animation} object from a resource + * + * @param context Application context used to access resources + * @param id The resource id of the animation to load + * @return The animation object reference by the specified id + * @throws NotFoundException when the animation cannot be loaded + */ + public static Animation loadAnimation(Context context, @AnimRes int id) + throws NotFoundException { + + XmlResourceParser parser = null; + try { + parser = context.getResources().getAnimation(id); + return createAnimationFromXml(context, parser); + } catch (XmlPullParserException ex) { + NotFoundException rnf = new NotFoundException("Can't load animation resource ID #0x" + + Integer.toHexString(id)); + rnf.initCause(ex); + throw rnf; + } catch (IOException ex) { + NotFoundException rnf = new NotFoundException("Can't load animation resource ID #0x" + + Integer.toHexString(id)); + rnf.initCause(ex); + throw rnf; + } finally { + if (parser != null) parser.close(); + } + } + + private static Animation createAnimationFromXml(Context c, XmlPullParser parser) + throws XmlPullParserException, IOException { + + return createAnimationFromXml(c, parser, null, Xml.asAttributeSet(parser)); + } + + private static Animation createAnimationFromXml(Context c, XmlPullParser parser, + AnimationSet parent, AttributeSet attrs) throws XmlPullParserException, IOException { + + Animation anim = null; + + // Make sure we are on a start tag. + int type; + int depth = parser.getDepth(); + + while (((type=parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth) + && type != XmlPullParser.END_DOCUMENT) { + + if (type != XmlPullParser.START_TAG) { + continue; + } + + String name = parser.getName(); + + if (name.equals("set")) { + anim = new AnimationSet(c, attrs); + createAnimationFromXml(c, parser, (AnimationSet)anim, attrs); + } else if (name.equals("alpha")) { + anim = new AlphaAnimation(c, attrs); + } else if (name.equals("scale")) { + anim = new ScaleAnimation(c, attrs); + } else if (name.equals("rotate")) { + anim = new RotateAnimation(c, attrs); + } else if (name.equals("translate")) { + anim = new TranslateAnimation(c, attrs); + } else { + throw new RuntimeException("Unknown animation name: " + parser.getName()); + } + + if (parent != null) { + parent.addAnimation(anim); + } + } + + return anim; + + } + + /** + * Loads a {@link LayoutAnimationController} object from a resource + * + * @param context Application context used to access resources + * @param id The resource id of the animation to load + * @return The animation object reference by the specified id + * @throws NotFoundException when the layout animation controller cannot be loaded + */ + public static LayoutAnimationController loadLayoutAnimation(Context context, @AnimRes int id) + throws NotFoundException { + + XmlResourceParser parser = null; + try { + parser = context.getResources().getAnimation(id); + return createLayoutAnimationFromXml(context, parser); + } catch (XmlPullParserException ex) { + NotFoundException rnf = new NotFoundException("Can't load animation resource ID #0x" + + Integer.toHexString(id)); + rnf.initCause(ex); + throw rnf; + } catch (IOException ex) { + NotFoundException rnf = new NotFoundException("Can't load animation resource ID #0x" + + Integer.toHexString(id)); + rnf.initCause(ex); + throw rnf; + } finally { + if (parser != null) parser.close(); + } + } + + private static LayoutAnimationController createLayoutAnimationFromXml(Context c, + XmlPullParser parser) throws XmlPullParserException, IOException { + + return createLayoutAnimationFromXml(c, parser, Xml.asAttributeSet(parser)); + } + + private static LayoutAnimationController createLayoutAnimationFromXml(Context c, + XmlPullParser parser, AttributeSet attrs) throws XmlPullParserException, IOException { + + LayoutAnimationController controller = null; + + int type; + int depth = parser.getDepth(); + + while (((type = parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth) + && type != XmlPullParser.END_DOCUMENT) { + + if (type != XmlPullParser.START_TAG) { + continue; + } + + String name = parser.getName(); + + if ("layoutAnimation".equals(name)) { + controller = new LayoutAnimationController(c, attrs); + } else if ("gridLayoutAnimation".equals(name)) { + controller = new GridLayoutAnimationController(c, attrs); + } else { + throw new RuntimeException("Unknown layout animation name: " + name); + } + } + + return controller; + } + + /** + * Make an animation for objects becoming visible. Uses a slide and fade + * effect. + * + * @param c Context for loading resources + * @param fromLeft is the object to be animated coming from the left + * @return The new animation + */ + public static Animation makeInAnimation(Context c, boolean fromLeft) { + Animation a; + if (fromLeft) { + a = AnimationUtils.loadAnimation(c, com.android.internal.R.anim.slide_in_left); + } else { + a = AnimationUtils.loadAnimation(c, com.android.internal.R.anim.slide_in_right); + } + + a.setInterpolator(new DecelerateInterpolator()); + a.setStartTime(currentAnimationTimeMillis()); + return a; + } + + /** + * Make an animation for objects becoming invisible. Uses a slide and fade + * effect. + * + * @param c Context for loading resources + * @param toRight is the object to be animated exiting to the right + * @return The new animation + */ + public static Animation makeOutAnimation(Context c, boolean toRight) { + Animation a; + if (toRight) { + a = AnimationUtils.loadAnimation(c, com.android.internal.R.anim.slide_out_right); + } else { + a = AnimationUtils.loadAnimation(c, com.android.internal.R.anim.slide_out_left); + } + + a.setInterpolator(new AccelerateInterpolator()); + a.setStartTime(currentAnimationTimeMillis()); + return a; + } + + + /** + * Make an animation for objects becoming visible. Uses a slide up and fade + * effect. + * + * @param c Context for loading resources + * @return The new animation + */ + public static Animation makeInChildBottomAnimation(Context c) { + Animation a; + a = AnimationUtils.loadAnimation(c, com.android.internal.R.anim.slide_in_child_bottom); + a.setInterpolator(new AccelerateInterpolator()); + a.setStartTime(currentAnimationTimeMillis()); + return a; + } + + /** + * Loads an {@link Interpolator} object from a resource + * + * @param context Application context used to access resources + * @param id The resource id of the animation to load + * @return The animation object reference by the specified id + * @throws NotFoundException + */ + public static Interpolator loadInterpolator(Context context, @AnimRes @InterpolatorRes int id) + throws NotFoundException { + XmlResourceParser parser = null; + try { + parser = context.getResources().getAnimation(id); + return createInterpolatorFromXml(context.getResources(), context.getTheme(), parser); + } catch (XmlPullParserException ex) { + NotFoundException rnf = new NotFoundException("Can't load animation resource ID #0x" + + Integer.toHexString(id)); + rnf.initCause(ex); + throw rnf; + } catch (IOException ex) { + NotFoundException rnf = new NotFoundException("Can't load animation resource ID #0x" + + Integer.toHexString(id)); + rnf.initCause(ex); + throw rnf; + } finally { + if (parser != null) parser.close(); + } + + } + + /** + * Loads an {@link Interpolator} object from a resource + * + * @param res The resources + * @param id The resource id of the animation to load + * @return The interpolator object reference by the specified id + * @throws NotFoundException + * @hide + */ + public static Interpolator loadInterpolator(Resources res, Theme theme, int id) throws NotFoundException { + XmlResourceParser parser = null; + try { + parser = res.getAnimation(id); + return createInterpolatorFromXml(res, theme, parser); + } catch (XmlPullParserException ex) { + NotFoundException rnf = new NotFoundException("Can't load animation resource ID #0x" + + Integer.toHexString(id)); + rnf.initCause(ex); + throw rnf; + } catch (IOException ex) { + NotFoundException rnf = new NotFoundException("Can't load animation resource ID #0x" + + Integer.toHexString(id)); + rnf.initCause(ex); + throw rnf; + } finally { + if (parser != null) + parser.close(); + } + + } + + private static Interpolator createInterpolatorFromXml(Resources res, Theme theme, XmlPullParser parser) + throws XmlPullParserException, IOException { + + BaseInterpolator interpolator = null; + + // Make sure we are on a start tag. + int type; + int depth = parser.getDepth(); + + while (((type = parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth) + && type != XmlPullParser.END_DOCUMENT) { + + if (type != XmlPullParser.START_TAG) { + continue; + } + + AttributeSet attrs = Xml.asAttributeSet(parser); + + String name = parser.getName(); + + if (name.equals("linearInterpolator")) { + interpolator = new LinearInterpolator(); + } else if (name.equals("accelerateInterpolator")) { + interpolator = new AccelerateInterpolator(res, theme, attrs); + } else if (name.equals("decelerateInterpolator")) { + interpolator = new DecelerateInterpolator(res, theme, attrs); + } else if (name.equals("accelerateDecelerateInterpolator")) { + interpolator = new AccelerateDecelerateInterpolator(); + } else if (name.equals("cycleInterpolator")) { + interpolator = new CycleInterpolator(res, theme, attrs); + } else if (name.equals("anticipateInterpolator")) { + interpolator = new AnticipateInterpolator(res, theme, attrs); + } else if (name.equals("overshootInterpolator")) { + interpolator = new OvershootInterpolator(res, theme, attrs); + } else if (name.equals("anticipateOvershootInterpolator")) { + interpolator = new AnticipateOvershootInterpolator(res, theme, attrs); + } else if (name.equals("bounceInterpolator")) { + interpolator = new BounceInterpolator(); + } else if (name.equals("pathInterpolator")) { + interpolator = new PathInterpolator(res, theme, attrs); + } else { + throw new RuntimeException("Unknown interpolator name: " + parser.getName()); + } + } + return interpolator; + } +} diff --git a/android/view/animation/AnticipateInterpolator.java b/android/view/animation/AnticipateInterpolator.java new file mode 100644 index 00000000..7a837c37 --- /dev/null +++ b/android/view/animation/AnticipateInterpolator.java @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2009 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.animation; + +import android.content.Context; +import android.content.res.Resources; +import android.content.res.Resources.Theme; +import android.content.res.TypedArray; +import android.util.AttributeSet; + +import com.android.internal.R; +import com.android.internal.view.animation.HasNativeInterpolator; +import com.android.internal.view.animation.NativeInterpolatorFactory; +import com.android.internal.view.animation.NativeInterpolatorFactoryHelper; + +/** + * An interpolator where the change starts backward then flings forward. + */ +@HasNativeInterpolator +public class AnticipateInterpolator extends BaseInterpolator implements NativeInterpolatorFactory { + private final float mTension; + + public AnticipateInterpolator() { + mTension = 2.0f; + } + + /** + * @param tension Amount of anticipation. When tension equals 0.0f, there is + * no anticipation and the interpolator becomes a simple + * acceleration interpolator. + */ + public AnticipateInterpolator(float tension) { + mTension = tension; + } + + public AnticipateInterpolator(Context context, AttributeSet attrs) { + this(context.getResources(), context.getTheme(), attrs); + } + + /** @hide */ + public AnticipateInterpolator(Resources res, Theme theme, AttributeSet attrs) { + TypedArray a; + if (theme != null) { + a = theme.obtainStyledAttributes(attrs, R.styleable.AnticipateInterpolator, 0, 0); + } else { + a = res.obtainAttributes(attrs, R.styleable.AnticipateInterpolator); + } + + mTension = a.getFloat(R.styleable.AnticipateInterpolator_tension, 2.0f); + setChangingConfiguration(a.getChangingConfigurations()); + a.recycle(); + } + + public float getInterpolation(float t) { + // a(t) = t * t * ((tension + 1) * t - tension) + return t * t * ((mTension + 1) * t - mTension); + } + + /** @hide */ + @Override + public long createNativeInterpolator() { + return NativeInterpolatorFactoryHelper.createAnticipateInterpolator(mTension); + } +} diff --git a/android/view/animation/AnticipateOvershootInterpolator.java b/android/view/animation/AnticipateOvershootInterpolator.java new file mode 100644 index 00000000..9a75134a --- /dev/null +++ b/android/view/animation/AnticipateOvershootInterpolator.java @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2009 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.animation; + +import static com.android.internal.R.styleable.AnticipateOvershootInterpolator; +import static com.android.internal.R.styleable.AnticipateOvershootInterpolator_extraTension; +import static com.android.internal.R.styleable.AnticipateOvershootInterpolator_tension; + +import android.content.Context; +import android.content.res.Resources; +import android.content.res.Resources.Theme; +import android.content.res.TypedArray; +import android.util.AttributeSet; + +import com.android.internal.view.animation.HasNativeInterpolator; +import com.android.internal.view.animation.NativeInterpolatorFactory; +import com.android.internal.view.animation.NativeInterpolatorFactoryHelper; + +/** + * An interpolator where the change starts backward then flings forward and overshoots + * the target value and finally goes back to the final value. + */ +@HasNativeInterpolator +public class AnticipateOvershootInterpolator extends BaseInterpolator + implements NativeInterpolatorFactory { + private final float mTension; + + public AnticipateOvershootInterpolator() { + mTension = 2.0f * 1.5f; + } + + /** + * @param tension Amount of anticipation/overshoot. When tension equals 0.0f, + * there is no anticipation/overshoot and the interpolator becomes + * a simple acceleration/deceleration interpolator. + */ + public AnticipateOvershootInterpolator(float tension) { + mTension = tension * 1.5f; + } + + /** + * @param tension Amount of anticipation/overshoot. When tension equals 0.0f, + * there is no anticipation/overshoot and the interpolator becomes + * a simple acceleration/deceleration interpolator. + * @param extraTension Amount by which to multiply the tension. For instance, + * to get the same overshoot as an OvershootInterpolator with + * a tension of 2.0f, you would use an extraTension of 1.5f. + */ + public AnticipateOvershootInterpolator(float tension, float extraTension) { + mTension = tension * extraTension; + } + + public AnticipateOvershootInterpolator(Context context, AttributeSet attrs) { + this(context.getResources(), context.getTheme(), attrs); + } + + /** @hide */ + public AnticipateOvershootInterpolator(Resources res, Theme theme, AttributeSet attrs) { + TypedArray a; + if (theme != null) { + a = theme.obtainStyledAttributes(attrs, AnticipateOvershootInterpolator, 0, 0); + } else { + a = res.obtainAttributes(attrs, AnticipateOvershootInterpolator); + } + + mTension = a.getFloat(AnticipateOvershootInterpolator_tension, 2.0f) * + a.getFloat(AnticipateOvershootInterpolator_extraTension, 1.5f); + setChangingConfiguration(a.getChangingConfigurations()); + a.recycle(); + } + + private static float a(float t, float s) { + return t * t * ((s + 1) * t - s); + } + + private static float o(float t, float s) { + return t * t * ((s + 1) * t + s); + } + + public float getInterpolation(float t) { + // a(t, s) = t * t * ((s + 1) * t - s) + // o(t, s) = t * t * ((s + 1) * t + s) + // f(t) = 0.5 * a(t * 2, tension * extraTension), when t < 0.5 + // f(t) = 0.5 * (o(t * 2 - 2, tension * extraTension) + 2), when t <= 1.0 + if (t < 0.5f) return 0.5f * a(t * 2.0f, mTension); + else return 0.5f * (o(t * 2.0f - 2.0f, mTension) + 2.0f); + } + + /** @hide */ + @Override + public long createNativeInterpolator() { + return NativeInterpolatorFactoryHelper.createAnticipateOvershootInterpolator(mTension); + } +} diff --git a/android/view/animation/BaseInterpolator.java b/android/view/animation/BaseInterpolator.java new file mode 100644 index 00000000..a78fa1ea --- /dev/null +++ b/android/view/animation/BaseInterpolator.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2014 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.animation; + +import android.content.pm.ActivityInfo.Config; + +/** + * An abstract class which is extended by default interpolators. + */ +abstract public class BaseInterpolator implements Interpolator { + private @Config int mChangingConfiguration; + /** + * @hide + */ + public @Config int getChangingConfiguration() { + return mChangingConfiguration; + } + + /** + * @hide + */ + void setChangingConfiguration(@Config int changingConfiguration) { + mChangingConfiguration = changingConfiguration; + } +} diff --git a/android/view/animation/BounceInterpolator.java b/android/view/animation/BounceInterpolator.java new file mode 100644 index 00000000..909eaa4c --- /dev/null +++ b/android/view/animation/BounceInterpolator.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2009 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.animation; + +import android.content.Context; +import android.util.AttributeSet; + +import com.android.internal.view.animation.HasNativeInterpolator; +import com.android.internal.view.animation.NativeInterpolatorFactory; +import com.android.internal.view.animation.NativeInterpolatorFactoryHelper; + +/** + * An interpolator where the change bounces at the end. + */ +@HasNativeInterpolator +public class BounceInterpolator extends BaseInterpolator implements NativeInterpolatorFactory { + public BounceInterpolator() { + } + + @SuppressWarnings({"UnusedDeclaration"}) + public BounceInterpolator(Context context, AttributeSet attrs) { + } + + private static float bounce(float t) { + return t * t * 8.0f; + } + + public float getInterpolation(float t) { + // _b(t) = t * t * 8 + // bs(t) = _b(t) for t < 0.3535 + // bs(t) = _b(t - 0.54719) + 0.7 for t < 0.7408 + // bs(t) = _b(t - 0.8526) + 0.9 for t < 0.9644 + // bs(t) = _b(t - 1.0435) + 0.95 for t <= 1.0 + // b(t) = bs(t * 1.1226) + t *= 1.1226f; + if (t < 0.3535f) return bounce(t); + else if (t < 0.7408f) return bounce(t - 0.54719f) + 0.7f; + else if (t < 0.9644f) return bounce(t - 0.8526f) + 0.9f; + else return bounce(t - 1.0435f) + 0.95f; + } + + /** @hide */ + @Override + public long createNativeInterpolator() { + return NativeInterpolatorFactoryHelper.createBounceInterpolator(); + } +}
\ No newline at end of file diff --git a/android/view/animation/ClipRectAnimation.java b/android/view/animation/ClipRectAnimation.java new file mode 100644 index 00000000..e194927e --- /dev/null +++ b/android/view/animation/ClipRectAnimation.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2014 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.animation; + +import android.graphics.Rect; + +/** + * An animation that controls the clip of an object. See the + * {@link android.view.animation full package} description for details and + * sample code. + * + * @hide + */ +public class ClipRectAnimation extends Animation { + protected Rect mFromRect = new Rect(); + protected Rect mToRect = new Rect(); + + /** + * Constructor to use when building a ClipRectAnimation from code + * + * @param fromClip the clip rect to animate from + * @param toClip the clip rect to animate to + */ + public ClipRectAnimation(Rect fromClip, Rect toClip) { + if (fromClip == null || toClip == null) { + throw new RuntimeException("Expected non-null animation clip rects"); + } + mFromRect.set(fromClip); + mToRect.set(toClip); + } + + /** + * Constructor to use when building a ClipRectAnimation from code + */ + public ClipRectAnimation(int fromL, int fromT, int fromR, int fromB, + int toL, int toT, int toR, int toB) { + mFromRect.set(fromL, fromT, fromR, fromB); + mToRect.set(toL, toT, toR, toB); + } + + @Override + protected void applyTransformation(float it, Transformation tr) { + int l = mFromRect.left + (int) ((mToRect.left - mFromRect.left) * it); + int t = mFromRect.top + (int) ((mToRect.top - mFromRect.top) * it); + int r = mFromRect.right + (int) ((mToRect.right - mFromRect.right) * it); + int b = mFromRect.bottom + (int) ((mToRect.bottom - mFromRect.bottom) * it); + tr.setClipRect(l, t, r, b); + } + + @Override + public boolean willChangeTransformationMatrix() { + return false; + } +} diff --git a/android/view/animation/CycleInterpolator.java b/android/view/animation/CycleInterpolator.java new file mode 100644 index 00000000..72d64a16 --- /dev/null +++ b/android/view/animation/CycleInterpolator.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2007 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.animation; + +import android.content.Context; +import android.content.res.Resources; +import android.content.res.Resources.Theme; +import android.content.res.TypedArray; +import android.util.AttributeSet; + +import com.android.internal.R; +import com.android.internal.view.animation.HasNativeInterpolator; +import com.android.internal.view.animation.NativeInterpolatorFactory; +import com.android.internal.view.animation.NativeInterpolatorFactoryHelper; + +/** + * Repeats the animation for a specified number of cycles. The + * rate of change follows a sinusoidal pattern. + * + */ +@HasNativeInterpolator +public class CycleInterpolator extends BaseInterpolator implements NativeInterpolatorFactory { + public CycleInterpolator(float cycles) { + mCycles = cycles; + } + + public CycleInterpolator(Context context, AttributeSet attrs) { + this(context.getResources(), context.getTheme(), attrs); + } + + /** @hide */ + public CycleInterpolator(Resources resources, Theme theme, AttributeSet attrs) { + TypedArray a; + if (theme != null) { + a = theme.obtainStyledAttributes(attrs, R.styleable.CycleInterpolator, 0, 0); + } else { + a = resources.obtainAttributes(attrs, R.styleable.CycleInterpolator); + } + + mCycles = a.getFloat(R.styleable.CycleInterpolator_cycles, 1.0f); + setChangingConfiguration(a.getChangingConfigurations()); + a.recycle(); + } + + public float getInterpolation(float input) { + return (float)(Math.sin(2 * mCycles * Math.PI * input)); + } + + private float mCycles; + + /** @hide */ + @Override + public long createNativeInterpolator() { + return NativeInterpolatorFactoryHelper.createCycleInterpolator(mCycles); + } +} diff --git a/android/view/animation/DecelerateInterpolator.java b/android/view/animation/DecelerateInterpolator.java new file mode 100644 index 00000000..f89743c1 --- /dev/null +++ b/android/view/animation/DecelerateInterpolator.java @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2007 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.animation; + +import android.content.Context; +import android.content.res.Resources; +import android.content.res.Resources.Theme; +import android.content.res.TypedArray; +import android.util.AttributeSet; + +import com.android.internal.R; +import com.android.internal.view.animation.HasNativeInterpolator; +import com.android.internal.view.animation.NativeInterpolatorFactory; +import com.android.internal.view.animation.NativeInterpolatorFactoryHelper; + +/** + * An interpolator where the rate of change starts out quickly and + * and then decelerates. + * + */ +@HasNativeInterpolator +public class DecelerateInterpolator extends BaseInterpolator implements NativeInterpolatorFactory { + public DecelerateInterpolator() { + } + + /** + * Constructor + * + * @param factor Degree to which the animation should be eased. Setting factor to 1.0f produces + * an upside-down y=x^2 parabola. Increasing factor above 1.0f makes exaggerates the + * ease-out effect (i.e., it starts even faster and ends evens slower) + */ + public DecelerateInterpolator(float factor) { + mFactor = factor; + } + + public DecelerateInterpolator(Context context, AttributeSet attrs) { + this(context.getResources(), context.getTheme(), attrs); + } + + /** @hide */ + public DecelerateInterpolator(Resources res, Theme theme, AttributeSet attrs) { + TypedArray a; + if (theme != null) { + a = theme.obtainStyledAttributes(attrs, R.styleable.DecelerateInterpolator, 0, 0); + } else { + a = res.obtainAttributes(attrs, R.styleable.DecelerateInterpolator); + } + + mFactor = a.getFloat(R.styleable.DecelerateInterpolator_factor, 1.0f); + setChangingConfiguration(a.getChangingConfigurations()); + a.recycle(); + } + + public float getInterpolation(float input) { + float result; + if (mFactor == 1.0f) { + result = (float)(1.0f - (1.0f - input) * (1.0f - input)); + } else { + result = (float)(1.0f - Math.pow((1.0f - input), 2 * mFactor)); + } + return result; + } + + private float mFactor = 1.0f; + + /** @hide */ + @Override + public long createNativeInterpolator() { + return NativeInterpolatorFactoryHelper.createDecelerateInterpolator(mFactor); + } +} diff --git a/android/view/animation/GridLayoutAnimationController.java b/android/view/animation/GridLayoutAnimationController.java new file mode 100644 index 00000000..0f189ae9 --- /dev/null +++ b/android/view/animation/GridLayoutAnimationController.java @@ -0,0 +1,424 @@ +/* + * Copyright (C) 2007 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.animation; + +import android.content.Context; +import android.content.res.TypedArray; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; + +import java.util.Random; + +/** + * A layout animation controller is used to animated a grid layout's children. + * + * While {@link LayoutAnimationController} relies only on the index of the child + * in the view group to compute the animation delay, this class uses both the + * X and Y coordinates of the child within a grid. + * + * In addition, the animation direction can be controlled. The default direction + * is <code>DIRECTION_LEFT_TO_RIGHT | DIRECTION_TOP_TO_BOTTOM</code>. You can + * also set the animation priority to columns or rows. The default priority is + * none. + * + * Information used to compute the animation delay of each child are stored + * in an instance of + * {@link android.view.animation.GridLayoutAnimationController.AnimationParameters}, + * itself stored in the {@link android.view.ViewGroup.LayoutParams} of the view. + * + * @see LayoutAnimationController + * @see android.widget.GridView + * + * @attr ref android.R.styleable#GridLayoutAnimation_columnDelay + * @attr ref android.R.styleable#GridLayoutAnimation_rowDelay + * @attr ref android.R.styleable#GridLayoutAnimation_direction + * @attr ref android.R.styleable#GridLayoutAnimation_directionPriority + */ +public class GridLayoutAnimationController extends LayoutAnimationController { + /** + * Animates the children starting from the left of the grid to the right. + */ + public static final int DIRECTION_LEFT_TO_RIGHT = 0x0; + + /** + * Animates the children starting from the right of the grid to the left. + */ + public static final int DIRECTION_RIGHT_TO_LEFT = 0x1; + + /** + * Animates the children starting from the top of the grid to the bottom. + */ + public static final int DIRECTION_TOP_TO_BOTTOM = 0x0; + + /** + * Animates the children starting from the bottom of the grid to the top. + */ + public static final int DIRECTION_BOTTOM_TO_TOP = 0x2; + + /** + * Bitmask used to retrieve the horizontal component of the direction. + */ + public static final int DIRECTION_HORIZONTAL_MASK = 0x1; + + /** + * Bitmask used to retrieve the vertical component of the direction. + */ + public static final int DIRECTION_VERTICAL_MASK = 0x2; + + /** + * Rows and columns are animated at the same time. + */ + public static final int PRIORITY_NONE = 0; + + /** + * Columns are animated first. + */ + public static final int PRIORITY_COLUMN = 1; + + /** + * Rows are animated first. + */ + public static final int PRIORITY_ROW = 2; + + private float mColumnDelay; + private float mRowDelay; + + private int mDirection; + private int mDirectionPriority; + + /** + * Creates a new grid layout animation controller from external resources. + * + * @param context the Context the view group is running in, through which + * it can access the resources + * @param attrs the attributes of the XML tag that is inflating the + * layout animation controller + */ + public GridLayoutAnimationController(Context context, AttributeSet attrs) { + super(context, attrs); + + TypedArray a = context.obtainStyledAttributes(attrs, + com.android.internal.R.styleable.GridLayoutAnimation); + + Animation.Description d = Animation.Description.parseValue( + a.peekValue(com.android.internal.R.styleable.GridLayoutAnimation_columnDelay)); + mColumnDelay = d.value; + d = Animation.Description.parseValue( + a.peekValue(com.android.internal.R.styleable.GridLayoutAnimation_rowDelay)); + mRowDelay = d.value; + //noinspection PointlessBitwiseExpression + mDirection = a.getInt(com.android.internal.R.styleable.GridLayoutAnimation_direction, + DIRECTION_LEFT_TO_RIGHT | DIRECTION_TOP_TO_BOTTOM); + mDirectionPriority = a.getInt(com.android.internal.R.styleable.GridLayoutAnimation_directionPriority, + PRIORITY_NONE); + + a.recycle(); + } + + /** + * Creates a new layout animation controller with a delay of 50% + * for both rows and columns and the specified animation. + * + * @param animation the animation to use on each child of the view group + */ + public GridLayoutAnimationController(Animation animation) { + this(animation, 0.5f, 0.5f); + } + + /** + * Creates a new layout animation controller with the specified delays + * and the specified animation. + * + * @param animation the animation to use on each child of the view group + * @param columnDelay the delay by which each column animation must be offset + * @param rowDelay the delay by which each row animation must be offset + */ + public GridLayoutAnimationController(Animation animation, float columnDelay, float rowDelay) { + super(animation); + mColumnDelay = columnDelay; + mRowDelay = rowDelay; + } + + /** + * Returns the delay by which the children's animation are offset from one + * column to the other. The delay is expressed as a fraction of the + * animation duration. + * + * @return a fraction of the animation duration + * + * @see #setColumnDelay(float) + * @see #getRowDelay() + * @see #setRowDelay(float) + */ + public float getColumnDelay() { + return mColumnDelay; + } + + /** + * Sets the delay, as a fraction of the animation duration, by which the + * children's animations are offset from one column to the other. + * + * @param columnDelay a fraction of the animation duration + * + * @see #getColumnDelay() + * @see #getRowDelay() + * @see #setRowDelay(float) + */ + public void setColumnDelay(float columnDelay) { + mColumnDelay = columnDelay; + } + + /** + * Returns the delay by which the children's animation are offset from one + * row to the other. The delay is expressed as a fraction of the + * animation duration. + * + * @return a fraction of the animation duration + * + * @see #setRowDelay(float) + * @see #getColumnDelay() + * @see #setColumnDelay(float) + */ + public float getRowDelay() { + return mRowDelay; + } + + /** + * Sets the delay, as a fraction of the animation duration, by which the + * children's animations are offset from one row to the other. + * + * @param rowDelay a fraction of the animation duration + * + * @see #getRowDelay() + * @see #getColumnDelay() + * @see #setColumnDelay(float) + */ + public void setRowDelay(float rowDelay) { + mRowDelay = rowDelay; + } + + /** + * Returns the direction of the animation. {@link #DIRECTION_HORIZONTAL_MASK} + * and {@link #DIRECTION_VERTICAL_MASK} can be used to retrieve the + * horizontal and vertical components of the direction. + * + * @return the direction of the animation + * + * @see #setDirection(int) + * @see #DIRECTION_BOTTOM_TO_TOP + * @see #DIRECTION_TOP_TO_BOTTOM + * @see #DIRECTION_LEFT_TO_RIGHT + * @see #DIRECTION_RIGHT_TO_LEFT + * @see #DIRECTION_HORIZONTAL_MASK + * @see #DIRECTION_VERTICAL_MASK + */ + public int getDirection() { + return mDirection; + } + + /** + * Sets the direction of the animation. The direction is expressed as an + * integer containing a horizontal and vertical component. For instance, + * <code>DIRECTION_BOTTOM_TO_TOP | DIRECTION_RIGHT_TO_LEFT</code>. + * + * @param direction the direction of the animation + * + * @see #getDirection() + * @see #DIRECTION_BOTTOM_TO_TOP + * @see #DIRECTION_TOP_TO_BOTTOM + * @see #DIRECTION_LEFT_TO_RIGHT + * @see #DIRECTION_RIGHT_TO_LEFT + * @see #DIRECTION_HORIZONTAL_MASK + * @see #DIRECTION_VERTICAL_MASK + */ + public void setDirection(int direction) { + mDirection = direction; + } + + /** + * Returns the direction priority for the animation. The priority can + * be either {@link #PRIORITY_NONE}, {@link #PRIORITY_COLUMN} or + * {@link #PRIORITY_ROW}. + * + * @return the priority of the animation direction + * + * @see #setDirectionPriority(int) + * @see #PRIORITY_COLUMN + * @see #PRIORITY_NONE + * @see #PRIORITY_ROW + */ + public int getDirectionPriority() { + return mDirectionPriority; + } + + /** + * Specifies the direction priority of the animation. For instance, + * {@link #PRIORITY_COLUMN} will give priority to columns: the animation + * will first play on the column, then on the rows.Z + * + * @param directionPriority the direction priority of the animation + * + * @see #getDirectionPriority() + * @see #PRIORITY_COLUMN + * @see #PRIORITY_NONE + * @see #PRIORITY_ROW + */ + public void setDirectionPriority(int directionPriority) { + mDirectionPriority = directionPriority; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean willOverlap() { + return mColumnDelay < 1.0f || mRowDelay < 1.0f; + } + + /** + * {@inheritDoc} + */ + @Override + protected long getDelayForView(View view) { + ViewGroup.LayoutParams lp = view.getLayoutParams(); + AnimationParameters params = (AnimationParameters) lp.layoutAnimationParameters; + + if (params == null) { + return 0; + } + + final int column = getTransformedColumnIndex(params); + final int row = getTransformedRowIndex(params); + + final int rowsCount = params.rowsCount; + final int columnsCount = params.columnsCount; + + final long duration = mAnimation.getDuration(); + final float columnDelay = mColumnDelay * duration; + final float rowDelay = mRowDelay * duration; + + float totalDelay; + long viewDelay; + + if (mInterpolator == null) { + mInterpolator = new LinearInterpolator(); + } + + switch (mDirectionPriority) { + case PRIORITY_COLUMN: + viewDelay = (long) (row * rowDelay + column * rowsCount * rowDelay); + totalDelay = rowsCount * rowDelay + columnsCount * rowsCount * rowDelay; + break; + case PRIORITY_ROW: + viewDelay = (long) (column * columnDelay + row * columnsCount * columnDelay); + totalDelay = columnsCount * columnDelay + rowsCount * columnsCount * columnDelay; + break; + case PRIORITY_NONE: + default: + viewDelay = (long) (column * columnDelay + row * rowDelay); + totalDelay = columnsCount * columnDelay + rowsCount * rowDelay; + break; + } + + float normalizedDelay = viewDelay / totalDelay; + normalizedDelay = mInterpolator.getInterpolation(normalizedDelay); + + return (long) (normalizedDelay * totalDelay); + } + + private int getTransformedColumnIndex(AnimationParameters params) { + int index; + switch (getOrder()) { + case ORDER_REVERSE: + index = params.columnsCount - 1 - params.column; + break; + case ORDER_RANDOM: + if (mRandomizer == null) { + mRandomizer = new Random(); + } + index = (int) (params.columnsCount * mRandomizer.nextFloat()); + break; + case ORDER_NORMAL: + default: + index = params.column; + break; + } + + int direction = mDirection & DIRECTION_HORIZONTAL_MASK; + if (direction == DIRECTION_RIGHT_TO_LEFT) { + index = params.columnsCount - 1 - index; + } + + return index; + } + + private int getTransformedRowIndex(AnimationParameters params) { + int index; + switch (getOrder()) { + case ORDER_REVERSE: + index = params.rowsCount - 1 - params.row; + break; + case ORDER_RANDOM: + if (mRandomizer == null) { + mRandomizer = new Random(); + } + index = (int) (params.rowsCount * mRandomizer.nextFloat()); + break; + case ORDER_NORMAL: + default: + index = params.row; + break; + } + + int direction = mDirection & DIRECTION_VERTICAL_MASK; + if (direction == DIRECTION_BOTTOM_TO_TOP) { + index = params.rowsCount - 1 - index; + } + + return index; + } + + /** + * The set of parameters that has to be attached to each view contained in + * the view group animated by the grid layout animation controller. These + * parameters are used to compute the start time of each individual view's + * animation. + */ + public static class AnimationParameters extends + LayoutAnimationController.AnimationParameters { + /** + * The view group's column to which the view belongs. + */ + public int column; + + /** + * The view group's row to which the view belongs. + */ + public int row; + + /** + * The number of columns in the view's enclosing grid layout. + */ + public int columnsCount; + + /** + * The number of rows in the view's enclosing grid layout. + */ + public int rowsCount; + } +} diff --git a/android/view/animation/Interpolator.java b/android/view/animation/Interpolator.java new file mode 100644 index 00000000..5d0fe7ef --- /dev/null +++ b/android/view/animation/Interpolator.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2006 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.animation; + +import android.animation.TimeInterpolator; + +/** + * An interpolator defines the rate of change of an animation. This allows + * the basic animation effects (alpha, scale, translate, rotate) to be + * accelerated, decelerated, repeated, etc. + */ +public interface Interpolator extends TimeInterpolator { + // A new interface, TimeInterpolator, was introduced for the new android.animation + // package. This older Interpolator interface extends TimeInterpolator so that users of + // the new Animator-based animations can use either the old Interpolator implementations or + // new classes that implement TimeInterpolator directly. +} diff --git a/android/view/animation/LayoutAnimationController.java b/android/view/animation/LayoutAnimationController.java new file mode 100644 index 00000000..7fa49c1a --- /dev/null +++ b/android/view/animation/LayoutAnimationController.java @@ -0,0 +1,437 @@ +/* + * Copyright (C) 2007 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.animation; + +import android.annotation.AnimRes; +import android.annotation.InterpolatorRes; +import android.content.Context; +import android.content.res.TypedArray; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; + +import java.util.Random; + +/** + * A layout animation controller is used to animated a layout's, or a view + * group's, children. Each child uses the same animation but for every one of + * them, the animation starts at a different time. A layout animation controller + * is used by {@link android.view.ViewGroup} to compute the delay by which each + * child's animation start must be offset. The delay is computed by using + * characteristics of each child, like its index in the view group. + * + * This standard implementation computes the delay by multiplying a fixed + * amount of miliseconds by the index of the child in its parent view group. + * Subclasses are supposed to override + * {@link #getDelayForView(android.view.View)} to implement a different way + * of computing the delay. For instance, a + * {@link android.view.animation.GridLayoutAnimationController} will compute the + * delay based on the column and row indices of the child in its parent view + * group. + * + * Information used to compute the animation delay of each child are stored + * in an instance of + * {@link android.view.animation.LayoutAnimationController.AnimationParameters}, + * itself stored in the {@link android.view.ViewGroup.LayoutParams} of the view. + * + * @attr ref android.R.styleable#LayoutAnimation_delay + * @attr ref android.R.styleable#LayoutAnimation_animationOrder + * @attr ref android.R.styleable#LayoutAnimation_interpolator + * @attr ref android.R.styleable#LayoutAnimation_animation + */ +public class LayoutAnimationController { + /** + * Distributes the animation delays in the order in which view were added + * to their view group. + */ + public static final int ORDER_NORMAL = 0; + + /** + * Distributes the animation delays in the reverse order in which view were + * added to their view group. + */ + public static final int ORDER_REVERSE = 1; + + /** + * Randomly distributes the animation delays. + */ + public static final int ORDER_RANDOM = 2; + + /** + * The animation applied on each child of the view group on which this + * layout animation controller is set. + */ + protected Animation mAnimation; + + /** + * The randomizer used when the order is set to random. Subclasses should + * use this object to avoid creating their own. + */ + protected Random mRandomizer; + + /** + * The interpolator used to interpolate the delays. + */ + protected Interpolator mInterpolator; + + private float mDelay; + private int mOrder; + + private long mDuration; + private long mMaxDelay; + + /** + * Creates a new layout animation controller from external resources. + * + * @param context the Context the view group is running in, through which + * it can access the resources + * @param attrs the attributes of the XML tag that is inflating the + * layout animation controller + */ + public LayoutAnimationController(Context context, AttributeSet attrs) { + TypedArray a = context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.LayoutAnimation); + + Animation.Description d = Animation.Description.parseValue( + a.peekValue(com.android.internal.R.styleable.LayoutAnimation_delay)); + mDelay = d.value; + + mOrder = a.getInt(com.android.internal.R.styleable.LayoutAnimation_animationOrder, ORDER_NORMAL); + + int resource = a.getResourceId(com.android.internal.R.styleable.LayoutAnimation_animation, 0); + if (resource > 0) { + setAnimation(context, resource); + } + + resource = a.getResourceId(com.android.internal.R.styleable.LayoutAnimation_interpolator, 0); + if (resource > 0) { + setInterpolator(context, resource); + } + + a.recycle(); + } + + /** + * Creates a new layout animation controller with a delay of 50% + * and the specified animation. + * + * @param animation the animation to use on each child of the view group + */ + public LayoutAnimationController(Animation animation) { + this(animation, 0.5f); + } + + /** + * Creates a new layout animation controller with the specified delay + * and the specified animation. + * + * @param animation the animation to use on each child of the view group + * @param delay the delay by which each child's animation must be offset + */ + public LayoutAnimationController(Animation animation, float delay) { + mDelay = delay; + setAnimation(animation); + } + + /** + * Returns the order used to compute the delay of each child's animation. + * + * @return one of {@link #ORDER_NORMAL}, {@link #ORDER_REVERSE} or + * {@link #ORDER_RANDOM} + * + * @attr ref android.R.styleable#LayoutAnimation_animationOrder + */ + public int getOrder() { + return mOrder; + } + + /** + * Sets the order used to compute the delay of each child's animation. + * + * @param order one of {@link #ORDER_NORMAL}, {@link #ORDER_REVERSE} or + * {@link #ORDER_RANDOM} + * + * @attr ref android.R.styleable#LayoutAnimation_animationOrder + */ + public void setOrder(int order) { + mOrder = order; + } + + /** + * Sets the animation to be run on each child of the view group on which + * this layout animation controller is . + * + * @param context the context from which the animation must be inflated + * @param resourceID the resource identifier of the animation + * + * @see #setAnimation(Animation) + * @see #getAnimation() + * + * @attr ref android.R.styleable#LayoutAnimation_animation + */ + public void setAnimation(Context context, @AnimRes int resourceID) { + setAnimation(AnimationUtils.loadAnimation(context, resourceID)); + } + + /** + * Sets the animation to be run on each child of the view group on which + * this layout animation controller is . + * + * @param animation the animation to run on each child of the view group + + * @see #setAnimation(android.content.Context, int) + * @see #getAnimation() + * + * @attr ref android.R.styleable#LayoutAnimation_animation + */ + public void setAnimation(Animation animation) { + mAnimation = animation; + mAnimation.setFillBefore(true); + } + + /** + * Returns the animation applied to each child of the view group on which + * this controller is set. + * + * @return an {@link android.view.animation.Animation} instance + * + * @see #setAnimation(android.content.Context, int) + * @see #setAnimation(Animation) + */ + public Animation getAnimation() { + return mAnimation; + } + + /** + * Sets the interpolator used to interpolate the delays between the + * children. + * + * @param context the context from which the interpolator must be inflated + * @param resourceID the resource identifier of the interpolator + * + * @see #getInterpolator() + * @see #setInterpolator(Interpolator) + * + * @attr ref android.R.styleable#LayoutAnimation_interpolator + */ + public void setInterpolator(Context context, @InterpolatorRes int resourceID) { + setInterpolator(AnimationUtils.loadInterpolator(context, resourceID)); + } + + /** + * Sets the interpolator used to interpolate the delays between the + * children. + * + * @param interpolator the interpolator + * + * @see #getInterpolator() + * @see #setInterpolator(Interpolator) + * + * @attr ref android.R.styleable#LayoutAnimation_interpolator + */ + public void setInterpolator(Interpolator interpolator) { + mInterpolator = interpolator; + } + + /** + * Returns the interpolator used to interpolate the delays between the + * children. + * + * @return an {@link android.view.animation.Interpolator} + */ + public Interpolator getInterpolator() { + return mInterpolator; + } + + /** + * Returns the delay by which the children's animation are offset. The + * delay is expressed as a fraction of the animation duration. + * + * @return a fraction of the animation duration + * + * @see #setDelay(float) + */ + public float getDelay() { + return mDelay; + } + + /** + * Sets the delay, as a fraction of the animation duration, by which the + * children's animations are offset. The general formula is: + * + * <pre> + * child animation delay = child index * delay * animation duration + * </pre> + * + * @param delay a fraction of the animation duration + * + * @see #getDelay() + */ + public void setDelay(float delay) { + mDelay = delay; + } + + /** + * Indicates whether two children's animations will overlap. Animations + * overlap when the delay is lower than 100% (or 1.0). + * + * @return true if animations will overlap, false otherwise + */ + public boolean willOverlap() { + return mDelay < 1.0f; + } + + /** + * Starts the animation. + */ + public void start() { + mDuration = mAnimation.getDuration(); + mMaxDelay = Long.MIN_VALUE; + mAnimation.setStartTime(-1); + } + + /** + * Returns the animation to be applied to the specified view. The returned + * animation is delayed by an offset computed according to the information + * provided by + * {@link android.view.animation.LayoutAnimationController.AnimationParameters}. + * This method is called by view groups to obtain the animation to set on + * a specific child. + * + * @param view the view to animate + * @return an animation delayed by the number of milliseconds returned by + * {@link #getDelayForView(android.view.View)} + * + * @see #getDelay() + * @see #setDelay(float) + * @see #getDelayForView(android.view.View) + */ + public final Animation getAnimationForView(View view) { + final long delay = getDelayForView(view) + mAnimation.getStartOffset(); + mMaxDelay = Math.max(mMaxDelay, delay); + + try { + final Animation animation = mAnimation.clone(); + animation.setStartOffset(delay); + return animation; + } catch (CloneNotSupportedException e) { + return null; + } + } + + /** + * Indicates whether the layout animation is over or not. A layout animation + * is considered done when the animation with the longest delay is done. + * + * @return true if all of the children's animations are over, false otherwise + */ + public boolean isDone() { + return AnimationUtils.currentAnimationTimeMillis() > + mAnimation.getStartTime() + mMaxDelay + mDuration; + } + + /** + * Returns the amount of milliseconds by which the specified view's + * animation must be delayed or offset. Subclasses should override this + * method to return a suitable value. + * + * This implementation returns <code>child animation delay</code> + * milliseconds where: + * + * <pre> + * child animation delay = child index * delay + * </pre> + * + * The index is retrieved from the + * {@link android.view.animation.LayoutAnimationController.AnimationParameters} + * found in the view's {@link android.view.ViewGroup.LayoutParams}. + * + * @param view the view for which to obtain the animation's delay + * @return a delay in milliseconds + * + * @see #getAnimationForView(android.view.View) + * @see #getDelay() + * @see #getTransformedIndex(android.view.animation.LayoutAnimationController.AnimationParameters) + * @see android.view.ViewGroup.LayoutParams + */ + protected long getDelayForView(View view) { + ViewGroup.LayoutParams lp = view.getLayoutParams(); + AnimationParameters params = lp.layoutAnimationParameters; + + if (params == null) { + return 0; + } + + final float delay = mDelay * mAnimation.getDuration(); + final long viewDelay = (long) (getTransformedIndex(params) * delay); + final float totalDelay = delay * params.count; + + if (mInterpolator == null) { + mInterpolator = new LinearInterpolator(); + } + + float normalizedDelay = viewDelay / totalDelay; + normalizedDelay = mInterpolator.getInterpolation(normalizedDelay); + + return (long) (normalizedDelay * totalDelay); + } + + /** + * Transforms the index stored in + * {@link android.view.animation.LayoutAnimationController.AnimationParameters} + * by the order returned by {@link #getOrder()}. Subclasses should override + * this method to provide additional support for other types of ordering. + * This method should be invoked by + * {@link #getDelayForView(android.view.View)} prior to any computation. + * + * @param params the animation parameters containing the index + * @return a transformed index + */ + protected int getTransformedIndex(AnimationParameters params) { + switch (getOrder()) { + case ORDER_REVERSE: + return params.count - 1 - params.index; + case ORDER_RANDOM: + if (mRandomizer == null) { + mRandomizer = new Random(); + } + return (int) (params.count * mRandomizer.nextFloat()); + case ORDER_NORMAL: + default: + return params.index; + } + } + + /** + * The set of parameters that has to be attached to each view contained in + * the view group animated by the layout animation controller. These + * parameters are used to compute the start time of each individual view's + * animation. + */ + public static class AnimationParameters { + /** + * The number of children in the view group containing the view to which + * these parameters are attached. + */ + public int count; + + /** + * The index of the view to which these parameters are attached in its + * containing view group. + */ + public int index; + } +} diff --git a/android/view/animation/LinearInterpolator.java b/android/view/animation/LinearInterpolator.java new file mode 100644 index 00000000..2a047b48 --- /dev/null +++ b/android/view/animation/LinearInterpolator.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2006 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.animation; + +import android.content.Context; +import android.util.AttributeSet; + +import com.android.internal.view.animation.HasNativeInterpolator; +import com.android.internal.view.animation.NativeInterpolatorFactory; +import com.android.internal.view.animation.NativeInterpolatorFactoryHelper; + +/** + * An interpolator where the rate of change is constant + */ +@HasNativeInterpolator +public class LinearInterpolator extends BaseInterpolator implements NativeInterpolatorFactory { + + public LinearInterpolator() { + } + + public LinearInterpolator(Context context, AttributeSet attrs) { + } + + public float getInterpolation(float input) { + return input; + } + + /** @hide */ + @Override + public long createNativeInterpolator() { + return NativeInterpolatorFactoryHelper.createLinearInterpolator(); + } +} diff --git a/android/view/animation/OvershootInterpolator.java b/android/view/animation/OvershootInterpolator.java new file mode 100644 index 00000000..306688a1 --- /dev/null +++ b/android/view/animation/OvershootInterpolator.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2009 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.animation; + +import android.content.Context; +import android.content.res.Resources; +import android.content.res.Resources.Theme; +import android.content.res.TypedArray; +import android.util.AttributeSet; + +import com.android.internal.R; +import com.android.internal.view.animation.HasNativeInterpolator; +import com.android.internal.view.animation.NativeInterpolatorFactory; +import com.android.internal.view.animation.NativeInterpolatorFactoryHelper; + +/** + * An interpolator where the change flings forward and overshoots the last value + * then comes back. + */ +@HasNativeInterpolator +public class OvershootInterpolator extends BaseInterpolator implements NativeInterpolatorFactory { + private final float mTension; + + public OvershootInterpolator() { + mTension = 2.0f; + } + + /** + * @param tension Amount of overshoot. When tension equals 0.0f, there is + * no overshoot and the interpolator becomes a simple + * deceleration interpolator. + */ + public OvershootInterpolator(float tension) { + mTension = tension; + } + + public OvershootInterpolator(Context context, AttributeSet attrs) { + this(context.getResources(), context.getTheme(), attrs); + } + + /** @hide */ + public OvershootInterpolator(Resources res, Theme theme, AttributeSet attrs) { + TypedArray a; + if (theme != null) { + a = theme.obtainStyledAttributes(attrs, R.styleable.OvershootInterpolator, 0, 0); + } else { + a = res.obtainAttributes(attrs, R.styleable.OvershootInterpolator); + } + + mTension = a.getFloat(R.styleable.OvershootInterpolator_tension, 2.0f); + setChangingConfiguration(a.getChangingConfigurations()); + a.recycle(); + } + + public float getInterpolation(float t) { + // _o(t) = t * t * ((tension + 1) * t + tension) + // o(t) = _o(t - 1) + 1 + t -= 1.0f; + return t * t * ((mTension + 1) * t + mTension) + 1.0f; + } + + /** @hide */ + @Override + public long createNativeInterpolator() { + return NativeInterpolatorFactoryHelper.createOvershootInterpolator(mTension); + } +} diff --git a/android/view/animation/PathInterpolator.java b/android/view/animation/PathInterpolator.java new file mode 100644 index 00000000..924437a2 --- /dev/null +++ b/android/view/animation/PathInterpolator.java @@ -0,0 +1,243 @@ +/* + * Copyright (C) 2013 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.animation; + +import android.content.Context; +import android.content.res.Resources; +import android.content.res.Resources.Theme; +import android.content.res.TypedArray; +import android.graphics.Path; +import android.util.AttributeSet; +import android.util.PathParser; +import android.view.InflateException; + +import com.android.internal.R; +import com.android.internal.view.animation.HasNativeInterpolator; +import com.android.internal.view.animation.NativeInterpolatorFactory; +import com.android.internal.view.animation.NativeInterpolatorFactoryHelper; + +/** + * An interpolator that can traverse a Path that extends from <code>Point</code> + * <code>(0, 0)</code> to <code>(1, 1)</code>. The x coordinate along the <code>Path</code> + * is the input value and the output is the y coordinate of the line at that point. + * This means that the Path must conform to a function <code>y = f(x)</code>. + * + * <p>The <code>Path</code> must not have gaps in the x direction and must not + * loop back on itself such that there can be two points sharing the same x coordinate. + * It is alright to have a disjoint line in the vertical direction:</p> + * <p><blockquote><pre> + * Path path = new Path(); + * path.lineTo(0.25f, 0.25f); + * path.moveTo(0.25f, 0.5f); + * path.lineTo(1f, 1f); + * </pre></blockquote></p> + */ +@HasNativeInterpolator +public class PathInterpolator extends BaseInterpolator implements NativeInterpolatorFactory { + + // This governs how accurate the approximation of the Path is. + private static final float PRECISION = 0.002f; + + private float[] mX; // x coordinates in the line + + private float[] mY; // y coordinates in the line + + /** + * Create an interpolator for an arbitrary <code>Path</code>. The <code>Path</code> + * must begin at <code>(0, 0)</code> and end at <code>(1, 1)</code>. + * + * @param path The <code>Path</code> to use to make the line representing the interpolator. + */ + public PathInterpolator(Path path) { + initPath(path); + } + + /** + * Create an interpolator for a quadratic Bezier curve. The end points + * <code>(0, 0)</code> and <code>(1, 1)</code> are assumed. + * + * @param controlX The x coordinate of the quadratic Bezier control point. + * @param controlY The y coordinate of the quadratic Bezier control point. + */ + public PathInterpolator(float controlX, float controlY) { + initQuad(controlX, controlY); + } + + /** + * Create an interpolator for a cubic Bezier curve. The end points + * <code>(0, 0)</code> and <code>(1, 1)</code> are assumed. + * + * @param controlX1 The x coordinate of the first control point of the cubic Bezier. + * @param controlY1 The y coordinate of the first control point of the cubic Bezier. + * @param controlX2 The x coordinate of the second control point of the cubic Bezier. + * @param controlY2 The y coordinate of the second control point of the cubic Bezier. + */ + public PathInterpolator(float controlX1, float controlY1, float controlX2, float controlY2) { + initCubic(controlX1, controlY1, controlX2, controlY2); + } + + public PathInterpolator(Context context, AttributeSet attrs) { + this(context.getResources(), context.getTheme(), attrs); + } + + /** @hide */ + public PathInterpolator(Resources res, Theme theme, AttributeSet attrs) { + TypedArray a; + if (theme != null) { + a = theme.obtainStyledAttributes(attrs, R.styleable.PathInterpolator, 0, 0); + } else { + a = res.obtainAttributes(attrs, R.styleable.PathInterpolator); + } + parseInterpolatorFromTypeArray(a); + setChangingConfiguration(a.getChangingConfigurations()); + a.recycle(); + } + + private void parseInterpolatorFromTypeArray(TypedArray a) { + // If there is pathData defined in the xml file, then the controls points + // will be all coming from pathData. + if (a.hasValue(R.styleable.PathInterpolator_pathData)) { + String pathData = a.getString(R.styleable.PathInterpolator_pathData); + Path path = PathParser.createPathFromPathData(pathData); + if (path == null) { + throw new InflateException("The path is null, which is created" + + " from " + pathData); + } + initPath(path); + } else { + if (!a.hasValue(R.styleable.PathInterpolator_controlX1)) { + throw new InflateException("pathInterpolator requires the controlX1 attribute"); + } else if (!a.hasValue(R.styleable.PathInterpolator_controlY1)) { + throw new InflateException("pathInterpolator requires the controlY1 attribute"); + } + float x1 = a.getFloat(R.styleable.PathInterpolator_controlX1, 0); + float y1 = a.getFloat(R.styleable.PathInterpolator_controlY1, 0); + + boolean hasX2 = a.hasValue(R.styleable.PathInterpolator_controlX2); + boolean hasY2 = a.hasValue(R.styleable.PathInterpolator_controlY2); + + if (hasX2 != hasY2) { + throw new InflateException( + "pathInterpolator requires both controlX2 and controlY2 for cubic Beziers."); + } + + if (!hasX2) { + initQuad(x1, y1); + } else { + float x2 = a.getFloat(R.styleable.PathInterpolator_controlX2, 0); + float y2 = a.getFloat(R.styleable.PathInterpolator_controlY2, 0); + initCubic(x1, y1, x2, y2); + } + } + } + + private void initQuad(float controlX, float controlY) { + Path path = new Path(); + path.moveTo(0, 0); + path.quadTo(controlX, controlY, 1f, 1f); + initPath(path); + } + + private void initCubic(float x1, float y1, float x2, float y2) { + Path path = new Path(); + path.moveTo(0, 0); + path.cubicTo(x1, y1, x2, y2, 1f, 1f); + initPath(path); + } + + private void initPath(Path path) { + float[] pointComponents = path.approximate(PRECISION); + + int numPoints = pointComponents.length / 3; + if (pointComponents[1] != 0 || pointComponents[2] != 0 + || pointComponents[pointComponents.length - 2] != 1 + || pointComponents[pointComponents.length - 1] != 1) { + throw new IllegalArgumentException("The Path must start at (0,0) and end at (1,1)"); + } + + mX = new float[numPoints]; + mY = new float[numPoints]; + float prevX = 0; + float prevFraction = 0; + int componentIndex = 0; + for (int i = 0; i < numPoints; i++) { + float fraction = pointComponents[componentIndex++]; + float x = pointComponents[componentIndex++]; + float y = pointComponents[componentIndex++]; + if (fraction == prevFraction && x != prevX) { + throw new IllegalArgumentException( + "The Path cannot have discontinuity in the X axis."); + } + if (x < prevX) { + throw new IllegalArgumentException("The Path cannot loop back on itself."); + } + mX[i] = x; + mY[i] = y; + prevX = x; + prevFraction = fraction; + } + } + + /** + * Using the line in the Path in this interpolator that can be described as + * <code>y = f(x)</code>, finds the y coordinate of the line given <code>t</code> + * as the x coordinate. Values less than 0 will always return 0 and values greater + * than 1 will always return 1. + * + * @param t Treated as the x coordinate along the line. + * @return The y coordinate of the Path along the line where x = <code>t</code>. + * @see Interpolator#getInterpolation(float) + */ + @Override + public float getInterpolation(float t) { + if (t <= 0) { + return 0; + } else if (t >= 1) { + return 1; + } + // Do a binary search for the correct x to interpolate between. + int startIndex = 0; + int endIndex = mX.length - 1; + + while (endIndex - startIndex > 1) { + int midIndex = (startIndex + endIndex) / 2; + if (t < mX[midIndex]) { + endIndex = midIndex; + } else { + startIndex = midIndex; + } + } + + float xRange = mX[endIndex] - mX[startIndex]; + if (xRange == 0) { + return mY[startIndex]; + } + + float tInRange = t - mX[startIndex]; + float fraction = tInRange / xRange; + + float startY = mY[startIndex]; + float endY = mY[endIndex]; + return startY + (fraction * (endY - startY)); + } + + /** @hide **/ + @Override + public long createNativeInterpolator() { + return NativeInterpolatorFactoryHelper.createPathInterpolator(mX, mY); + } + +} diff --git a/android/view/animation/RotateAnimation.java b/android/view/animation/RotateAnimation.java new file mode 100644 index 00000000..3c325d9b --- /dev/null +++ b/android/view/animation/RotateAnimation.java @@ -0,0 +1,183 @@ +/* + * Copyright (C) 2006 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.animation; + +import android.content.Context; +import android.content.res.TypedArray; +import android.util.AttributeSet; + +/** + * An animation that controls the rotation of an object. This rotation takes + * place in the X-Y plane. You can specify the point to use for the center of + * the rotation, where (0,0) is the top left point. If not specified, (0,0) is + * the default rotation point. + * + */ +public class RotateAnimation extends Animation { + private float mFromDegrees; + private float mToDegrees; + + private int mPivotXType = ABSOLUTE; + private int mPivotYType = ABSOLUTE; + private float mPivotXValue = 0.0f; + private float mPivotYValue = 0.0f; + + private float mPivotX; + private float mPivotY; + + /** + * Constructor used when a RotateAnimation is loaded from a resource. + * + * @param context Application context to use + * @param attrs Attribute set from which to read values + */ + public RotateAnimation(Context context, AttributeSet attrs) { + super(context, attrs); + + TypedArray a = context.obtainStyledAttributes(attrs, + com.android.internal.R.styleable.RotateAnimation); + + mFromDegrees = a.getFloat( + com.android.internal.R.styleable.RotateAnimation_fromDegrees, 0.0f); + mToDegrees = a.getFloat(com.android.internal.R.styleable.RotateAnimation_toDegrees, 0.0f); + + Description d = Description.parseValue(a.peekValue( + com.android.internal.R.styleable.RotateAnimation_pivotX)); + mPivotXType = d.type; + mPivotXValue = d.value; + + d = Description.parseValue(a.peekValue( + com.android.internal.R.styleable.RotateAnimation_pivotY)); + mPivotYType = d.type; + mPivotYValue = d.value; + + a.recycle(); + + initializePivotPoint(); + } + + /** + * Constructor to use when building a RotateAnimation from code. + * Default pivotX/pivotY point is (0,0). + * + * @param fromDegrees Rotation offset to apply at the start of the + * animation. + * + * @param toDegrees Rotation offset to apply at the end of the animation. + */ + public RotateAnimation(float fromDegrees, float toDegrees) { + mFromDegrees = fromDegrees; + mToDegrees = toDegrees; + mPivotX = 0.0f; + mPivotY = 0.0f; + } + + /** + * Constructor to use when building a RotateAnimation from code + * + * @param fromDegrees Rotation offset to apply at the start of the + * animation. + * + * @param toDegrees Rotation offset to apply at the end of the animation. + * + * @param pivotX The X coordinate of the point about which the object is + * being rotated, specified as an absolute number where 0 is the left + * edge. + * @param pivotY The Y coordinate of the point about which the object is + * being rotated, specified as an absolute number where 0 is the top + * edge. + */ + public RotateAnimation(float fromDegrees, float toDegrees, float pivotX, float pivotY) { + mFromDegrees = fromDegrees; + mToDegrees = toDegrees; + + mPivotXType = ABSOLUTE; + mPivotYType = ABSOLUTE; + mPivotXValue = pivotX; + mPivotYValue = pivotY; + initializePivotPoint(); + } + + /** + * Constructor to use when building a RotateAnimation from code + * + * @param fromDegrees Rotation offset to apply at the start of the + * animation. + * + * @param toDegrees Rotation offset to apply at the end of the animation. + * + * @param pivotXType Specifies how pivotXValue should be interpreted. One of + * Animation.ABSOLUTE, Animation.RELATIVE_TO_SELF, or + * Animation.RELATIVE_TO_PARENT. + * @param pivotXValue The X coordinate of the point about which the object + * is being rotated, specified as an absolute number where 0 is the + * left edge. This value can either be an absolute number if + * pivotXType is ABSOLUTE, or a percentage (where 1.0 is 100%) + * otherwise. + * @param pivotYType Specifies how pivotYValue should be interpreted. One of + * Animation.ABSOLUTE, Animation.RELATIVE_TO_SELF, or + * Animation.RELATIVE_TO_PARENT. + * @param pivotYValue The Y coordinate of the point about which the object + * is being rotated, specified as an absolute number where 0 is the + * top edge. This value can either be an absolute number if + * pivotYType is ABSOLUTE, or a percentage (where 1.0 is 100%) + * otherwise. + */ + public RotateAnimation(float fromDegrees, float toDegrees, int pivotXType, float pivotXValue, + int pivotYType, float pivotYValue) { + mFromDegrees = fromDegrees; + mToDegrees = toDegrees; + + mPivotXValue = pivotXValue; + mPivotXType = pivotXType; + mPivotYValue = pivotYValue; + mPivotYType = pivotYType; + initializePivotPoint(); + } + + /** + * Called at the end of constructor methods to initialize, if possible, values for + * the pivot point. This is only possible for ABSOLUTE pivot values. + */ + private void initializePivotPoint() { + if (mPivotXType == ABSOLUTE) { + mPivotX = mPivotXValue; + } + if (mPivotYType == ABSOLUTE) { + mPivotY = mPivotYValue; + } + } + + @Override + protected void applyTransformation(float interpolatedTime, Transformation t) { + float degrees = mFromDegrees + ((mToDegrees - mFromDegrees) * interpolatedTime); + float scale = getScaleFactor(); + + if (mPivotX == 0.0f && mPivotY == 0.0f) { + t.getMatrix().setRotate(degrees); + } else { + t.getMatrix().setRotate(degrees, mPivotX * scale, mPivotY * scale); + } + } + + @Override + public void initialize(int width, int height, int parentWidth, int parentHeight) { + super.initialize(width, height, parentWidth, parentHeight); + mPivotX = resolveSize(mPivotXType, mPivotXValue, width, parentWidth); + mPivotY = resolveSize(mPivotYType, mPivotYValue, height, parentHeight); + } +} diff --git a/android/view/animation/ScaleAnimation.java b/android/view/animation/ScaleAnimation.java new file mode 100644 index 00000000..e9a84364 --- /dev/null +++ b/android/view/animation/ScaleAnimation.java @@ -0,0 +1,289 @@ +/* + * Copyright (C) 2006 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.animation; + +import android.content.Context; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.util.AttributeSet; +import android.util.TypedValue; + +/** + * An animation that controls the scale of an object. You can specify the point + * to use for the center of scaling. + * + */ +public class ScaleAnimation extends Animation { + private final Resources mResources; + + private float mFromX; + private float mToX; + private float mFromY; + private float mToY; + + private int mFromXType = TypedValue.TYPE_NULL; + private int mToXType = TypedValue.TYPE_NULL; + private int mFromYType = TypedValue.TYPE_NULL; + private int mToYType = TypedValue.TYPE_NULL; + + private int mFromXData = 0; + private int mToXData = 0; + private int mFromYData = 0; + private int mToYData = 0; + + private int mPivotXType = ABSOLUTE; + private int mPivotYType = ABSOLUTE; + private float mPivotXValue = 0.0f; + private float mPivotYValue = 0.0f; + + private float mPivotX; + private float mPivotY; + + /** + * Constructor used when a ScaleAnimation is loaded from a resource. + * + * @param context Application context to use + * @param attrs Attribute set from which to read values + */ + public ScaleAnimation(Context context, AttributeSet attrs) { + super(context, attrs); + + mResources = context.getResources(); + + TypedArray a = context.obtainStyledAttributes(attrs, + com.android.internal.R.styleable.ScaleAnimation); + + TypedValue tv = a.peekValue( + com.android.internal.R.styleable.ScaleAnimation_fromXScale); + mFromX = 0.0f; + if (tv != null) { + if (tv.type == TypedValue.TYPE_FLOAT) { + // This is a scaling factor. + mFromX = tv.getFloat(); + } else { + mFromXType = tv.type; + mFromXData = tv.data; + } + } + tv = a.peekValue( + com.android.internal.R.styleable.ScaleAnimation_toXScale); + mToX = 0.0f; + if (tv != null) { + if (tv.type == TypedValue.TYPE_FLOAT) { + // This is a scaling factor. + mToX = tv.getFloat(); + } else { + mToXType = tv.type; + mToXData = tv.data; + } + } + + tv = a.peekValue( + com.android.internal.R.styleable.ScaleAnimation_fromYScale); + mFromY = 0.0f; + if (tv != null) { + if (tv.type == TypedValue.TYPE_FLOAT) { + // This is a scaling factor. + mFromY = tv.getFloat(); + } else { + mFromYType = tv.type; + mFromYData = tv.data; + } + } + tv = a.peekValue( + com.android.internal.R.styleable.ScaleAnimation_toYScale); + mToY = 0.0f; + if (tv != null) { + if (tv.type == TypedValue.TYPE_FLOAT) { + // This is a scaling factor. + mToY = tv.getFloat(); + } else { + mToYType = tv.type; + mToYData = tv.data; + } + } + + Description d = Description.parseValue(a.peekValue( + com.android.internal.R.styleable.ScaleAnimation_pivotX)); + mPivotXType = d.type; + mPivotXValue = d.value; + + d = Description.parseValue(a.peekValue( + com.android.internal.R.styleable.ScaleAnimation_pivotY)); + mPivotYType = d.type; + mPivotYValue = d.value; + + a.recycle(); + + initializePivotPoint(); + } + + /** + * Constructor to use when building a ScaleAnimation from code + * + * @param fromX Horizontal scaling factor to apply at the start of the + * animation + * @param toX Horizontal scaling factor to apply at the end of the animation + * @param fromY Vertical scaling factor to apply at the start of the + * animation + * @param toY Vertical scaling factor to apply at the end of the animation + */ + public ScaleAnimation(float fromX, float toX, float fromY, float toY) { + mResources = null; + mFromX = fromX; + mToX = toX; + mFromY = fromY; + mToY = toY; + mPivotX = 0; + mPivotY = 0; + } + + /** + * Constructor to use when building a ScaleAnimation from code + * + * @param fromX Horizontal scaling factor to apply at the start of the + * animation + * @param toX Horizontal scaling factor to apply at the end of the animation + * @param fromY Vertical scaling factor to apply at the start of the + * animation + * @param toY Vertical scaling factor to apply at the end of the animation + * @param pivotX The X coordinate of the point about which the object is + * being scaled, specified as an absolute number where 0 is the left + * edge. (This point remains fixed while the object changes size.) + * @param pivotY The Y coordinate of the point about which the object is + * being scaled, specified as an absolute number where 0 is the top + * edge. (This point remains fixed while the object changes size.) + */ + public ScaleAnimation(float fromX, float toX, float fromY, float toY, + float pivotX, float pivotY) { + mResources = null; + mFromX = fromX; + mToX = toX; + mFromY = fromY; + mToY = toY; + + mPivotXType = ABSOLUTE; + mPivotYType = ABSOLUTE; + mPivotXValue = pivotX; + mPivotYValue = pivotY; + initializePivotPoint(); + } + + /** + * Constructor to use when building a ScaleAnimation from code + * + * @param fromX Horizontal scaling factor to apply at the start of the + * animation + * @param toX Horizontal scaling factor to apply at the end of the animation + * @param fromY Vertical scaling factor to apply at the start of the + * animation + * @param toY Vertical scaling factor to apply at the end of the animation + * @param pivotXType Specifies how pivotXValue should be interpreted. One of + * Animation.ABSOLUTE, Animation.RELATIVE_TO_SELF, or + * Animation.RELATIVE_TO_PARENT. + * @param pivotXValue The X coordinate of the point about which the object + * is being scaled, specified as an absolute number where 0 is the + * left edge. (This point remains fixed while the object changes + * size.) This value can either be an absolute number if pivotXType + * is ABSOLUTE, or a percentage (where 1.0 is 100%) otherwise. + * @param pivotYType Specifies how pivotYValue should be interpreted. One of + * Animation.ABSOLUTE, Animation.RELATIVE_TO_SELF, or + * Animation.RELATIVE_TO_PARENT. + * @param pivotYValue The Y coordinate of the point about which the object + * is being scaled, specified as an absolute number where 0 is the + * top edge. (This point remains fixed while the object changes + * size.) This value can either be an absolute number if pivotYType + * is ABSOLUTE, or a percentage (where 1.0 is 100%) otherwise. + */ + public ScaleAnimation(float fromX, float toX, float fromY, float toY, + int pivotXType, float pivotXValue, int pivotYType, float pivotYValue) { + mResources = null; + mFromX = fromX; + mToX = toX; + mFromY = fromY; + mToY = toY; + + mPivotXValue = pivotXValue; + mPivotXType = pivotXType; + mPivotYValue = pivotYValue; + mPivotYType = pivotYType; + initializePivotPoint(); + } + + /** + * Called at the end of constructor methods to initialize, if possible, values for + * the pivot point. This is only possible for ABSOLUTE pivot values. + */ + private void initializePivotPoint() { + if (mPivotXType == ABSOLUTE) { + mPivotX = mPivotXValue; + } + if (mPivotYType == ABSOLUTE) { + mPivotY = mPivotYValue; + } + } + + @Override + protected void applyTransformation(float interpolatedTime, Transformation t) { + float sx = 1.0f; + float sy = 1.0f; + float scale = getScaleFactor(); + + if (mFromX != 1.0f || mToX != 1.0f) { + sx = mFromX + ((mToX - mFromX) * interpolatedTime); + } + if (mFromY != 1.0f || mToY != 1.0f) { + sy = mFromY + ((mToY - mFromY) * interpolatedTime); + } + + if (mPivotX == 0 && mPivotY == 0) { + t.getMatrix().setScale(sx, sy); + } else { + t.getMatrix().setScale(sx, sy, scale * mPivotX, scale * mPivotY); + } + } + + float resolveScale(float scale, int type, int data, int size, int psize) { + float targetSize; + if (type == TypedValue.TYPE_FRACTION) { + targetSize = TypedValue.complexToFraction(data, size, psize); + } else if (type == TypedValue.TYPE_DIMENSION) { + targetSize = TypedValue.complexToDimension(data, mResources.getDisplayMetrics()); + } else { + return scale; + } + + if (size == 0) { + return 1; + } + + return targetSize/(float)size; + } + + @Override + public void initialize(int width, int height, int parentWidth, int parentHeight) { + super.initialize(width, height, parentWidth, parentHeight); + + mFromX = resolveScale(mFromX, mFromXType, mFromXData, width, parentWidth); + mToX = resolveScale(mToX, mToXType, mToXData, width, parentWidth); + mFromY = resolveScale(mFromY, mFromYType, mFromYData, height, parentHeight); + mToY = resolveScale(mToY, mToYType, mToYData, height, parentHeight); + + mPivotX = resolveSize(mPivotXType, mPivotXValue, width, parentWidth); + mPivotY = resolveSize(mPivotYType, mPivotYValue, height, parentHeight); + } +} diff --git a/android/view/animation/Transformation.java b/android/view/animation/Transformation.java new file mode 100644 index 00000000..8eb5b5cf --- /dev/null +++ b/android/view/animation/Transformation.java @@ -0,0 +1,247 @@ +/* + * Copyright (C) 2006 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.animation; + +import android.annotation.FloatRange; +import android.graphics.Matrix; +import android.graphics.Rect; + +import java.io.PrintWriter; + +/** + * Defines the transformation to be applied at + * one point in time of an Animation. + * + */ +public class Transformation { + /** + * Indicates a transformation that has no effect (alpha = 1 and identity matrix.) + */ + public static final int TYPE_IDENTITY = 0x0; + /** + * Indicates a transformation that applies an alpha only (uses an identity matrix.) + */ + public static final int TYPE_ALPHA = 0x1; + /** + * Indicates a transformation that applies a matrix only (alpha = 1.) + */ + public static final int TYPE_MATRIX = 0x2; + /** + * Indicates a transformation that applies an alpha and a matrix. + */ + public static final int TYPE_BOTH = TYPE_ALPHA | TYPE_MATRIX; + + protected Matrix mMatrix; + protected float mAlpha; + protected int mTransformationType; + + private boolean mHasClipRect; + private Rect mClipRect = new Rect(); + + /** + * Creates a new transformation with alpha = 1 and the identity matrix. + */ + public Transformation() { + clear(); + } + + /** + * Reset the transformation to a state that leaves the object + * being animated in an unmodified state. The transformation type is + * {@link #TYPE_BOTH} by default. + */ + public void clear() { + if (mMatrix == null) { + mMatrix = new Matrix(); + } else { + mMatrix.reset(); + } + mClipRect.setEmpty(); + mHasClipRect = false; + mAlpha = 1.0f; + mTransformationType = TYPE_BOTH; + } + + /** + * Indicates the nature of this transformation. + * + * @return {@link #TYPE_ALPHA}, {@link #TYPE_MATRIX}, + * {@link #TYPE_BOTH} or {@link #TYPE_IDENTITY}. + */ + public int getTransformationType() { + return mTransformationType; + } + + /** + * Sets the transformation type. + * + * @param transformationType One of {@link #TYPE_ALPHA}, + * {@link #TYPE_MATRIX}, {@link #TYPE_BOTH} or + * {@link #TYPE_IDENTITY}. + */ + public void setTransformationType(int transformationType) { + mTransformationType = transformationType; + } + + /** + * Clones the specified transformation. + * + * @param t The transformation to clone. + */ + public void set(Transformation t) { + mAlpha = t.getAlpha(); + mMatrix.set(t.getMatrix()); + if (t.mHasClipRect) { + setClipRect(t.getClipRect()); + } else { + mHasClipRect = false; + mClipRect.setEmpty(); + } + mTransformationType = t.getTransformationType(); + } + + /** + * Apply this Transformation to an existing Transformation, e.g. apply + * a scale effect to something that has already been rotated. + * @param t + */ + public void compose(Transformation t) { + mAlpha *= t.getAlpha(); + mMatrix.preConcat(t.getMatrix()); + if (t.mHasClipRect) { + Rect bounds = t.getClipRect(); + if (mHasClipRect) { + setClipRect(mClipRect.left + bounds.left, mClipRect.top + bounds.top, + mClipRect.right + bounds.right, mClipRect.bottom + bounds.bottom); + } else { + setClipRect(bounds); + } + } + } + + /** + * Like {@link #compose(Transformation)} but does this.postConcat(t) of + * the transformation matrix. + * @hide + */ + public void postCompose(Transformation t) { + mAlpha *= t.getAlpha(); + mMatrix.postConcat(t.getMatrix()); + if (t.mHasClipRect) { + Rect bounds = t.getClipRect(); + if (mHasClipRect) { + setClipRect(mClipRect.left + bounds.left, mClipRect.top + bounds.top, + mClipRect.right + bounds.right, mClipRect.bottom + bounds.bottom); + } else { + setClipRect(bounds); + } + } + } + + /** + * @return The 3x3 Matrix representing the trnasformation to apply to the + * coordinates of the object being animated + */ + public Matrix getMatrix() { + return mMatrix; + } + + /** + * Sets the degree of transparency + * @param alpha 1.0 means fully opaqe and 0.0 means fully transparent + */ + public void setAlpha(@FloatRange(from=0.0, to=1.0) float alpha) { + mAlpha = alpha; + } + + /** + * Sets the current Transform's clip rect + * @hide + */ + public void setClipRect(Rect r) { + setClipRect(r.left, r.top, r.right, r.bottom); + } + + /** + * Sets the current Transform's clip rect + * @hide + */ + public void setClipRect(int l, int t, int r, int b) { + mClipRect.set(l, t, r, b); + mHasClipRect = true; + } + + /** + * Returns the current Transform's clip rect + * @hide + */ + public Rect getClipRect() { + return mClipRect; + } + + /** + * Returns whether the current Transform's clip rect is set + * @hide + */ + public boolean hasClipRect() { + return mHasClipRect; + } + + /** + * @return The degree of transparency + */ + public float getAlpha() { + return mAlpha; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(64); + sb.append("Transformation"); + toShortString(sb); + return sb.toString(); + } + + /** + * Return a string representation of the transformation in a compact form. + */ + public String toShortString() { + StringBuilder sb = new StringBuilder(64); + toShortString(sb); + return sb.toString(); + } + + /** + * @hide + */ + public void toShortString(StringBuilder sb) { + sb.append("{alpha="); sb.append(mAlpha); + sb.append(" matrix="); mMatrix.toShortString(sb); + sb.append('}'); + } + + /** + * Print short string, to optimize dumping. + * @hide + */ + public void printShortString(PrintWriter pw) { + pw.print("{alpha="); pw.print(mAlpha); + pw.print(" matrix="); + mMatrix.printShortString(pw); + pw.print('}'); + } +} diff --git a/android/view/animation/TranslateAnimation.java b/android/view/animation/TranslateAnimation.java new file mode 100644 index 00000000..216022b2 --- /dev/null +++ b/android/view/animation/TranslateAnimation.java @@ -0,0 +1,178 @@ +/* + * Copyright (C) 2006 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.animation; + +import android.content.Context; +import android.content.res.TypedArray; +import android.util.AttributeSet; + +/** + * An animation that controls the position of an object. See the + * {@link android.view.animation full package} description for details and + * sample code. + * + */ +public class TranslateAnimation extends Animation { + private int mFromXType = ABSOLUTE; + private int mToXType = ABSOLUTE; + + private int mFromYType = ABSOLUTE; + private int mToYType = ABSOLUTE; + + /** @hide */ + protected float mFromXValue = 0.0f; + /** @hide */ + protected float mToXValue = 0.0f; + + /** @hide */ + protected float mFromYValue = 0.0f; + /** @hide */ + protected float mToYValue = 0.0f; + + /** @hide */ + protected float mFromXDelta; + /** @hide */ + protected float mToXDelta; + /** @hide */ + protected float mFromYDelta; + /** @hide */ + protected float mToYDelta; + + /** + * Constructor used when a TranslateAnimation is loaded from a resource. + * + * @param context Application context to use + * @param attrs Attribute set from which to read values + */ + public TranslateAnimation(Context context, AttributeSet attrs) { + super(context, attrs); + + TypedArray a = context.obtainStyledAttributes(attrs, + com.android.internal.R.styleable.TranslateAnimation); + + Description d = Description.parseValue(a.peekValue( + com.android.internal.R.styleable.TranslateAnimation_fromXDelta)); + mFromXType = d.type; + mFromXValue = d.value; + + d = Description.parseValue(a.peekValue( + com.android.internal.R.styleable.TranslateAnimation_toXDelta)); + mToXType = d.type; + mToXValue = d.value; + + d = Description.parseValue(a.peekValue( + com.android.internal.R.styleable.TranslateAnimation_fromYDelta)); + mFromYType = d.type; + mFromYValue = d.value; + + d = Description.parseValue(a.peekValue( + com.android.internal.R.styleable.TranslateAnimation_toYDelta)); + mToYType = d.type; + mToYValue = d.value; + + a.recycle(); + } + + /** + * Constructor to use when building a TranslateAnimation from code + * + * @param fromXDelta Change in X coordinate to apply at the start of the + * animation + * @param toXDelta Change in X coordinate to apply at the end of the + * animation + * @param fromYDelta Change in Y coordinate to apply at the start of the + * animation + * @param toYDelta Change in Y coordinate to apply at the end of the + * animation + */ + public TranslateAnimation(float fromXDelta, float toXDelta, float fromYDelta, float toYDelta) { + mFromXValue = fromXDelta; + mToXValue = toXDelta; + mFromYValue = fromYDelta; + mToYValue = toYDelta; + + mFromXType = ABSOLUTE; + mToXType = ABSOLUTE; + mFromYType = ABSOLUTE; + mToYType = ABSOLUTE; + } + + /** + * Constructor to use when building a TranslateAnimation from code + * + * @param fromXType Specifies how fromXValue should be interpreted. One of + * Animation.ABSOLUTE, Animation.RELATIVE_TO_SELF, or + * Animation.RELATIVE_TO_PARENT. + * @param fromXValue Change in X coordinate to apply at the start of the + * animation. This value can either be an absolute number if fromXType + * is ABSOLUTE, or a percentage (where 1.0 is 100%) otherwise. + * @param toXType Specifies how toXValue should be interpreted. One of + * Animation.ABSOLUTE, Animation.RELATIVE_TO_SELF, or + * Animation.RELATIVE_TO_PARENT. + * @param toXValue Change in X coordinate to apply at the end of the + * animation. This value can either be an absolute number if toXType + * is ABSOLUTE, or a percentage (where 1.0 is 100%) otherwise. + * @param fromYType Specifies how fromYValue should be interpreted. One of + * Animation.ABSOLUTE, Animation.RELATIVE_TO_SELF, or + * Animation.RELATIVE_TO_PARENT. + * @param fromYValue Change in Y coordinate to apply at the start of the + * animation. This value can either be an absolute number if fromYType + * is ABSOLUTE, or a percentage (where 1.0 is 100%) otherwise. + * @param toYType Specifies how toYValue should be interpreted. One of + * Animation.ABSOLUTE, Animation.RELATIVE_TO_SELF, or + * Animation.RELATIVE_TO_PARENT. + * @param toYValue Change in Y coordinate to apply at the end of the + * animation. This value can either be an absolute number if toYType + * is ABSOLUTE, or a percentage (where 1.0 is 100%) otherwise. + */ + public TranslateAnimation(int fromXType, float fromXValue, int toXType, float toXValue, + int fromYType, float fromYValue, int toYType, float toYValue) { + + mFromXValue = fromXValue; + mToXValue = toXValue; + mFromYValue = fromYValue; + mToYValue = toYValue; + + mFromXType = fromXType; + mToXType = toXType; + mFromYType = fromYType; + mToYType = toYType; + } + + + @Override + protected void applyTransformation(float interpolatedTime, Transformation t) { + float dx = mFromXDelta; + float dy = mFromYDelta; + if (mFromXDelta != mToXDelta) { + dx = mFromXDelta + ((mToXDelta - mFromXDelta) * interpolatedTime); + } + if (mFromYDelta != mToYDelta) { + dy = mFromYDelta + ((mToYDelta - mFromYDelta) * interpolatedTime); + } + t.getMatrix().setTranslate(dx, dy); + } + + @Override + public void initialize(int width, int height, int parentWidth, int parentHeight) { + super.initialize(width, height, parentWidth, parentHeight); + mFromXDelta = resolveSize(mFromXType, mFromXValue, width, parentWidth); + mToXDelta = resolveSize(mToXType, mToXValue, width, parentWidth); + mFromYDelta = resolveSize(mFromYType, mFromYValue, height, parentHeight); + mToYDelta = resolveSize(mToYType, mToYValue, height, parentHeight); + } +} diff --git a/android/view/animation/TranslateXAnimation.java b/android/view/animation/TranslateXAnimation.java new file mode 100644 index 00000000..d75323f2 --- /dev/null +++ b/android/view/animation/TranslateXAnimation.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2015 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.animation; + +import android.graphics.Matrix; + +/** + * Special case of TranslateAnimation that translates only horizontally, picking up the + * vertical values from whatever is set on the Transformation already. When used in + * conjunction with a TranslateYAnimation, allows independent animation of x and y + * position. + * @hide + */ +public class TranslateXAnimation extends TranslateAnimation { + float[] mTmpValues = new float[9]; + + /** + * Constructor. Passes in 0 for the y parameters of TranslateAnimation + */ + public TranslateXAnimation(float fromXDelta, float toXDelta) { + super(fromXDelta, toXDelta, 0, 0); + } + + /** + * Constructor. Passes in 0 for the y parameters of TranslateAnimation + */ + public TranslateXAnimation(int fromXType, float fromXValue, int toXType, float toXValue) { + super(fromXType, fromXValue, toXType, toXValue, ABSOLUTE, 0, ABSOLUTE, 0); + } + + /** + * Calculates and sets x translation values on given transformation. + */ + @Override + protected void applyTransformation(float interpolatedTime, Transformation t) { + Matrix m = t.getMatrix(); + m.getValues(mTmpValues); + float dx = mFromXDelta + ((mToXDelta - mFromXDelta) * interpolatedTime); + t.getMatrix().setTranslate(dx, mTmpValues[Matrix.MTRANS_Y]); + } +} diff --git a/android/view/animation/TranslateYAnimation.java b/android/view/animation/TranslateYAnimation.java new file mode 100644 index 00000000..714558dc --- /dev/null +++ b/android/view/animation/TranslateYAnimation.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2015 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.animation; + +import android.graphics.Matrix; + +/** + * Special case of TranslateAnimation that translates only vertically, picking up the + * horizontal values from whatever is set on the Transformation already. When used in + * conjunction with a TranslateXAnimation, allows independent animation of x and y + * position. + * @hide + */ +public class TranslateYAnimation extends TranslateAnimation { + float[] mTmpValues = new float[9]; + + /** + * Constructor. Passes in 0 for the x parameters of TranslateAnimation + */ + public TranslateYAnimation(float fromYDelta, float toYDelta) { + super(0, 0, fromYDelta, toYDelta); + } + + /** + * Constructor. Passes in 0 for the x parameters of TranslateAnimation + */ + public TranslateYAnimation(int fromYType, float fromYValue, int toYType, float toYValue) { + super(ABSOLUTE, 0, ABSOLUTE, 0, fromYType, fromYValue, toYType, toYValue); + } + + /** + * Calculates and sets y translation values on given transformation. + */ + @Override + protected void applyTransformation(float interpolatedTime, Transformation t) { + Matrix m = t.getMatrix(); + m.getValues(mTmpValues); + float dy = mFromYDelta + ((mToYDelta - mFromYDelta) * interpolatedTime); + t.getMatrix().setTranslate(mTmpValues[Matrix.MTRANS_X], dy); + } +} |