| /* | |
| * 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 animate the children of a layout or a view | |
| * group. 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), context); | |
| 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; | |
| } | |
| } |