1/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.view.animation;
18
19import android.view.View;
20import android.view.ViewGroup;
21import android.content.Context;
22import android.content.res.TypedArray;
23import android.util.AttributeSet;
24
25import java.util.Random;
26
27/**
28 * A layout animation controller is used to animated a grid layout's children.
29 *
30 * While {@link LayoutAnimationController} relies only on the index of the child
31 * in the view group to compute the animation delay, this class uses both the
32 * X and Y coordinates of the child within a grid.
33 *
34 * In addition, the animation direction can be controlled. The default direction
35 * is <code>DIRECTION_LEFT_TO_RIGHT | DIRECTION_TOP_TO_BOTTOM</code>. You can
36 * also set the animation priority to columns or rows. The default priority is
37 * none.
38 *
39 * Information used to compute the animation delay of each child are stored
40 * in an instance of
41 * {@link android.view.animation.GridLayoutAnimationController.AnimationParameters},
42 * itself stored in the {@link android.view.ViewGroup.LayoutParams} of the view.
43 *
44 * @see LayoutAnimationController
45 * @see android.widget.GridView
46 *
47 * @attr ref android.R.styleable#GridLayoutAnimation_columnDelay
48 * @attr ref android.R.styleable#GridLayoutAnimation_rowDelay
49 * @attr ref android.R.styleable#GridLayoutAnimation_direction
50 * @attr ref android.R.styleable#GridLayoutAnimation_directionPriority
51 */
52public class GridLayoutAnimationController extends LayoutAnimationController {
53    /**
54     * Animates the children starting from the left of the grid to the right.
55     */
56    public static final int DIRECTION_LEFT_TO_RIGHT = 0x0;
57
58    /**
59     * Animates the children starting from the right of the grid to the left.
60     */
61    public static final int DIRECTION_RIGHT_TO_LEFT = 0x1;
62
63    /**
64     * Animates the children starting from the top of the grid to the bottom.
65     */
66    public static final int DIRECTION_TOP_TO_BOTTOM = 0x0;
67
68    /**
69     * Animates the children starting from the bottom of the grid to the top.
70     */
71    public static final int DIRECTION_BOTTOM_TO_TOP = 0x2;
72
73    /**
74     * Bitmask used to retrieve the horizontal component of the direction.
75     */
76    public static final int DIRECTION_HORIZONTAL_MASK = 0x1;
77
78    /**
79     * Bitmask used to retrieve the vertical component of the direction.
80     */
81    public static final int DIRECTION_VERTICAL_MASK   = 0x2;
82
83    /**
84     * Rows and columns are animated at the same time.
85     */
86    public static final int PRIORITY_NONE   = 0;
87
88    /**
89     * Columns are animated first.
90     */
91    public static final int PRIORITY_COLUMN = 1;
92
93    /**
94     * Rows are animated first.
95     */
96    public static final int PRIORITY_ROW    = 2;
97
98    private float mColumnDelay;
99    private float mRowDelay;
100
101    private int mDirection;
102    private int mDirectionPriority;
103
104    /**
105     * Creates a new grid layout animation controller from external resources.
106     *
107     * @param context the Context the view  group is running in, through which
108     *        it can access the resources
109     * @param attrs the attributes of the XML tag that is inflating the
110     *        layout animation controller
111     */
112    public GridLayoutAnimationController(Context context, AttributeSet attrs) {
113        super(context, attrs);
114
115        TypedArray a = context.obtainStyledAttributes(attrs,
116                com.android.internal.R.styleable.GridLayoutAnimation);
117
118        Animation.Description d = Animation.Description.parseValue(
119                a.peekValue(com.android.internal.R.styleable.GridLayoutAnimation_columnDelay));
120        mColumnDelay = d.value;
121        d = Animation.Description.parseValue(
122                a.peekValue(com.android.internal.R.styleable.GridLayoutAnimation_rowDelay));
123        mRowDelay = d.value;
124        //noinspection PointlessBitwiseExpression
125        mDirection = a.getInt(com.android.internal.R.styleable.GridLayoutAnimation_direction,
126                DIRECTION_LEFT_TO_RIGHT | DIRECTION_TOP_TO_BOTTOM);
127        mDirectionPriority = a.getInt(com.android.internal.R.styleable.GridLayoutAnimation_directionPriority,
128                PRIORITY_NONE);
129
130        a.recycle();
131    }
132
133    /**
134     * Creates a new layout animation controller with a delay of 50%
135     * for both rows and columns and the specified animation.
136     *
137     * @param animation the animation to use on each child of the view group
138     */
139    public GridLayoutAnimationController(Animation animation) {
140        this(animation, 0.5f, 0.5f);
141    }
142
143    /**
144     * Creates a new layout animation controller with the specified delays
145     * and the specified animation.
146     *
147     * @param animation the animation to use on each child of the view group
148     * @param columnDelay the delay by which each column animation must be offset
149     * @param rowDelay the delay by which each row animation must be offset
150     */
151    public GridLayoutAnimationController(Animation animation, float columnDelay, float rowDelay) {
152        super(animation);
153        mColumnDelay = columnDelay;
154        mRowDelay = rowDelay;
155    }
156
157    /**
158     * Returns the delay by which the children's animation are offset from one
159     * column to the other. The delay is expressed as a fraction of the
160     * animation duration.
161     *
162     * @return a fraction of the animation duration
163     *
164     * @see #setColumnDelay(float)
165     * @see #getRowDelay()
166     * @see #setRowDelay(float)
167     */
168    public float getColumnDelay() {
169        return mColumnDelay;
170    }
171
172    /**
173     * Sets the delay, as a fraction of the animation duration, by which the
174     * children's animations are offset from one column to the other.
175     *
176     * @param columnDelay a fraction of the animation duration
177     *
178     * @see #getColumnDelay()
179     * @see #getRowDelay()
180     * @see #setRowDelay(float)
181     */
182    public void setColumnDelay(float columnDelay) {
183        mColumnDelay = columnDelay;
184    }
185
186    /**
187     * Returns the delay by which the children's animation are offset from one
188     * row to the other. The delay is expressed as a fraction of the
189     * animation duration.
190     *
191     * @return a fraction of the animation duration
192     *
193     * @see #setRowDelay(float)
194     * @see #getColumnDelay()
195     * @see #setColumnDelay(float)
196     */
197    public float getRowDelay() {
198        return mRowDelay;
199    }
200
201    /**
202     * Sets the delay, as a fraction of the animation duration, by which the
203     * children's animations are offset from one row to the other.
204     *
205     * @param rowDelay a fraction of the animation duration
206     *
207     * @see #getRowDelay()
208     * @see #getColumnDelay()
209     * @see #setColumnDelay(float)
210     */
211    public void setRowDelay(float rowDelay) {
212        mRowDelay = rowDelay;
213    }
214
215    /**
216     * Returns the direction of the animation. {@link #DIRECTION_HORIZONTAL_MASK}
217     * and {@link #DIRECTION_VERTICAL_MASK} can be used to retrieve the
218     * horizontal and vertical components of the direction.
219     *
220     * @return the direction of the animation
221     *
222     * @see #setDirection(int)
223     * @see #DIRECTION_BOTTOM_TO_TOP
224     * @see #DIRECTION_TOP_TO_BOTTOM
225     * @see #DIRECTION_LEFT_TO_RIGHT
226     * @see #DIRECTION_RIGHT_TO_LEFT
227     * @see #DIRECTION_HORIZONTAL_MASK
228     * @see #DIRECTION_VERTICAL_MASK
229     */
230    public int getDirection() {
231        return mDirection;
232    }
233
234    /**
235     * Sets the direction of the animation. The direction is expressed as an
236     * integer containing a horizontal and vertical component. For instance,
237     * <code>DIRECTION_BOTTOM_TO_TOP | DIRECTION_RIGHT_TO_LEFT</code>.
238     *
239     * @param direction the direction of the animation
240     *
241     * @see #getDirection()
242     * @see #DIRECTION_BOTTOM_TO_TOP
243     * @see #DIRECTION_TOP_TO_BOTTOM
244     * @see #DIRECTION_LEFT_TO_RIGHT
245     * @see #DIRECTION_RIGHT_TO_LEFT
246     * @see #DIRECTION_HORIZONTAL_MASK
247     * @see #DIRECTION_VERTICAL_MASK
248     */
249    public void setDirection(int direction) {
250        mDirection = direction;
251    }
252
253    /**
254     * Returns the direction priority for the animation. The priority can
255     * be either {@link #PRIORITY_NONE}, {@link #PRIORITY_COLUMN} or
256     * {@link #PRIORITY_ROW}.
257     *
258     * @return the priority of the animation direction
259     *
260     * @see #setDirectionPriority(int)
261     * @see #PRIORITY_COLUMN
262     * @see #PRIORITY_NONE
263     * @see #PRIORITY_ROW
264     */
265    public int getDirectionPriority() {
266        return mDirectionPriority;
267    }
268
269    /**
270     * Specifies the direction priority of the animation. For instance,
271     * {@link #PRIORITY_COLUMN} will give priority to columns: the animation
272     * will first play on the column, then on the rows.Z
273     *
274     * @param directionPriority the direction priority of the animation
275     *
276     * @see #getDirectionPriority()
277     * @see #PRIORITY_COLUMN
278     * @see #PRIORITY_NONE
279     * @see #PRIORITY_ROW
280     */
281    public void setDirectionPriority(int directionPriority) {
282        mDirectionPriority = directionPriority;
283    }
284
285    /**
286     * {@inheritDoc}
287     */
288    @Override
289    public boolean willOverlap() {
290        return mColumnDelay < 1.0f || mRowDelay < 1.0f;
291    }
292
293    /**
294     * {@inheritDoc}
295     */
296    @Override
297    protected long getDelayForView(View view) {
298        ViewGroup.LayoutParams lp = view.getLayoutParams();
299        AnimationParameters params = (AnimationParameters) lp.layoutAnimationParameters;
300
301        if (params == null) {
302            return 0;
303        }
304
305        final int column = getTransformedColumnIndex(params);
306        final int row = getTransformedRowIndex(params);
307
308        final int rowsCount = params.rowsCount;
309        final int columnsCount = params.columnsCount;
310
311        final long duration = mAnimation.getDuration();
312        final float columnDelay = mColumnDelay * duration;
313        final float rowDelay = mRowDelay * duration;
314
315        float totalDelay;
316        long viewDelay;
317
318        if (mInterpolator == null) {
319            mInterpolator = new LinearInterpolator();
320        }
321
322        switch (mDirectionPriority) {
323            case PRIORITY_COLUMN:
324                viewDelay = (long) (row * rowDelay + column * rowsCount * rowDelay);
325                totalDelay = rowsCount * rowDelay + columnsCount * rowsCount * rowDelay;
326                break;
327            case PRIORITY_ROW:
328                viewDelay = (long) (column * columnDelay + row * columnsCount * columnDelay);
329                totalDelay = columnsCount * columnDelay + rowsCount * columnsCount * columnDelay;
330                break;
331            case PRIORITY_NONE:
332            default:
333                viewDelay = (long) (column * columnDelay + row * rowDelay);
334                totalDelay = columnsCount * columnDelay + rowsCount * rowDelay;
335                break;
336        }
337
338        float normalizedDelay = viewDelay / totalDelay;
339        normalizedDelay = mInterpolator.getInterpolation(normalizedDelay);
340
341        return (long) (normalizedDelay * totalDelay);
342    }
343
344    private int getTransformedColumnIndex(AnimationParameters params) {
345        int index;
346        switch (getOrder()) {
347            case ORDER_REVERSE:
348                index = params.columnsCount - 1 - params.column;
349                break;
350            case ORDER_RANDOM:
351                if (mRandomizer == null) {
352                    mRandomizer = new Random();
353                }
354                index = (int) (params.columnsCount * mRandomizer.nextFloat());
355                break;
356            case ORDER_NORMAL:
357            default:
358                index = params.column;
359                break;
360        }
361
362        int direction = mDirection & DIRECTION_HORIZONTAL_MASK;
363        if (direction == DIRECTION_RIGHT_TO_LEFT) {
364            index = params.columnsCount - 1 - index;
365        }
366
367        return index;
368    }
369
370    private int getTransformedRowIndex(AnimationParameters params) {
371        int index;
372        switch (getOrder()) {
373            case ORDER_REVERSE:
374                index = params.rowsCount - 1 - params.row;
375                break;
376            case ORDER_RANDOM:
377                if (mRandomizer == null) {
378                    mRandomizer = new Random();
379                }
380                index = (int) (params.rowsCount * mRandomizer.nextFloat());
381                break;
382            case ORDER_NORMAL:
383            default:
384                index = params.row;
385                break;
386        }
387
388        int direction = mDirection & DIRECTION_VERTICAL_MASK;
389        if (direction == DIRECTION_BOTTOM_TO_TOP) {
390            index = params.rowsCount - 1 - index;
391        }
392
393        return index;
394    }
395
396    /**
397     * The set of parameters that has to be attached to each view contained in
398     * the view group animated by the grid layout animation controller. These
399     * parameters are used to compute the start time of each individual view's
400     * animation.
401     */
402    public static class AnimationParameters extends
403            LayoutAnimationController.AnimationParameters {
404        /**
405         * The view group's column to which the view belongs.
406         */
407        public int column;
408
409        /**
410         * The view group's row to which the view belongs.
411         */
412        public int row;
413
414        /**
415         * The number of columns in the view's enclosing grid layout.
416         */
417        public int columnsCount;
418
419        /**
420         * The number of rows in the view's enclosing grid layout.
421         */
422        public int rowsCount;
423    }
424}
425