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 
17 package android.view.animation;
18 
19 import android.content.Context;
20 import android.content.res.TypedArray;
21 import android.util.AttributeSet;
22 import android.view.View;
23 import android.view.ViewGroup;
24 
25 import 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  */
52 public 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      */
GridLayoutAnimationController(Context context, AttributeSet attrs)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                 context);
121         mColumnDelay = d.value;
122         d = Animation.Description.parseValue(
123                 a.peekValue(com.android.internal.R.styleable.GridLayoutAnimation_rowDelay),
124                 context);
125         mRowDelay = d.value;
126         //noinspection PointlessBitwiseExpression
127         mDirection = a.getInt(com.android.internal.R.styleable.GridLayoutAnimation_direction,
128                 DIRECTION_LEFT_TO_RIGHT | DIRECTION_TOP_TO_BOTTOM);
129         mDirectionPriority = a.getInt(com.android.internal.R.styleable.GridLayoutAnimation_directionPriority,
130                 PRIORITY_NONE);
131 
132         a.recycle();
133     }
134 
135     /**
136      * Creates a new layout animation controller with a delay of 50%
137      * for both rows and columns and the specified animation.
138      *
139      * @param animation the animation to use on each child of the view group
140      */
GridLayoutAnimationController(Animation animation)141     public GridLayoutAnimationController(Animation animation) {
142         this(animation, 0.5f, 0.5f);
143     }
144 
145     /**
146      * Creates a new layout animation controller with the specified delays
147      * and the specified animation.
148      *
149      * @param animation the animation to use on each child of the view group
150      * @param columnDelay the delay by which each column animation must be offset
151      * @param rowDelay the delay by which each row animation must be offset
152      */
GridLayoutAnimationController(Animation animation, float columnDelay, float rowDelay)153     public GridLayoutAnimationController(Animation animation, float columnDelay, float rowDelay) {
154         super(animation);
155         mColumnDelay = columnDelay;
156         mRowDelay = rowDelay;
157     }
158 
159     /**
160      * Returns the delay by which the children's animation are offset from one
161      * column to the other. The delay is expressed as a fraction of the
162      * animation duration.
163      *
164      * @return a fraction of the animation duration
165      *
166      * @see #setColumnDelay(float)
167      * @see #getRowDelay()
168      * @see #setRowDelay(float)
169      */
getColumnDelay()170     public float getColumnDelay() {
171         return mColumnDelay;
172     }
173 
174     /**
175      * Sets the delay, as a fraction of the animation duration, by which the
176      * children's animations are offset from one column to the other.
177      *
178      * @param columnDelay a fraction of the animation duration
179      *
180      * @see #getColumnDelay()
181      * @see #getRowDelay()
182      * @see #setRowDelay(float)
183      */
setColumnDelay(float columnDelay)184     public void setColumnDelay(float columnDelay) {
185         mColumnDelay = columnDelay;
186     }
187 
188     /**
189      * Returns the delay by which the children's animation are offset from one
190      * row to the other. The delay is expressed as a fraction of the
191      * animation duration.
192      *
193      * @return a fraction of the animation duration
194      *
195      * @see #setRowDelay(float)
196      * @see #getColumnDelay()
197      * @see #setColumnDelay(float)
198      */
getRowDelay()199     public float getRowDelay() {
200         return mRowDelay;
201     }
202 
203     /**
204      * Sets the delay, as a fraction of the animation duration, by which the
205      * children's animations are offset from one row to the other.
206      *
207      * @param rowDelay a fraction of the animation duration
208      *
209      * @see #getRowDelay()
210      * @see #getColumnDelay()
211      * @see #setColumnDelay(float)
212      */
setRowDelay(float rowDelay)213     public void setRowDelay(float rowDelay) {
214         mRowDelay = rowDelay;
215     }
216 
217     /**
218      * Returns the direction of the animation. {@link #DIRECTION_HORIZONTAL_MASK}
219      * and {@link #DIRECTION_VERTICAL_MASK} can be used to retrieve the
220      * horizontal and vertical components of the direction.
221      *
222      * @return the direction of the animation
223      *
224      * @see #setDirection(int)
225      * @see #DIRECTION_BOTTOM_TO_TOP
226      * @see #DIRECTION_TOP_TO_BOTTOM
227      * @see #DIRECTION_LEFT_TO_RIGHT
228      * @see #DIRECTION_RIGHT_TO_LEFT
229      * @see #DIRECTION_HORIZONTAL_MASK
230      * @see #DIRECTION_VERTICAL_MASK
231      */
getDirection()232     public int getDirection() {
233         return mDirection;
234     }
235 
236     /**
237      * Sets the direction of the animation. The direction is expressed as an
238      * integer containing a horizontal and vertical component. For instance,
239      * <code>DIRECTION_BOTTOM_TO_TOP | DIRECTION_RIGHT_TO_LEFT</code>.
240      *
241      * @param direction the direction of the animation
242      *
243      * @see #getDirection()
244      * @see #DIRECTION_BOTTOM_TO_TOP
245      * @see #DIRECTION_TOP_TO_BOTTOM
246      * @see #DIRECTION_LEFT_TO_RIGHT
247      * @see #DIRECTION_RIGHT_TO_LEFT
248      * @see #DIRECTION_HORIZONTAL_MASK
249      * @see #DIRECTION_VERTICAL_MASK
250      */
setDirection(int direction)251     public void setDirection(int direction) {
252         mDirection = direction;
253     }
254 
255     /**
256      * Returns the direction priority for the animation. The priority can
257      * be either {@link #PRIORITY_NONE}, {@link #PRIORITY_COLUMN} or
258      * {@link #PRIORITY_ROW}.
259      *
260      * @return the priority of the animation direction
261      *
262      * @see #setDirectionPriority(int)
263      * @see #PRIORITY_COLUMN
264      * @see #PRIORITY_NONE
265      * @see #PRIORITY_ROW
266      */
getDirectionPriority()267     public int getDirectionPriority() {
268         return mDirectionPriority;
269     }
270 
271     /**
272      * Specifies the direction priority of the animation. For instance,
273      * {@link #PRIORITY_COLUMN} will give priority to columns: the animation
274      * will first play on the column, then on the rows.Z
275      *
276      * @param directionPriority the direction priority of the animation
277      *
278      * @see #getDirectionPriority()
279      * @see #PRIORITY_COLUMN
280      * @see #PRIORITY_NONE
281      * @see #PRIORITY_ROW
282      */
setDirectionPriority(int directionPriority)283     public void setDirectionPriority(int directionPriority) {
284         mDirectionPriority = directionPriority;
285     }
286 
287     /**
288      * {@inheritDoc}
289      */
290     @Override
willOverlap()291     public boolean willOverlap() {
292         return mColumnDelay < 1.0f || mRowDelay < 1.0f;
293     }
294 
295     /**
296      * {@inheritDoc}
297      */
298     @Override
getDelayForView(View view)299     protected long getDelayForView(View view) {
300         ViewGroup.LayoutParams lp = view.getLayoutParams();
301         AnimationParameters params = (AnimationParameters) lp.layoutAnimationParameters;
302 
303         if (params == null) {
304             return 0;
305         }
306 
307         final int column = getTransformedColumnIndex(params);
308         final int row = getTransformedRowIndex(params);
309 
310         final int rowsCount = params.rowsCount;
311         final int columnsCount = params.columnsCount;
312 
313         final long duration = mAnimation.getDuration();
314         final float columnDelay = mColumnDelay * duration;
315         final float rowDelay = mRowDelay * duration;
316 
317         float totalDelay;
318         long viewDelay;
319 
320         if (mInterpolator == null) {
321             mInterpolator = new LinearInterpolator();
322         }
323 
324         switch (mDirectionPriority) {
325             case PRIORITY_COLUMN:
326                 viewDelay = (long) (row * rowDelay + column * rowsCount * rowDelay);
327                 totalDelay = rowsCount * rowDelay + columnsCount * rowsCount * rowDelay;
328                 break;
329             case PRIORITY_ROW:
330                 viewDelay = (long) (column * columnDelay + row * columnsCount * columnDelay);
331                 totalDelay = columnsCount * columnDelay + rowsCount * columnsCount * columnDelay;
332                 break;
333             case PRIORITY_NONE:
334             default:
335                 viewDelay = (long) (column * columnDelay + row * rowDelay);
336                 totalDelay = columnsCount * columnDelay + rowsCount * rowDelay;
337                 break;
338         }
339 
340         float normalizedDelay = viewDelay / totalDelay;
341         normalizedDelay = mInterpolator.getInterpolation(normalizedDelay);
342 
343         return (long) (normalizedDelay * totalDelay);
344     }
345 
getTransformedColumnIndex(AnimationParameters params)346     private int getTransformedColumnIndex(AnimationParameters params) {
347         int index;
348         switch (getOrder()) {
349             case ORDER_REVERSE:
350                 index = params.columnsCount - 1 - params.column;
351                 break;
352             case ORDER_RANDOM:
353                 if (mRandomizer == null) {
354                     mRandomizer = new Random();
355                 }
356                 index = (int) (params.columnsCount * mRandomizer.nextFloat());
357                 break;
358             case ORDER_NORMAL:
359             default:
360                 index = params.column;
361                 break;
362         }
363 
364         int direction = mDirection & DIRECTION_HORIZONTAL_MASK;
365         if (direction == DIRECTION_RIGHT_TO_LEFT) {
366             index = params.columnsCount - 1 - index;
367         }
368 
369         return index;
370     }
371 
getTransformedRowIndex(AnimationParameters params)372     private int getTransformedRowIndex(AnimationParameters params) {
373         int index;
374         switch (getOrder()) {
375             case ORDER_REVERSE:
376                 index = params.rowsCount - 1 - params.row;
377                 break;
378             case ORDER_RANDOM:
379                 if (mRandomizer == null) {
380                     mRandomizer = new Random();
381                 }
382                 index = (int) (params.rowsCount * mRandomizer.nextFloat());
383                 break;
384             case ORDER_NORMAL:
385             default:
386                 index = params.row;
387                 break;
388         }
389 
390         int direction = mDirection & DIRECTION_VERTICAL_MASK;
391         if (direction == DIRECTION_BOTTOM_TO_TOP) {
392             index = params.rowsCount - 1 - index;
393         }
394 
395         return index;
396     }
397 
398     /**
399      * The set of parameters that has to be attached to each view contained in
400      * the view group animated by the grid layout animation controller. These
401      * parameters are used to compute the start time of each individual view's
402      * animation.
403      */
404     public static class AnimationParameters extends
405             LayoutAnimationController.AnimationParameters {
406         /**
407          * The view group's column to which the view belongs.
408          */
409         public int column;
410 
411         /**
412          * The view group's row to which the view belongs.
413          */
414         public int row;
415 
416         /**
417          * The number of columns in the view's enclosing grid layout.
418          */
419         public int columnsCount;
420 
421         /**
422          * The number of rows in the view's enclosing grid layout.
423          */
424         public int rowsCount;
425     }
426 }
427