/* * 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.view.View; import android.view.ViewGroup; import android.content.Context; import android.content.res.TypedArray; import android.util.AttributeSet; 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 DIRECTION_LEFT_TO_RIGHT | DIRECTION_TOP_TO_BOTTOM. 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, * DIRECTION_BOTTOM_TO_TOP | DIRECTION_RIGHT_TO_LEFT. * * @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; } }