1 /*
2  * Copyright (C) 2011 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.widget;
18 
19 import static android.view.Gravity.AXIS_PULL_AFTER;
20 import static android.view.Gravity.AXIS_PULL_BEFORE;
21 import static android.view.Gravity.AXIS_SPECIFIED;
22 import static android.view.Gravity.AXIS_X_SHIFT;
23 import static android.view.Gravity.AXIS_Y_SHIFT;
24 import static android.view.Gravity.HORIZONTAL_GRAVITY_MASK;
25 import static android.view.Gravity.RELATIVE_LAYOUT_DIRECTION;
26 import static android.view.Gravity.VERTICAL_GRAVITY_MASK;
27 import static android.view.View.MeasureSpec.EXACTLY;
28 import static android.view.View.MeasureSpec.makeMeasureSpec;
29 
30 import static java.lang.Math.max;
31 import static java.lang.Math.min;
32 
33 import android.annotation.IntDef;
34 import android.content.Context;
35 import android.content.res.TypedArray;
36 import android.graphics.Canvas;
37 import android.graphics.Color;
38 import android.graphics.Insets;
39 import android.graphics.Paint;
40 import android.util.AttributeSet;
41 import android.util.Log;
42 import android.util.LogPrinter;
43 import android.util.Pair;
44 import android.util.Printer;
45 import android.view.Gravity;
46 import android.view.View;
47 import android.view.ViewGroup;
48 import android.widget.RemoteViews.RemoteView;
49 
50 import com.android.internal.R;
51 
52 import java.lang.annotation.Retention;
53 import java.lang.annotation.RetentionPolicy;
54 import java.lang.reflect.Array;
55 import java.util.ArrayList;
56 import java.util.Arrays;
57 import java.util.HashMap;
58 import java.util.List;
59 import java.util.Map;
60 
61 /**
62  * A layout that places its children in a rectangular <em>grid</em>.
63  * <p>
64  * The grid is composed of a set of infinitely thin lines that separate the
65  * viewing area into <em>cells</em>. Throughout the API, grid lines are referenced
66  * by grid <em>indices</em>. A grid with {@code N} columns
67  * has {@code N + 1} grid indices that run from {@code 0}
68  * through {@code N} inclusive. Regardless of how GridLayout is
69  * configured, grid index {@code 0} is fixed to the leading edge of the
70  * container and grid index {@code N} is fixed to its trailing edge
71  * (after padding is taken into account).
72  *
73  * <h4>Row and Column Specs</h4>
74  *
75  * Children occupy one or more contiguous cells, as defined
76  * by their {@link GridLayout.LayoutParams#rowSpec rowSpec} and
77  * {@link GridLayout.LayoutParams#columnSpec columnSpec} layout parameters.
78  * Each spec defines the set of rows or columns that are to be
79  * occupied; and how children should be aligned within the resulting group of cells.
80  * Although cells do not normally overlap in a GridLayout, GridLayout does
81  * not prevent children being defined to occupy the same cell or group of cells.
82  * In this case however, there is no guarantee that children will not themselves
83  * overlap after the layout operation completes.
84  *
85  * <h4>Default Cell Assignment</h4>
86  *
87  * If a child does not specify the row and column indices of the cell it
88  * wishes to occupy, GridLayout assigns cell locations automatically using its:
89  * {@link GridLayout#setOrientation(int) orientation},
90  * {@link GridLayout#setRowCount(int) rowCount} and
91  * {@link GridLayout#setColumnCount(int) columnCount} properties.
92  *
93  * <h4>Space</h4>
94  *
95  * Space between children may be specified either by using instances of the
96  * dedicated {@link Space} view or by setting the
97  *
98  * {@link ViewGroup.MarginLayoutParams#leftMargin leftMargin},
99  * {@link ViewGroup.MarginLayoutParams#topMargin topMargin},
100  * {@link ViewGroup.MarginLayoutParams#rightMargin rightMargin} and
101  * {@link ViewGroup.MarginLayoutParams#bottomMargin bottomMargin}
102  *
103  * layout parameters. When the
104  * {@link GridLayout#setUseDefaultMargins(boolean) useDefaultMargins}
105  * property is set, default margins around children are automatically
106  * allocated based on the prevailing UI style guide for the platform.
107  * Each of the margins so defined may be independently overridden by an assignment
108  * to the appropriate layout parameter.
109  * Default values will generally produce a reasonable spacing between components
110  * but values may change between different releases of the platform.
111  *
112  * <h4>Excess Space Distribution</h4>
113  *
114  * As of API 21, GridLayout's distribution of excess space accomodates the principle of weight.
115  * In the event that no weights are specified, the previous conventions are respected and
116  * columns and rows are taken as flexible if their views specify some form of alignment
117  * within their groups.
118  * <p>
119  * The flexibility of a view is therefore influenced by its alignment which is,
120  * in turn, typically defined by setting the
121  * {@link LayoutParams#setGravity(int) gravity} property of the child's layout parameters.
122  * If either a weight or alignment were defined along a given axis then the component
123  * is taken as <em>flexible</em> in that direction. If no weight or alignment was set,
124  * the component is instead assumed to be <em>inflexible</em>.
125  * <p>
126  * Multiple components in the same row or column group are
127  * considered to act in <em>parallel</em>. Such a
128  * group is flexible only if <em>all</em> of the components
129  * within it are flexible. Row and column groups that sit either side of a common boundary
130  * are instead considered to act in <em>series</em>. The composite group made of these two
131  * elements is flexible if <em>one</em> of its elements is flexible.
132  * <p>
133  * To make a column stretch, make sure all of the components inside it define a
134  * weight or a gravity. To prevent a column from stretching, ensure that one of the components
135  * in the column does not define a weight or a gravity.
136  * <p>
137  * When the principle of flexibility does not provide complete disambiguation,
138  * GridLayout's algorithms favour rows and columns that are closer to its <em>right</em>
139  * and <em>bottom</em> edges. To be more precise, GridLayout treats each of its layout
140  * parameters as a constraint in the a set of variables that define the grid-lines along a
141  * given axis. During layout, GridLayout solves the constraints so as to return the unique
142  * solution to those constraints for which all variables are less-than-or-equal-to
143  * the corresponding value in any other valid solution.
144  *
145  * <h4>Interpretation of GONE</h4>
146  *
147  * For layout purposes, GridLayout treats views whose visibility status is
148  * {@link View#GONE GONE}, as having zero width and height. This is subtly different from
149  * the policy of ignoring views that are marked as GONE outright. If, for example, a gone-marked
150  * view was alone in a column, that column would itself collapse to zero width if and only if
151  * no gravity was defined on the view. If gravity was defined, then the gone-marked
152  * view has no effect on the layout and the container should be laid out as if the view
153  * had never been added to it. GONE views are taken to have zero weight during excess space
154  * distribution.
155  * <p>
156  * These statements apply equally to rows as well as columns, and to groups of rows or columns.
157  *
158  * <p>
159  * See {@link GridLayout.LayoutParams} for a full description of the
160  * layout parameters used by GridLayout.
161  *
162  * @attr ref android.R.styleable#GridLayout_orientation
163  * @attr ref android.R.styleable#GridLayout_rowCount
164  * @attr ref android.R.styleable#GridLayout_columnCount
165  * @attr ref android.R.styleable#GridLayout_useDefaultMargins
166  * @attr ref android.R.styleable#GridLayout_rowOrderPreserved
167  * @attr ref android.R.styleable#GridLayout_columnOrderPreserved
168  */
169 @RemoteView
170 public class GridLayout extends ViewGroup {
171 
172     // Public constants
173 
174     /** @hide */
175     @IntDef(prefix = { "HORIZONTAL", "VERTICAL" }, value = {
176             HORIZONTAL,
177             VERTICAL
178     })
179     @Retention(RetentionPolicy.SOURCE)
180     public @interface Orientation {}
181 
182     /**
183      * The horizontal orientation.
184      */
185     public static final int HORIZONTAL = LinearLayout.HORIZONTAL;
186 
187     /**
188      * The vertical orientation.
189      */
190     public static final int VERTICAL = LinearLayout.VERTICAL;
191 
192     /**
193      * The constant used to indicate that a value is undefined.
194      * Fields can use this value to indicate that their values
195      * have not yet been set. Similarly, methods can return this value
196      * to indicate that there is no suitable value that the implementation
197      * can return.
198      * The value used for the constant (currently {@link Integer#MIN_VALUE}) is
199      * intended to avoid confusion between valid values whose sign may not be known.
200      */
201     public static final int UNDEFINED = Integer.MIN_VALUE;
202 
203     /** @hide */
204     @IntDef(prefix = { "ALIGN_" }, value = {
205             ALIGN_BOUNDS,
206             ALIGN_MARGINS
207     })
208     @Retention(RetentionPolicy.SOURCE)
209     public @interface AlignmentMode {}
210 
211     /**
212      * This constant is an {@link #setAlignmentMode(int) alignmentMode}.
213      * When the {@code alignmentMode} is set to {@link #ALIGN_BOUNDS}, alignment
214      * is made between the edges of each component's raw
215      * view boundary: i.e. the area delimited by the component's:
216      * {@link android.view.View#getTop() top},
217      * {@link android.view.View#getLeft() left},
218      * {@link android.view.View#getBottom() bottom} and
219      * {@link android.view.View#getRight() right} properties.
220      * <p>
221      * For example, when {@code GridLayout} is in {@link #ALIGN_BOUNDS} mode,
222      * children that belong to a row group that uses {@link #TOP} alignment will
223      * all return the same value when their {@link android.view.View#getTop()}
224      * method is called.
225      *
226      * @see #setAlignmentMode(int)
227      */
228     public static final int ALIGN_BOUNDS = 0;
229 
230     /**
231      * This constant is an {@link #setAlignmentMode(int) alignmentMode}.
232      * When the {@code alignmentMode} is set to {@link #ALIGN_MARGINS},
233      * the bounds of each view are extended outwards, according
234      * to their margins, before the edges of the resulting rectangle are aligned.
235      * <p>
236      * For example, when {@code GridLayout} is in {@link #ALIGN_MARGINS} mode,
237      * the quantity {@code top - layoutParams.topMargin} is the same for all children that
238      * belong to a row group that uses {@link #TOP} alignment.
239      *
240      * @see #setAlignmentMode(int)
241      */
242     public static final int ALIGN_MARGINS = 1;
243 
244     // Misc constants
245 
246     static final int MAX_SIZE = 100000;
247     static final int DEFAULT_CONTAINER_MARGIN = 0;
248     static final int UNINITIALIZED_HASH = 0;
249     static final Printer LOG_PRINTER = new LogPrinter(Log.DEBUG, GridLayout.class.getName());
250     static final Printer NO_PRINTER = new Printer() {
251         @Override
252         public void println(String x) {
253         }
254     };
255 
256     // Defaults
257 
258     private static final int DEFAULT_ORIENTATION = HORIZONTAL;
259     private static final int DEFAULT_COUNT = UNDEFINED;
260     private static final boolean DEFAULT_USE_DEFAULT_MARGINS = false;
261     private static final boolean DEFAULT_ORDER_PRESERVED = true;
262     private static final int DEFAULT_ALIGNMENT_MODE = ALIGN_MARGINS;
263 
264     // TypedArray indices
265 
266     private static final int ORIENTATION = R.styleable.GridLayout_orientation;
267     private static final int ROW_COUNT = R.styleable.GridLayout_rowCount;
268     private static final int COLUMN_COUNT = R.styleable.GridLayout_columnCount;
269     private static final int USE_DEFAULT_MARGINS = R.styleable.GridLayout_useDefaultMargins;
270     private static final int ALIGNMENT_MODE = R.styleable.GridLayout_alignmentMode;
271     private static final int ROW_ORDER_PRESERVED = R.styleable.GridLayout_rowOrderPreserved;
272     private static final int COLUMN_ORDER_PRESERVED = R.styleable.GridLayout_columnOrderPreserved;
273 
274     // Instance variables
275 
276     final Axis mHorizontalAxis = new Axis(true);
277     final Axis mVerticalAxis = new Axis(false);
278     int mOrientation = DEFAULT_ORIENTATION;
279     boolean mUseDefaultMargins = DEFAULT_USE_DEFAULT_MARGINS;
280     int mAlignmentMode = DEFAULT_ALIGNMENT_MODE;
281     int mDefaultGap;
282     int mLastLayoutParamsHashCode = UNINITIALIZED_HASH;
283     Printer mPrinter = LOG_PRINTER;
284 
285     // Constructors
286 
GridLayout(Context context)287     public GridLayout(Context context) {
288         this(context, null);
289     }
290 
GridLayout(Context context, AttributeSet attrs)291     public GridLayout(Context context, AttributeSet attrs) {
292         this(context, attrs, 0);
293     }
294 
GridLayout(Context context, AttributeSet attrs, int defStyleAttr)295     public GridLayout(Context context, AttributeSet attrs, int defStyleAttr) {
296         this(context, attrs, defStyleAttr, 0);
297     }
298 
GridLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)299     public GridLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
300         super(context, attrs, defStyleAttr, defStyleRes);
301         mDefaultGap = context.getResources().getDimensionPixelOffset(R.dimen.default_gap);
302         final TypedArray a = context.obtainStyledAttributes(
303                 attrs, R.styleable.GridLayout, defStyleAttr, defStyleRes);
304         try {
305             setRowCount(a.getInt(ROW_COUNT, DEFAULT_COUNT));
306             setColumnCount(a.getInt(COLUMN_COUNT, DEFAULT_COUNT));
307             setOrientation(a.getInt(ORIENTATION, DEFAULT_ORIENTATION));
308             setUseDefaultMargins(a.getBoolean(USE_DEFAULT_MARGINS, DEFAULT_USE_DEFAULT_MARGINS));
309             setAlignmentMode(a.getInt(ALIGNMENT_MODE, DEFAULT_ALIGNMENT_MODE));
310             setRowOrderPreserved(a.getBoolean(ROW_ORDER_PRESERVED, DEFAULT_ORDER_PRESERVED));
311             setColumnOrderPreserved(a.getBoolean(COLUMN_ORDER_PRESERVED, DEFAULT_ORDER_PRESERVED));
312         } finally {
313             a.recycle();
314         }
315     }
316 
317     // Implementation
318 
319     /**
320      * Returns the current orientation.
321      *
322      * @return either {@link #HORIZONTAL} or {@link #VERTICAL}
323      *
324      * @see #setOrientation(int)
325      *
326      * @attr ref android.R.styleable#GridLayout_orientation
327      */
328     @Orientation
getOrientation()329     public int getOrientation() {
330         return mOrientation;
331     }
332 
333     /**
334      *
335      * GridLayout uses the orientation property for two purposes:
336      * <ul>
337      *  <li>
338      *      To control the 'direction' in which default row/column indices are generated
339      *      when they are not specified in a component's layout parameters.
340      *  </li>
341      *  <li>
342      *      To control which axis should be processed first during the layout operation:
343      *      when orientation is {@link #HORIZONTAL} the horizontal axis is laid out first.
344      *  </li>
345      * </ul>
346      *
347      * The order in which axes are laid out is important if, for example, the height of
348      * one of GridLayout's children is dependent on its width - and its width is, in turn,
349      * dependent on the widths of other components.
350      * <p>
351      * If your layout contains a {@link TextView} (or derivative:
352      * {@code Button}, {@code EditText}, {@code CheckBox}, etc.) which is
353      * in multi-line mode (the default) it is normally best to leave GridLayout's
354      * orientation as {@code HORIZONTAL} - because {@code TextView} is capable of
355      * deriving its height for a given width, but not the other way around.
356      * <p>
357      * Other than the effects above, orientation does not affect the actual layout operation of
358      * GridLayout, so it's fine to leave GridLayout in {@code HORIZONTAL} mode even if
359      * the height of the intended layout greatly exceeds its width.
360      * <p>
361      * The default value of this property is {@link #HORIZONTAL}.
362      *
363      * @param orientation either {@link #HORIZONTAL} or {@link #VERTICAL}
364      *
365      * @see #getOrientation()
366      *
367      * @attr ref android.R.styleable#GridLayout_orientation
368      */
setOrientation(@rientation int orientation)369     public void setOrientation(@Orientation int orientation) {
370         if (this.mOrientation != orientation) {
371             this.mOrientation = orientation;
372             invalidateStructure();
373             requestLayout();
374         }
375     }
376 
377     /**
378      * Returns the current number of rows. This is either the last value that was set
379      * with {@link #setRowCount(int)} or, if no such value was set, the maximum
380      * value of each the upper bounds defined in {@link LayoutParams#rowSpec}.
381      *
382      * @return the current number of rows
383      *
384      * @see #setRowCount(int)
385      * @see LayoutParams#rowSpec
386      *
387      * @attr ref android.R.styleable#GridLayout_rowCount
388      */
getRowCount()389     public int getRowCount() {
390         return mVerticalAxis.getCount();
391     }
392 
393     /**
394      * RowCount is used only to generate default row/column indices when
395      * they are not specified by a component's layout parameters.
396      *
397      * @param rowCount the number of rows
398      *
399      * @see #getRowCount()
400      * @see LayoutParams#rowSpec
401      *
402      * @attr ref android.R.styleable#GridLayout_rowCount
403      */
setRowCount(int rowCount)404     public void setRowCount(int rowCount) {
405         mVerticalAxis.setCount(rowCount);
406         invalidateStructure();
407         requestLayout();
408     }
409 
410     /**
411      * Returns the current number of columns. This is either the last value that was set
412      * with {@link #setColumnCount(int)} or, if no such value was set, the maximum
413      * value of each the upper bounds defined in {@link LayoutParams#columnSpec}.
414      *
415      * @return the current number of columns
416      *
417      * @see #setColumnCount(int)
418      * @see LayoutParams#columnSpec
419      *
420      * @attr ref android.R.styleable#GridLayout_columnCount
421      */
getColumnCount()422     public int getColumnCount() {
423         return mHorizontalAxis.getCount();
424     }
425 
426     /**
427      * ColumnCount is used only to generate default column/column indices when
428      * they are not specified by a component's layout parameters.
429      *
430      * @param columnCount the number of columns.
431      *
432      * @see #getColumnCount()
433      * @see LayoutParams#columnSpec
434      *
435      * @attr ref android.R.styleable#GridLayout_columnCount
436      */
setColumnCount(int columnCount)437     public void setColumnCount(int columnCount) {
438         mHorizontalAxis.setCount(columnCount);
439         invalidateStructure();
440         requestLayout();
441     }
442 
443     /**
444      * Returns whether or not this GridLayout will allocate default margins when no
445      * corresponding layout parameters are defined.
446      *
447      * @return {@code true} if default margins should be allocated
448      *
449      * @see #setUseDefaultMargins(boolean)
450      *
451      * @attr ref android.R.styleable#GridLayout_useDefaultMargins
452      */
getUseDefaultMargins()453     public boolean getUseDefaultMargins() {
454         return mUseDefaultMargins;
455     }
456 
457     /**
458      * When {@code true}, GridLayout allocates default margins around children
459      * based on the child's visual characteristics. Each of the
460      * margins so defined may be independently overridden by an assignment
461      * to the appropriate layout parameter.
462      * <p>
463      * When {@code false}, the default value of all margins is zero.
464      * <p>
465      * When setting to {@code true}, consider setting the value of the
466      * {@link #setAlignmentMode(int) alignmentMode}
467      * property to {@link #ALIGN_BOUNDS}.
468      * <p>
469      * The default value of this property is {@code false}.
470      *
471      * @param useDefaultMargins use {@code true} to make GridLayout allocate default margins
472      *
473      * @see #getUseDefaultMargins()
474      * @see #setAlignmentMode(int)
475      *
476      * @see MarginLayoutParams#leftMargin
477      * @see MarginLayoutParams#topMargin
478      * @see MarginLayoutParams#rightMargin
479      * @see MarginLayoutParams#bottomMargin
480      *
481      * @attr ref android.R.styleable#GridLayout_useDefaultMargins
482      */
setUseDefaultMargins(boolean useDefaultMargins)483     public void setUseDefaultMargins(boolean useDefaultMargins) {
484         this.mUseDefaultMargins = useDefaultMargins;
485         requestLayout();
486     }
487 
488     /**
489      * Returns the alignment mode.
490      *
491      * @return the alignment mode; either {@link #ALIGN_BOUNDS} or {@link #ALIGN_MARGINS}
492      *
493      * @see #ALIGN_BOUNDS
494      * @see #ALIGN_MARGINS
495      *
496      * @see #setAlignmentMode(int)
497      *
498      * @attr ref android.R.styleable#GridLayout_alignmentMode
499      */
500     @AlignmentMode
getAlignmentMode()501     public int getAlignmentMode() {
502         return mAlignmentMode;
503     }
504 
505     /**
506      * Sets the alignment mode to be used for all of the alignments between the
507      * children of this container.
508      * <p>
509      * The default value of this property is {@link #ALIGN_MARGINS}.
510      *
511      * @param alignmentMode either {@link #ALIGN_BOUNDS} or {@link #ALIGN_MARGINS}
512      *
513      * @see #ALIGN_BOUNDS
514      * @see #ALIGN_MARGINS
515      *
516      * @see #getAlignmentMode()
517      *
518      * @attr ref android.R.styleable#GridLayout_alignmentMode
519      */
setAlignmentMode(@lignmentMode int alignmentMode)520     public void setAlignmentMode(@AlignmentMode int alignmentMode) {
521         this.mAlignmentMode = alignmentMode;
522         requestLayout();
523     }
524 
525     /**
526      * Returns whether or not row boundaries are ordered by their grid indices.
527      *
528      * @return {@code true} if row boundaries must appear in the order of their indices,
529      *         {@code false} otherwise
530      *
531      * @see #setRowOrderPreserved(boolean)
532      *
533      * @attr ref android.R.styleable#GridLayout_rowOrderPreserved
534      */
isRowOrderPreserved()535     public boolean isRowOrderPreserved() {
536         return mVerticalAxis.isOrderPreserved();
537     }
538 
539     /**
540      * When this property is {@code true}, GridLayout is forced to place the row boundaries
541      * so that their associated grid indices are in ascending order in the view.
542      * <p>
543      * When this property is {@code false} GridLayout is at liberty to place the vertical row
544      * boundaries in whatever order best fits the given constraints.
545      * <p>
546      * The default value of this property is {@code true}.
547 
548      * @param rowOrderPreserved {@code true} to force GridLayout to respect the order
549      *        of row boundaries
550      *
551      * @see #isRowOrderPreserved()
552      *
553      * @attr ref android.R.styleable#GridLayout_rowOrderPreserved
554      */
setRowOrderPreserved(boolean rowOrderPreserved)555     public void setRowOrderPreserved(boolean rowOrderPreserved) {
556         mVerticalAxis.setOrderPreserved(rowOrderPreserved);
557         invalidateStructure();
558         requestLayout();
559     }
560 
561     /**
562      * Returns whether or not column boundaries are ordered by their grid indices.
563      *
564      * @return {@code true} if column boundaries must appear in the order of their indices,
565      *         {@code false} otherwise
566      *
567      * @see #setColumnOrderPreserved(boolean)
568      *
569      * @attr ref android.R.styleable#GridLayout_columnOrderPreserved
570      */
isColumnOrderPreserved()571     public boolean isColumnOrderPreserved() {
572         return mHorizontalAxis.isOrderPreserved();
573     }
574 
575     /**
576      * When this property is {@code true}, GridLayout is forced to place the column boundaries
577      * so that their associated grid indices are in ascending order in the view.
578      * <p>
579      * When this property is {@code false} GridLayout is at liberty to place the horizontal column
580      * boundaries in whatever order best fits the given constraints.
581      * <p>
582      * The default value of this property is {@code true}.
583      *
584      * @param columnOrderPreserved use {@code true} to force GridLayout to respect the order
585      *        of column boundaries.
586      *
587      * @see #isColumnOrderPreserved()
588      *
589      * @attr ref android.R.styleable#GridLayout_columnOrderPreserved
590      */
setColumnOrderPreserved(boolean columnOrderPreserved)591     public void setColumnOrderPreserved(boolean columnOrderPreserved) {
592         mHorizontalAxis.setOrderPreserved(columnOrderPreserved);
593         invalidateStructure();
594         requestLayout();
595     }
596 
597     /**
598      * Return the printer that will log diagnostics from this layout.
599      *
600      * @see #setPrinter(android.util.Printer)
601      *
602      * @return the printer associated with this view
603      *
604      * @hide
605      */
getPrinter()606     public Printer getPrinter() {
607         return mPrinter;
608     }
609 
610     /**
611      * Set the printer that will log diagnostics from this layout.
612      * The default value is created by {@link android.util.LogPrinter}.
613      *
614      * @param printer the printer associated with this layout
615      *
616      * @see #getPrinter()
617      *
618      * @hide
619      */
setPrinter(Printer printer)620     public void setPrinter(Printer printer) {
621         this.mPrinter = (printer == null) ? NO_PRINTER : printer;
622     }
623 
624     // Static utility methods
625 
max2(int[] a, int valueIfEmpty)626     static int max2(int[] a, int valueIfEmpty) {
627         int result = valueIfEmpty;
628         for (int i = 0, N = a.length; i < N; i++) {
629             result = Math.max(result, a[i]);
630         }
631         return result;
632     }
633 
634     @SuppressWarnings("unchecked")
append(T[] a, T[] b)635     static <T> T[] append(T[] a, T[] b) {
636         T[] result = (T[]) Array.newInstance(a.getClass().getComponentType(), a.length + b.length);
637         System.arraycopy(a, 0, result, 0, a.length);
638         System.arraycopy(b, 0, result, a.length, b.length);
639         return result;
640     }
641 
getAlignment(int gravity, boolean horizontal)642     static Alignment getAlignment(int gravity, boolean horizontal) {
643         int mask = horizontal ? HORIZONTAL_GRAVITY_MASK : VERTICAL_GRAVITY_MASK;
644         int shift = horizontal ? AXIS_X_SHIFT : AXIS_Y_SHIFT;
645         int flags = (gravity & mask) >> shift;
646         switch (flags) {
647             case (AXIS_SPECIFIED | AXIS_PULL_BEFORE):
648                 return horizontal ? LEFT : TOP;
649             case (AXIS_SPECIFIED | AXIS_PULL_AFTER):
650                 return horizontal ? RIGHT : BOTTOM;
651             case (AXIS_SPECIFIED | AXIS_PULL_BEFORE | AXIS_PULL_AFTER):
652                 return FILL;
653             case AXIS_SPECIFIED:
654                 return CENTER;
655             case (AXIS_SPECIFIED | AXIS_PULL_BEFORE | RELATIVE_LAYOUT_DIRECTION):
656                 return START;
657             case (AXIS_SPECIFIED | AXIS_PULL_AFTER | RELATIVE_LAYOUT_DIRECTION):
658                 return END;
659             default:
660                 return UNDEFINED_ALIGNMENT;
661         }
662     }
663 
664     /** @noinspection UnusedParameters*/
getDefaultMargin(View c, boolean horizontal, boolean leading)665     private int getDefaultMargin(View c, boolean horizontal, boolean leading) {
666         if (c.getClass() == Space.class) {
667             return 0;
668         }
669         return mDefaultGap / 2;
670     }
671 
getDefaultMargin(View c, boolean isAtEdge, boolean horizontal, boolean leading)672     private int getDefaultMargin(View c, boolean isAtEdge, boolean horizontal, boolean leading) {
673         return /*isAtEdge ? DEFAULT_CONTAINER_MARGIN :*/ getDefaultMargin(c, horizontal, leading);
674     }
675 
getDefaultMargin(View c, LayoutParams p, boolean horizontal, boolean leading)676     private int getDefaultMargin(View c, LayoutParams p, boolean horizontal, boolean leading) {
677         if (!mUseDefaultMargins) {
678             return 0;
679         }
680         Spec spec = horizontal ? p.columnSpec : p.rowSpec;
681         Axis axis = horizontal ? mHorizontalAxis : mVerticalAxis;
682         Interval span = spec.span;
683         boolean leading1 = (horizontal && isLayoutRtl()) ? !leading : leading;
684         boolean isAtEdge = leading1 ? (span.min == 0) : (span.max == axis.getCount());
685 
686         return getDefaultMargin(c, isAtEdge, horizontal, leading);
687     }
688 
getMargin1(View view, boolean horizontal, boolean leading)689     int getMargin1(View view, boolean horizontal, boolean leading) {
690         LayoutParams lp = getLayoutParams(view);
691         int margin = horizontal ?
692                 (leading ? lp.leftMargin : lp.rightMargin) :
693                 (leading ? lp.topMargin : lp.bottomMargin);
694         return margin == UNDEFINED ? getDefaultMargin(view, lp, horizontal, leading) : margin;
695     }
696 
getMargin(View view, boolean horizontal, boolean leading)697     private int getMargin(View view, boolean horizontal, boolean leading) {
698         if (mAlignmentMode == ALIGN_MARGINS) {
699             return getMargin1(view, horizontal, leading);
700         } else {
701             Axis axis = horizontal ? mHorizontalAxis : mVerticalAxis;
702             int[] margins = leading ? axis.getLeadingMargins() : axis.getTrailingMargins();
703             LayoutParams lp = getLayoutParams(view);
704             Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
705             int index = leading ? spec.span.min : spec.span.max;
706             return margins[index];
707         }
708     }
709 
getTotalMargin(View child, boolean horizontal)710     private int getTotalMargin(View child, boolean horizontal) {
711         return getMargin(child, horizontal, true) + getMargin(child, horizontal, false);
712     }
713 
fits(int[] a, int value, int start, int end)714     private static boolean fits(int[] a, int value, int start, int end) {
715         if (end > a.length) {
716             return false;
717         }
718         for (int i = start; i < end; i++) {
719             if (a[i] > value) {
720                 return false;
721             }
722         }
723         return true;
724     }
725 
procrusteanFill(int[] a, int start, int end, int value)726     private static void procrusteanFill(int[] a, int start, int end, int value) {
727         int length = a.length;
728         Arrays.fill(a, Math.min(start, length), Math.min(end, length), value);
729     }
730 
setCellGroup(LayoutParams lp, int row, int rowSpan, int col, int colSpan)731     private static void setCellGroup(LayoutParams lp, int row, int rowSpan, int col, int colSpan) {
732         lp.setRowSpecSpan(new Interval(row, row + rowSpan));
733         lp.setColumnSpecSpan(new Interval(col, col + colSpan));
734     }
735 
736     // Logic to avert infinite loops by ensuring that the cells can be placed somewhere.
clip(Interval minorRange, boolean minorWasDefined, int count)737     private static int clip(Interval minorRange, boolean minorWasDefined, int count) {
738         int size = minorRange.size();
739         if (count == 0) {
740             return size;
741         }
742         int min = minorWasDefined ? min(minorRange.min, count) : 0;
743         return min(size, count - min);
744     }
745 
746     // install default indices for cells that don't define them
validateLayoutParams()747     private void validateLayoutParams() {
748         final boolean horizontal = (mOrientation == HORIZONTAL);
749         final Axis axis = horizontal ? mHorizontalAxis : mVerticalAxis;
750         final int count = (axis.definedCount != UNDEFINED) ? axis.definedCount : 0;
751 
752         int major = 0;
753         int minor = 0;
754         int[] maxSizes = new int[count];
755 
756         for (int i = 0, N = getChildCount(); i < N; i++) {
757             LayoutParams lp = (LayoutParams) getChildAt(i).getLayoutParams();
758 
759             final Spec majorSpec = horizontal ? lp.rowSpec : lp.columnSpec;
760             final Interval majorRange = majorSpec.span;
761             final boolean majorWasDefined = majorSpec.startDefined;
762             final int majorSpan = majorRange.size();
763             if (majorWasDefined) {
764                 major = majorRange.min;
765             }
766 
767             final Spec minorSpec = horizontal ? lp.columnSpec : lp.rowSpec;
768             final Interval minorRange = minorSpec.span;
769             final boolean minorWasDefined = minorSpec.startDefined;
770             final int minorSpan = clip(minorRange, minorWasDefined, count);
771             if (minorWasDefined) {
772                 minor = minorRange.min;
773             }
774 
775             if (count != 0) {
776                 // Find suitable row/col values when at least one is undefined.
777                 if (!majorWasDefined || !minorWasDefined) {
778                     while (!fits(maxSizes, major, minor, minor + minorSpan)) {
779                         if (minorWasDefined) {
780                             major++;
781                         } else {
782                             if (minor + minorSpan <= count) {
783                                 minor++;
784                             } else {
785                                 minor = 0;
786                                 major++;
787                             }
788                         }
789                     }
790                 }
791                 procrusteanFill(maxSizes, minor, minor + minorSpan, major + majorSpan);
792             }
793 
794             if (horizontal) {
795                 setCellGroup(lp, major, majorSpan, minor, minorSpan);
796             } else {
797                 setCellGroup(lp, minor, minorSpan, major, majorSpan);
798             }
799 
800             minor = minor + minorSpan;
801         }
802     }
803 
invalidateStructure()804     private void invalidateStructure() {
805         mLastLayoutParamsHashCode = UNINITIALIZED_HASH;
806         mHorizontalAxis.invalidateStructure();
807         mVerticalAxis.invalidateStructure();
808         // This can end up being done twice. Better twice than not at all.
809         invalidateValues();
810     }
811 
invalidateValues()812     private void invalidateValues() {
813         // Need null check because requestLayout() is called in View's initializer,
814         // before we are set up.
815         if (mHorizontalAxis != null && mVerticalAxis != null) {
816             mHorizontalAxis.invalidateValues();
817             mVerticalAxis.invalidateValues();
818         }
819     }
820 
821     /** @hide */
822     @Override
onSetLayoutParams(View child, ViewGroup.LayoutParams layoutParams)823     protected void onSetLayoutParams(View child, ViewGroup.LayoutParams layoutParams) {
824         super.onSetLayoutParams(child, layoutParams);
825 
826         if (!checkLayoutParams(layoutParams)) {
827             handleInvalidParams("supplied LayoutParams are of the wrong type");
828         }
829 
830         invalidateStructure();
831     }
832 
getLayoutParams(View c)833     final LayoutParams getLayoutParams(View c) {
834         return (LayoutParams) c.getLayoutParams();
835     }
836 
handleInvalidParams(String msg)837     private static void handleInvalidParams(String msg) {
838         throw new IllegalArgumentException(msg + ". ");
839     }
840 
checkLayoutParams(LayoutParams lp, boolean horizontal)841     private void checkLayoutParams(LayoutParams lp, boolean horizontal) {
842         String groupName = horizontal ? "column" : "row";
843         Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
844         Interval span = spec.span;
845         if (span.min != UNDEFINED && span.min < 0) {
846             handleInvalidParams(groupName + " indices must be positive");
847         }
848         Axis axis = horizontal ? mHorizontalAxis : mVerticalAxis;
849         int count = axis.definedCount;
850         if (count != UNDEFINED) {
851             if (span.max > count) {
852                 handleInvalidParams(groupName +
853                         " indices (start + span) mustn't exceed the " + groupName + " count");
854             }
855             if (span.size() > count) {
856                 handleInvalidParams(groupName + " span mustn't exceed the " + groupName + " count");
857             }
858         }
859     }
860 
861     @Override
checkLayoutParams(ViewGroup.LayoutParams p)862     protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
863         if (!(p instanceof LayoutParams)) {
864             return false;
865         }
866         LayoutParams lp = (LayoutParams) p;
867 
868         checkLayoutParams(lp, true);
869         checkLayoutParams(lp, false);
870 
871         return true;
872     }
873 
874     @Override
generateDefaultLayoutParams()875     protected LayoutParams generateDefaultLayoutParams() {
876         return new LayoutParams();
877     }
878 
879     @Override
generateLayoutParams(AttributeSet attrs)880     public LayoutParams generateLayoutParams(AttributeSet attrs) {
881         return new LayoutParams(getContext(), attrs);
882     }
883 
884     @Override
generateLayoutParams(ViewGroup.LayoutParams lp)885     protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
886         if (sPreserveMarginParamsInLayoutParamConversion) {
887             if (lp instanceof LayoutParams) {
888                 return new LayoutParams((LayoutParams) lp);
889             } else if (lp instanceof MarginLayoutParams) {
890                 return new LayoutParams((MarginLayoutParams) lp);
891             }
892         }
893         return new LayoutParams(lp);
894     }
895 
896     // Draw grid
897 
drawLine(Canvas graphics, int x1, int y1, int x2, int y2, Paint paint)898     private void drawLine(Canvas graphics, int x1, int y1, int x2, int y2, Paint paint) {
899         if (isLayoutRtl()) {
900             int width = getWidth();
901             graphics.drawLine(width - x1, y1, width - x2, y2, paint);
902         } else {
903             graphics.drawLine(x1, y1, x2, y2, paint);
904         }
905     }
906 
907     /**
908      * @hide
909      */
910     @Override
onDebugDrawMargins(Canvas canvas, Paint paint)911     protected void onDebugDrawMargins(Canvas canvas, Paint paint) {
912         // Apply defaults, so as to remove UNDEFINED values
913         LayoutParams lp = new LayoutParams();
914         for (int i = 0; i < getChildCount(); i++) {
915             View c = getChildAt(i);
916             lp.setMargins(
917                     getMargin1(c, true, true),
918                     getMargin1(c, false, true),
919                     getMargin1(c, true, false),
920                     getMargin1(c, false, false));
921             lp.onDebugDraw(c, canvas, paint);
922         }
923     }
924 
925     /**
926      * @hide
927      */
928     @Override
onDebugDraw(Canvas canvas)929     protected void onDebugDraw(Canvas canvas) {
930         Paint paint = new Paint();
931         paint.setStyle(Paint.Style.STROKE);
932         paint.setColor(Color.argb(50, 255, 255, 255));
933 
934         Insets insets = getOpticalInsets();
935 
936         int top    =               getPaddingTop()    + insets.top;
937         int left   =               getPaddingLeft()   + insets.left;
938         int right  = getWidth()  - getPaddingRight()  - insets.right;
939         int bottom = getHeight() - getPaddingBottom() - insets.bottom;
940 
941         int[] xs = mHorizontalAxis.locations;
942         if (xs != null) {
943             for (int i = 0, length = xs.length; i < length; i++) {
944                 int x = left + xs[i];
945                 drawLine(canvas, x, top, x, bottom, paint);
946             }
947         }
948 
949         int[] ys = mVerticalAxis.locations;
950         if (ys != null) {
951             for (int i = 0, length = ys.length; i < length; i++) {
952                 int y = top + ys[i];
953                 drawLine(canvas, left, y, right, y, paint);
954             }
955         }
956 
957         super.onDebugDraw(canvas);
958     }
959 
960     @Override
onViewAdded(View child)961     public void onViewAdded(View child) {
962         super.onViewAdded(child);
963         invalidateStructure();
964     }
965 
966     @Override
onViewRemoved(View child)967     public void onViewRemoved(View child) {
968         super.onViewRemoved(child);
969         invalidateStructure();
970     }
971 
972     /**
973      * We need to call invalidateStructure() when a child's GONE flag changes state.
974      * This implementation is a catch-all, invalidating on any change in the visibility flags.
975      *
976      * @hide
977      */
978     @Override
onChildVisibilityChanged(View child, int oldVisibility, int newVisibility)979     protected void onChildVisibilityChanged(View child, int oldVisibility, int newVisibility) {
980         super.onChildVisibilityChanged(child, oldVisibility, newVisibility);
981         if (oldVisibility == GONE || newVisibility == GONE) {
982         invalidateStructure();
983         }
984     }
985 
computeLayoutParamsHashCode()986     private int computeLayoutParamsHashCode() {
987         int result = 1;
988         for (int i = 0, N = getChildCount(); i < N; i++) {
989             View c = getChildAt(i);
990             if (c.getVisibility() == View.GONE) continue;
991             LayoutParams lp = (LayoutParams) c.getLayoutParams();
992             result = 31 * result + lp.hashCode();
993         }
994         return result;
995     }
996 
consistencyCheck()997     private void consistencyCheck() {
998         if (mLastLayoutParamsHashCode == UNINITIALIZED_HASH) {
999             validateLayoutParams();
1000             mLastLayoutParamsHashCode = computeLayoutParamsHashCode();
1001         } else if (mLastLayoutParamsHashCode != computeLayoutParamsHashCode()) {
1002             mPrinter.println("The fields of some layout parameters were modified in between "
1003                     + "layout operations. Check the javadoc for GridLayout.LayoutParams#rowSpec.");
1004             invalidateStructure();
1005             consistencyCheck();
1006         }
1007     }
1008 
1009     // Measurement
1010 
1011     // Note: padding has already been removed from the supplied specs
measureChildWithMargins2(View child, int parentWidthSpec, int parentHeightSpec, int childWidth, int childHeight)1012     private void measureChildWithMargins2(View child, int parentWidthSpec, int parentHeightSpec,
1013             int childWidth, int childHeight) {
1014         int childWidthSpec = getChildMeasureSpec(parentWidthSpec,
1015                 getTotalMargin(child, true), childWidth);
1016         int childHeightSpec = getChildMeasureSpec(parentHeightSpec,
1017                 getTotalMargin(child, false), childHeight);
1018         child.measure(childWidthSpec, childHeightSpec);
1019     }
1020 
1021     // Note: padding has already been removed from the supplied specs
measureChildrenWithMargins(int widthSpec, int heightSpec, boolean firstPass)1022     private void measureChildrenWithMargins(int widthSpec, int heightSpec, boolean firstPass) {
1023         for (int i = 0, N = getChildCount(); i < N; i++) {
1024             View c = getChildAt(i);
1025             if (c.getVisibility() == View.GONE) continue;
1026             LayoutParams lp = getLayoutParams(c);
1027             if (firstPass) {
1028                 measureChildWithMargins2(c, widthSpec, heightSpec, lp.width, lp.height);
1029             } else {
1030                 boolean horizontal = (mOrientation == HORIZONTAL);
1031                 Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
1032                 if (spec.getAbsoluteAlignment(horizontal) == FILL) {
1033                     Interval span = spec.span;
1034                     Axis axis = horizontal ? mHorizontalAxis : mVerticalAxis;
1035                     int[] locations = axis.getLocations();
1036                     int cellSize = locations[span.max] - locations[span.min];
1037                     int viewSize = cellSize - getTotalMargin(c, horizontal);
1038                     if (horizontal) {
1039                         measureChildWithMargins2(c, widthSpec, heightSpec, viewSize, lp.height);
1040                     } else {
1041                         measureChildWithMargins2(c, widthSpec, heightSpec, lp.width, viewSize);
1042                     }
1043                 }
1044             }
1045         }
1046     }
1047 
adjust(int measureSpec, int delta)1048     static int adjust(int measureSpec, int delta) {
1049         return makeMeasureSpec(
1050                 MeasureSpec.getSize(measureSpec + delta),  MeasureSpec.getMode(measureSpec));
1051     }
1052 
1053     @Override
onMeasure(int widthSpec, int heightSpec)1054     protected void onMeasure(int widthSpec, int heightSpec) {
1055         consistencyCheck();
1056 
1057         /** If we have been called by {@link View#measure(int, int)}, one of width or height
1058          *  is  likely to have changed. We must invalidate if so. */
1059         invalidateValues();
1060 
1061         int hPadding = getPaddingLeft() + getPaddingRight();
1062         int vPadding = getPaddingTop()  + getPaddingBottom();
1063 
1064         int widthSpecSansPadding =  adjust( widthSpec, -hPadding);
1065         int heightSpecSansPadding = adjust(heightSpec, -vPadding);
1066 
1067         measureChildrenWithMargins(widthSpecSansPadding, heightSpecSansPadding, true);
1068 
1069         int widthSansPadding;
1070         int heightSansPadding;
1071 
1072         // Use the orientation property to decide which axis should be laid out first.
1073         if (mOrientation == HORIZONTAL) {
1074             widthSansPadding = mHorizontalAxis.getMeasure(widthSpecSansPadding);
1075             measureChildrenWithMargins(widthSpecSansPadding, heightSpecSansPadding, false);
1076             heightSansPadding = mVerticalAxis.getMeasure(heightSpecSansPadding);
1077         } else {
1078             heightSansPadding = mVerticalAxis.getMeasure(heightSpecSansPadding);
1079             measureChildrenWithMargins(widthSpecSansPadding, heightSpecSansPadding, false);
1080             widthSansPadding = mHorizontalAxis.getMeasure(widthSpecSansPadding);
1081         }
1082 
1083         int measuredWidth  = Math.max(widthSansPadding  + hPadding, getSuggestedMinimumWidth());
1084         int measuredHeight = Math.max(heightSansPadding + vPadding, getSuggestedMinimumHeight());
1085 
1086         setMeasuredDimension(
1087                 resolveSizeAndState(measuredWidth,   widthSpec, 0),
1088                 resolveSizeAndState(measuredHeight, heightSpec, 0));
1089     }
1090 
getMeasurement(View c, boolean horizontal)1091     private int getMeasurement(View c, boolean horizontal) {
1092         return horizontal ? c.getMeasuredWidth() : c.getMeasuredHeight();
1093     }
1094 
getMeasurementIncludingMargin(View c, boolean horizontal)1095     final int getMeasurementIncludingMargin(View c, boolean horizontal) {
1096         if (c.getVisibility() == View.GONE) {
1097             return 0;
1098         }
1099         return getMeasurement(c, horizontal) + getTotalMargin(c, horizontal);
1100     }
1101 
1102     @Override
requestLayout()1103     public void requestLayout() {
1104         super.requestLayout();
1105         invalidateValues();
1106     }
1107 
1108     // Layout container
1109 
1110     /**
1111      * {@inheritDoc}
1112      */
1113     /*
1114      The layout operation is implemented by delegating the heavy lifting to the
1115      to the mHorizontalAxis and mVerticalAxis instances of the internal Axis class.
1116      Together they compute the locations of the vertical and horizontal lines of
1117      the grid (respectively!).
1118 
1119      This method is then left with the simpler task of applying margins, gravity
1120      and sizing to each child view and then placing it in its cell.
1121      */
1122     @Override
onLayout(boolean changed, int left, int top, int right, int bottom)1123     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
1124         consistencyCheck();
1125 
1126         int targetWidth = right - left;
1127         int targetHeight = bottom - top;
1128 
1129         int paddingLeft = getPaddingLeft();
1130         int paddingTop = getPaddingTop();
1131         int paddingRight = getPaddingRight();
1132         int paddingBottom = getPaddingBottom();
1133 
1134         mHorizontalAxis.layout(targetWidth - paddingLeft - paddingRight);
1135         mVerticalAxis.layout(targetHeight - paddingTop - paddingBottom);
1136 
1137         int[] hLocations = mHorizontalAxis.getLocations();
1138         int[] vLocations = mVerticalAxis.getLocations();
1139 
1140         for (int i = 0, N = getChildCount(); i < N; i++) {
1141             View c = getChildAt(i);
1142             if (c.getVisibility() == View.GONE) continue;
1143             LayoutParams lp = getLayoutParams(c);
1144             Spec columnSpec = lp.columnSpec;
1145             Spec rowSpec = lp.rowSpec;
1146 
1147             Interval colSpan = columnSpec.span;
1148             Interval rowSpan = rowSpec.span;
1149 
1150             int x1 = hLocations[colSpan.min];
1151             int y1 = vLocations[rowSpan.min];
1152 
1153             int x2 = hLocations[colSpan.max];
1154             int y2 = vLocations[rowSpan.max];
1155 
1156             int cellWidth = x2 - x1;
1157             int cellHeight = y2 - y1;
1158 
1159             int pWidth = getMeasurement(c, true);
1160             int pHeight = getMeasurement(c, false);
1161 
1162             Alignment hAlign = columnSpec.getAbsoluteAlignment(true);
1163             Alignment vAlign = rowSpec.getAbsoluteAlignment(false);
1164 
1165             Bounds boundsX = mHorizontalAxis.getGroupBounds().getValue(i);
1166             Bounds boundsY = mVerticalAxis.getGroupBounds().getValue(i);
1167 
1168             // Gravity offsets: the location of the alignment group relative to its cell group.
1169             int gravityOffsetX = hAlign.getGravityOffset(c, cellWidth - boundsX.size(true));
1170             int gravityOffsetY = vAlign.getGravityOffset(c, cellHeight - boundsY.size(true));
1171 
1172             int leftMargin = getMargin(c, true, true);
1173             int topMargin = getMargin(c, false, true);
1174             int rightMargin = getMargin(c, true, false);
1175             int bottomMargin = getMargin(c, false, false);
1176 
1177             int sumMarginsX = leftMargin + rightMargin;
1178             int sumMarginsY = topMargin + bottomMargin;
1179 
1180             // Alignment offsets: the location of the view relative to its alignment group.
1181             int alignmentOffsetX = boundsX.getOffset(this, c, hAlign, pWidth + sumMarginsX, true);
1182             int alignmentOffsetY = boundsY.getOffset(this, c, vAlign, pHeight + sumMarginsY, false);
1183 
1184             int width = hAlign.getSizeInCell(c, pWidth, cellWidth - sumMarginsX);
1185             int height = vAlign.getSizeInCell(c, pHeight, cellHeight - sumMarginsY);
1186 
1187             int dx = x1 + gravityOffsetX + alignmentOffsetX;
1188 
1189             int cx = !isLayoutRtl() ? paddingLeft + leftMargin + dx :
1190                     targetWidth - width - paddingRight - rightMargin - dx;
1191             int cy = paddingTop + y1 + gravityOffsetY + alignmentOffsetY + topMargin;
1192 
1193             if (width != c.getMeasuredWidth() || height != c.getMeasuredHeight()) {
1194                 c.measure(makeMeasureSpec(width, EXACTLY), makeMeasureSpec(height, EXACTLY));
1195             }
1196             c.layout(cx, cy, cx + width, cy + height);
1197         }
1198     }
1199 
1200     @Override
getAccessibilityClassName()1201     public CharSequence getAccessibilityClassName() {
1202         return GridLayout.class.getName();
1203     }
1204 
1205     // Inner classes
1206 
1207     /*
1208      This internal class houses the algorithm for computing the locations of grid lines;
1209      along either the horizontal or vertical axis. A GridLayout uses two instances of this class -
1210      distinguished by the "horizontal" flag which is true for the horizontal axis and false
1211      for the vertical one.
1212      */
1213     final class Axis {
1214         private static final int NEW = 0;
1215         private static final int PENDING = 1;
1216         private static final int COMPLETE = 2;
1217 
1218         public final boolean horizontal;
1219 
1220         public int definedCount = UNDEFINED;
1221         private int maxIndex = UNDEFINED;
1222 
1223         PackedMap<Spec, Bounds> groupBounds;
1224         public boolean groupBoundsValid = false;
1225 
1226         PackedMap<Interval, MutableInt> forwardLinks;
1227         public boolean forwardLinksValid = false;
1228 
1229         PackedMap<Interval, MutableInt> backwardLinks;
1230         public boolean backwardLinksValid = false;
1231 
1232         public int[] leadingMargins;
1233         public boolean leadingMarginsValid = false;
1234 
1235         public int[] trailingMargins;
1236         public boolean trailingMarginsValid = false;
1237 
1238         public Arc[] arcs;
1239         public boolean arcsValid = false;
1240 
1241         public int[] locations;
1242         public boolean locationsValid = false;
1243 
1244         public boolean hasWeights;
1245         public boolean hasWeightsValid = false;
1246         public int[] deltas;
1247 
1248         boolean orderPreserved = DEFAULT_ORDER_PRESERVED;
1249 
1250         private MutableInt parentMin = new MutableInt(0);
1251         private MutableInt parentMax = new MutableInt(-MAX_SIZE);
1252 
Axis(boolean horizontal)1253         private Axis(boolean horizontal) {
1254             this.horizontal = horizontal;
1255         }
1256 
calculateMaxIndex()1257         private int calculateMaxIndex() {
1258             // the number Integer.MIN_VALUE + 1 comes up in undefined cells
1259             int result = -1;
1260             for (int i = 0, N = getChildCount(); i < N; i++) {
1261                 View c = getChildAt(i);
1262                 LayoutParams params = getLayoutParams(c);
1263                 Spec spec = horizontal ? params.columnSpec : params.rowSpec;
1264                 Interval span = spec.span;
1265                 result = max(result, span.min);
1266                 result = max(result, span.max);
1267                 result = max(result, span.size());
1268             }
1269             return result == -1 ? UNDEFINED : result;
1270         }
1271 
getMaxIndex()1272         private int getMaxIndex() {
1273             if (maxIndex == UNDEFINED) {
1274                 maxIndex = max(0, calculateMaxIndex()); // use zero when there are no children
1275             }
1276             return maxIndex;
1277         }
1278 
getCount()1279         public int getCount() {
1280             return max(definedCount, getMaxIndex());
1281         }
1282 
setCount(int count)1283         public void setCount(int count) {
1284             if (count != UNDEFINED && count < getMaxIndex()) {
1285                 handleInvalidParams((horizontal ? "column" : "row") +
1286                         "Count must be greater than or equal to the maximum of all grid indices " +
1287                         "(and spans) defined in the LayoutParams of each child");
1288             }
1289             this.definedCount = count;
1290         }
1291 
isOrderPreserved()1292         public boolean isOrderPreserved() {
1293             return orderPreserved;
1294         }
1295 
setOrderPreserved(boolean orderPreserved)1296         public void setOrderPreserved(boolean orderPreserved) {
1297             this.orderPreserved = orderPreserved;
1298             invalidateStructure();
1299         }
1300 
createGroupBounds()1301         private PackedMap<Spec, Bounds> createGroupBounds() {
1302             Assoc<Spec, Bounds> assoc = Assoc.of(Spec.class, Bounds.class);
1303             for (int i = 0, N = getChildCount(); i < N; i++) {
1304                 View c = getChildAt(i);
1305                 // we must include views that are GONE here, see introductory javadoc
1306                 LayoutParams lp = getLayoutParams(c);
1307                 Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
1308                 Bounds bounds = spec.getAbsoluteAlignment(horizontal).getBounds();
1309                 assoc.put(spec, bounds);
1310             }
1311             return assoc.pack();
1312         }
1313 
computeGroupBounds()1314         private void computeGroupBounds() {
1315             Bounds[] values = groupBounds.values;
1316             for (int i = 0; i < values.length; i++) {
1317                 values[i].reset();
1318             }
1319             for (int i = 0, N = getChildCount(); i < N; i++) {
1320                 View c = getChildAt(i);
1321                 // we must include views that are GONE here, see introductory javadoc
1322                 LayoutParams lp = getLayoutParams(c);
1323                 Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
1324                 int size = getMeasurementIncludingMargin(c, horizontal) +
1325                         ((spec.weight == 0) ? 0 : getDeltas()[i]);
1326                 groupBounds.getValue(i).include(GridLayout.this, c, spec, this, size);
1327             }
1328         }
1329 
getGroupBounds()1330         public PackedMap<Spec, Bounds> getGroupBounds() {
1331             if (groupBounds == null) {
1332                 groupBounds = createGroupBounds();
1333             }
1334             if (!groupBoundsValid) {
1335                 computeGroupBounds();
1336                 groupBoundsValid = true;
1337             }
1338             return groupBounds;
1339         }
1340 
1341         // Add values computed by alignment - taking the max of all alignments in each span
createLinks(boolean min)1342         private PackedMap<Interval, MutableInt> createLinks(boolean min) {
1343             Assoc<Interval, MutableInt> result = Assoc.of(Interval.class, MutableInt.class);
1344             Spec[] keys = getGroupBounds().keys;
1345             for (int i = 0, N = keys.length; i < N; i++) {
1346                 Interval span = min ? keys[i].span : keys[i].span.inverse();
1347                 result.put(span, new MutableInt());
1348             }
1349             return result.pack();
1350         }
1351 
computeLinks(PackedMap<Interval, MutableInt> links, boolean min)1352         private void computeLinks(PackedMap<Interval, MutableInt> links, boolean min) {
1353             MutableInt[] spans = links.values;
1354             for (int i = 0; i < spans.length; i++) {
1355                 spans[i].reset();
1356             }
1357 
1358             // Use getter to trigger a re-evaluation
1359             Bounds[] bounds = getGroupBounds().values;
1360             for (int i = 0; i < bounds.length; i++) {
1361                 int size = bounds[i].size(min);
1362                 MutableInt valueHolder = links.getValue(i);
1363                 // this effectively takes the max() of the minima and the min() of the maxima
1364                 valueHolder.value = max(valueHolder.value, min ? size : -size);
1365             }
1366         }
1367 
getForwardLinks()1368         private PackedMap<Interval, MutableInt> getForwardLinks() {
1369             if (forwardLinks == null) {
1370                 forwardLinks = createLinks(true);
1371             }
1372             if (!forwardLinksValid) {
1373                 computeLinks(forwardLinks, true);
1374                 forwardLinksValid = true;
1375             }
1376             return forwardLinks;
1377         }
1378 
getBackwardLinks()1379         private PackedMap<Interval, MutableInt> getBackwardLinks() {
1380             if (backwardLinks == null) {
1381                 backwardLinks = createLinks(false);
1382             }
1383             if (!backwardLinksValid) {
1384                 computeLinks(backwardLinks, false);
1385                 backwardLinksValid = true;
1386             }
1387             return backwardLinks;
1388         }
1389 
include(List<Arc> arcs, Interval key, MutableInt size, boolean ignoreIfAlreadyPresent)1390         private void include(List<Arc> arcs, Interval key, MutableInt size,
1391                 boolean ignoreIfAlreadyPresent) {
1392             /*
1393             Remove self referential links.
1394             These appear:
1395                 . as parental constraints when GridLayout has no children
1396                 . when components have been marked as GONE
1397             */
1398             if (key.size() == 0) {
1399                 return;
1400             }
1401             // this bit below should really be computed outside here -
1402             // its just to stop default (row/col > 0) constraints obliterating valid entries
1403             if (ignoreIfAlreadyPresent) {
1404                 for (Arc arc : arcs) {
1405                     Interval span = arc.span;
1406                     if (span.equals(key)) {
1407                         return;
1408                     }
1409                 }
1410             }
1411             arcs.add(new Arc(key, size));
1412         }
1413 
include(List<Arc> arcs, Interval key, MutableInt size)1414         private void include(List<Arc> arcs, Interval key, MutableInt size) {
1415             include(arcs, key, size, true);
1416         }
1417 
1418         // Group arcs by their first vertex, returning an array of arrays.
1419         // This is linear in the number of arcs.
groupArcsByFirstVertex(Arc[] arcs)1420         Arc[][] groupArcsByFirstVertex(Arc[] arcs) {
1421             int N = getCount() + 1; // the number of vertices
1422             Arc[][] result = new Arc[N][];
1423             int[] sizes = new int[N];
1424             for (Arc arc : arcs) {
1425                 sizes[arc.span.min]++;
1426             }
1427             for (int i = 0; i < sizes.length; i++) {
1428                 result[i] = new Arc[sizes[i]];
1429             }
1430             // reuse the sizes array to hold the current last elements as we insert each arc
1431             Arrays.fill(sizes, 0);
1432             for (Arc arc : arcs) {
1433                 int i = arc.span.min;
1434                 result[i][sizes[i]++] = arc;
1435             }
1436 
1437             return result;
1438         }
1439 
topologicalSort(final Arc[] arcs)1440         private Arc[] topologicalSort(final Arc[] arcs) {
1441             return new Object() {
1442                 Arc[] result = new Arc[arcs.length];
1443                 int cursor = result.length - 1;
1444                 Arc[][] arcsByVertex = groupArcsByFirstVertex(arcs);
1445                 int[] visited = new int[getCount() + 1];
1446 
1447                 void walk(int loc) {
1448                     switch (visited[loc]) {
1449                         case NEW: {
1450                             visited[loc] = PENDING;
1451                             for (Arc arc : arcsByVertex[loc]) {
1452                                 walk(arc.span.max);
1453                                 result[cursor--] = arc;
1454                             }
1455                             visited[loc] = COMPLETE;
1456                             break;
1457                         }
1458                         case PENDING: {
1459                             // le singe est dans l'arbre
1460                             assert false;
1461                             break;
1462                         }
1463                         case COMPLETE: {
1464                             break;
1465                         }
1466                     }
1467                 }
1468 
1469                 Arc[] sort() {
1470                     for (int loc = 0, N = arcsByVertex.length; loc < N; loc++) {
1471                         walk(loc);
1472                     }
1473                     assert cursor == -1;
1474                     return result;
1475                 }
1476             }.sort();
1477         }
1478 
topologicalSort(List<Arc> arcs)1479         private Arc[] topologicalSort(List<Arc> arcs) {
1480             return topologicalSort(arcs.toArray(new Arc[arcs.size()]));
1481         }
1482 
addComponentSizes(List<Arc> result, PackedMap<Interval, MutableInt> links)1483         private void addComponentSizes(List<Arc> result, PackedMap<Interval, MutableInt> links) {
1484             for (int i = 0; i < links.keys.length; i++) {
1485                 Interval key = links.keys[i];
1486                 include(result, key, links.values[i], false);
1487             }
1488         }
1489 
createArcs()1490         private Arc[] createArcs() {
1491             List<Arc> mins = new ArrayList<Arc>();
1492             List<Arc> maxs = new ArrayList<Arc>();
1493 
1494             // Add the minimum values from the components.
1495             addComponentSizes(mins, getForwardLinks());
1496             // Add the maximum values from the components.
1497             addComponentSizes(maxs, getBackwardLinks());
1498 
1499             // Add ordering constraints to prevent row/col sizes from going negative
1500             if (orderPreserved) {
1501                 // Add a constraint for every row/col
1502                 for (int i = 0; i < getCount(); i++) {
1503                     include(mins, new Interval(i, i + 1), new MutableInt(0));
1504                 }
1505             }
1506 
1507             // Add the container constraints. Use the version of include that allows
1508             // duplicate entries in case a child spans the entire grid.
1509             int N = getCount();
1510             include(mins, new Interval(0, N), parentMin, false);
1511             include(maxs, new Interval(N, 0), parentMax, false);
1512 
1513             // Sort
1514             Arc[] sMins = topologicalSort(mins);
1515             Arc[] sMaxs = topologicalSort(maxs);
1516 
1517             return append(sMins, sMaxs);
1518         }
1519 
computeArcs()1520         private void computeArcs() {
1521             // getting the links validates the values that are shared by the arc list
1522             getForwardLinks();
1523             getBackwardLinks();
1524         }
1525 
getArcs()1526         public Arc[] getArcs() {
1527             if (arcs == null) {
1528                 arcs = createArcs();
1529             }
1530             if (!arcsValid) {
1531                 computeArcs();
1532                 arcsValid = true;
1533             }
1534             return arcs;
1535         }
1536 
relax(int[] locations, Arc entry)1537         private boolean relax(int[] locations, Arc entry) {
1538             if (!entry.valid) {
1539                 return false;
1540             }
1541             Interval span = entry.span;
1542             int u = span.min;
1543             int v = span.max;
1544             int value = entry.value.value;
1545             int candidate = locations[u] + value;
1546             if (candidate > locations[v]) {
1547                 locations[v] = candidate;
1548                 return true;
1549             }
1550             return false;
1551         }
1552 
init(int[] locations)1553         private void init(int[] locations) {
1554             Arrays.fill(locations, 0);
1555         }
1556 
arcsToString(List<Arc> arcs)1557         private String arcsToString(List<Arc> arcs) {
1558             String var = horizontal ? "x" : "y";
1559             StringBuilder result = new StringBuilder();
1560             boolean first = true;
1561             for (Arc arc : arcs) {
1562                 if (first) {
1563                     first = false;
1564                 } else {
1565                     result = result.append(", ");
1566                 }
1567                 int src = arc.span.min;
1568                 int dst = arc.span.max;
1569                 int value = arc.value.value;
1570                 result.append((src < dst) ?
1571                         var + dst + "-" + var + src + ">=" + value :
1572                         var + src + "-" + var + dst + "<=" + -value);
1573 
1574             }
1575             return result.toString();
1576         }
1577 
1578         private void logError(String axisName, Arc[] arcs, boolean[] culprits0) {
1579             List<Arc> culprits = new ArrayList<Arc>();
1580             List<Arc> removed = new ArrayList<Arc>();
1581             for (int c = 0; c < arcs.length; c++) {
1582                 Arc arc = arcs[c];
1583                 if (culprits0[c]) {
1584                     culprits.add(arc);
1585                 }
1586                 if (!arc.valid) {
1587                     removed.add(arc);
1588                 }
1589             }
1590             mPrinter.println(axisName + " constraints: " + arcsToString(culprits) +
1591                     " are inconsistent; permanently removing: " + arcsToString(removed) + ". ");
1592         }
1593 
1594         /*
1595         Bellman-Ford variant - modified to reduce typical running time from O(N^2) to O(N)
1596 
1597         GridLayout converts its requirements into a system of linear constraints of the
1598         form:
1599 
1600         x[i] - x[j] < a[k]
1601 
1602         Where the x[i] are variables and the a[k] are constants.
1603 
1604         For example, if the variables were instead labeled x, y, z we might have:
1605 
1606             x - y < 17
1607             y - z < 23
1608             z - x < 42
1609 
1610         This is a special case of the Linear Programming problem that is, in turn,
1611         equivalent to the single-source shortest paths problem on a digraph, for
1612         which the O(n^2) Bellman-Ford algorithm the most commonly used general solution.
1613         */
1614         private boolean solve(Arc[] arcs, int[] locations) {
1615             return solve(arcs, locations, true);
1616         }
1617 
1618         private boolean solve(Arc[] arcs, int[] locations, boolean modifyOnError) {
1619             String axisName = horizontal ? "horizontal" : "vertical";
1620             int N = getCount() + 1; // The number of vertices is the number of columns/rows + 1.
1621             boolean[] originalCulprits = null;
1622 
1623             for (int p = 0; p < arcs.length; p++) {
1624                 init(locations);
1625 
1626                 // We take one extra pass over traditional Bellman-Ford (and omit their final step)
1627                 for (int i = 0; i < N; i++) {
1628                     boolean changed = false;
1629                     for (int j = 0, length = arcs.length; j < length; j++) {
1630                         changed |= relax(locations, arcs[j]);
1631                     }
1632                     if (!changed) {
1633                         if (originalCulprits != null) {
1634                             logError(axisName, arcs, originalCulprits);
1635                         }
1636                         return true;
1637                     }
1638                 }
1639 
1640                 if (!modifyOnError) {
1641                     return false; // cannot solve with these constraints
1642                 }
1643 
1644                 boolean[] culprits = new boolean[arcs.length];
1645                 for (int i = 0; i < N; i++) {
1646                     for (int j = 0, length = arcs.length; j < length; j++) {
1647                         culprits[j] |= relax(locations, arcs[j]);
1648                     }
1649                 }
1650 
1651                 if (p == 0) {
1652                     originalCulprits = culprits;
1653                 }
1654 
1655                 for (int i = 0; i < arcs.length; i++) {
1656                     if (culprits[i]) {
1657                         Arc arc = arcs[i];
1658                         // Only remove max values, min values alone cannot be inconsistent
1659                         if (arc.span.min < arc.span.max) {
1660                             continue;
1661                         }
1662                         arc.valid = false;
1663                         break;
1664                     }
1665                 }
1666             }
1667             return true;
1668         }
1669 
1670         private void computeMargins(boolean leading) {
1671             int[] margins = leading ? leadingMargins : trailingMargins;
1672             for (int i = 0, N = getChildCount(); i < N; i++) {
1673                 View c = getChildAt(i);
1674                 if (c.getVisibility() == View.GONE) continue;
1675                 LayoutParams lp = getLayoutParams(c);
1676                 Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
1677                 Interval span = spec.span;
1678                 int index = leading ? span.min : span.max;
1679                 margins[index] = max(margins[index], getMargin1(c, horizontal, leading));
1680             }
1681         }
1682 
1683         // External entry points
1684 
1685         public int[] getLeadingMargins() {
1686             if (leadingMargins == null) {
1687                 leadingMargins = new int[getCount() + 1];
1688             }
1689             if (!leadingMarginsValid) {
1690                 computeMargins(true);
1691                 leadingMarginsValid = true;
1692             }
1693             return leadingMargins;
1694         }
1695 
1696         public int[] getTrailingMargins() {
1697             if (trailingMargins == null) {
1698                 trailingMargins = new int[getCount() + 1];
1699             }
1700             if (!trailingMarginsValid) {
1701                 computeMargins(false);
1702                 trailingMarginsValid = true;
1703             }
1704             return trailingMargins;
1705         }
1706 
1707         private boolean solve(int[] a) {
1708             return solve(getArcs(), a);
1709         }
1710 
1711         private boolean computeHasWeights() {
1712             for (int i = 0, N = getChildCount(); i < N; i++) {
1713                 final View child = getChildAt(i);
1714                 if (child.getVisibility() == View.GONE) {
1715                     continue;
1716                 }
1717                 LayoutParams lp = getLayoutParams(child);
1718                 Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
1719                 if (spec.weight != 0) {
1720                     return true;
1721                 }
1722             }
1723             return false;
1724         }
1725 
1726         private boolean hasWeights() {
1727             if (!hasWeightsValid) {
1728                 hasWeights = computeHasWeights();
1729                 hasWeightsValid = true;
1730             }
1731             return hasWeights;
1732         }
1733 
1734         public int[] getDeltas() {
1735             if (deltas == null) {
1736                 deltas = new int[getChildCount()];
1737             }
1738             return deltas;
1739         }
1740 
1741         private void shareOutDelta(int totalDelta, float totalWeight) {
1742             Arrays.fill(deltas, 0);
1743             for (int i = 0, N = getChildCount(); i < N; i++) {
1744                 final View c = getChildAt(i);
1745                 if (c.getVisibility() == View.GONE) {
1746                     continue;
1747                 }
1748                 LayoutParams lp = getLayoutParams(c);
1749                 Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
1750                 float weight = spec.weight;
1751                 if (weight != 0) {
1752                     int delta = Math.round((weight * totalDelta / totalWeight));
1753                     deltas[i] = delta;
1754                     // the two adjustments below are to counter the above rounding and avoid
1755                     // off-by-ones at the end
1756                     totalDelta -= delta;
1757                     totalWeight -= weight;
1758                 }
1759             }
1760         }
1761 
1762         private void solveAndDistributeSpace(int[] a) {
1763             Arrays.fill(getDeltas(), 0);
1764             solve(a);
1765             int deltaMax = parentMin.value * getChildCount() + 1; //exclusive
1766             if (deltaMax < 2) {
1767                 return; //don't have any delta to distribute
1768             }
1769             int deltaMin = 0; //inclusive
1770 
1771             float totalWeight = calculateTotalWeight();
1772 
1773             int validDelta = -1; //delta for which a solution exists
1774             boolean validSolution = true;
1775             // do a binary search to find the max delta that won't conflict with constraints
1776             while(deltaMin < deltaMax) {
1777                 // cast to long to prevent overflow.
1778                 final int delta = (int) (((long) deltaMin + deltaMax) / 2);
1779                 invalidateValues();
1780                 shareOutDelta(delta, totalWeight);
1781                 validSolution = solve(getArcs(), a, false);
1782                 if (validSolution) {
1783                     validDelta = delta;
1784                     deltaMin = delta + 1;
1785                 } else {
1786                     deltaMax = delta;
1787                 }
1788             }
1789             if (validDelta > 0 && !validSolution) {
1790                 // last solution was not successful but we have a successful one. Use it.
1791                 invalidateValues();
1792                 shareOutDelta(validDelta, totalWeight);
1793                 solve(a);
1794             }
1795         }
1796 
1797         private float calculateTotalWeight() {
1798             float totalWeight = 0f;
1799             for (int i = 0, N = getChildCount(); i < N; i++) {
1800                 View c = getChildAt(i);
1801                 if (c.getVisibility() == View.GONE) {
1802                     continue;
1803                 }
1804                 LayoutParams lp = getLayoutParams(c);
1805                 Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
1806                 totalWeight += spec.weight;
1807             }
1808             return totalWeight;
1809         }
1810 
1811         private void computeLocations(int[] a) {
1812             if (!hasWeights()) {
1813                 solve(a);
1814             } else {
1815                 solveAndDistributeSpace(a);
1816             }
1817             if (!orderPreserved) {
1818                 // Solve returns the smallest solution to the constraint system for which all
1819                 // values are positive. One value is therefore zero - though if the row/col
1820                 // order is not preserved this may not be the first vertex. For consistency,
1821                 // translate all the values so that they measure the distance from a[0]; the
1822                 // leading edge of the parent. After this transformation some values may be
1823                 // negative.
1824                 int a0 = a[0];
1825                 for (int i = 0, N = a.length; i < N; i++) {
1826                     a[i] = a[i] - a0;
1827                 }
1828             }
1829         }
1830 
1831         public int[] getLocations() {
1832             if (locations == null) {
1833                 int N = getCount() + 1;
1834                 locations = new int[N];
1835             }
1836             if (!locationsValid) {
1837                 computeLocations(locations);
1838                 locationsValid = true;
1839             }
1840             return locations;
1841         }
1842 
1843         private int size(int[] locations) {
1844             // The parental edges are attached to vertices 0 and N - even when order is not
1845             // being preserved and other vertices fall outside this range. Measure the distance
1846             // between vertices 0 and N, assuming that locations[0] = 0.
1847             return locations[getCount()];
1848         }
1849 
1850         private void setParentConstraints(int min, int max) {
1851             parentMin.value = min;
1852             parentMax.value = -max;
1853             locationsValid = false;
1854         }
1855 
1856         private int getMeasure(int min, int max) {
1857             setParentConstraints(min, max);
1858             return size(getLocations());
1859         }
1860 
1861         public int getMeasure(int measureSpec) {
1862             int mode = MeasureSpec.getMode(measureSpec);
1863             int size = MeasureSpec.getSize(measureSpec);
1864             switch (mode) {
1865                 case MeasureSpec.UNSPECIFIED: {
1866                     return getMeasure(0, MAX_SIZE);
1867                 }
1868                 case MeasureSpec.EXACTLY: {
1869                     return getMeasure(size, size);
1870                 }
1871                 case MeasureSpec.AT_MOST: {
1872                     return getMeasure(0, size);
1873                 }
1874                 default: {
1875                     assert false;
1876                     return 0;
1877                 }
1878             }
1879         }
1880 
1881         public void layout(int size) {
1882             setParentConstraints(size, size);
1883             getLocations();
1884         }
1885 
1886         public void invalidateStructure() {
1887             maxIndex = UNDEFINED;
1888 
1889             groupBounds = null;
1890             forwardLinks = null;
1891             backwardLinks = null;
1892 
1893             leadingMargins = null;
1894             trailingMargins = null;
1895             arcs = null;
1896 
1897             locations = null;
1898 
1899             deltas = null;
1900             hasWeightsValid = false;
1901 
1902             invalidateValues();
1903         }
1904 
1905         public void invalidateValues() {
1906             groupBoundsValid = false;
1907             forwardLinksValid = false;
1908             backwardLinksValid = false;
1909 
1910             leadingMarginsValid = false;
1911             trailingMarginsValid = false;
1912             arcsValid = false;
1913 
1914             locationsValid = false;
1915         }
1916     }
1917 
1918     /**
1919      * Layout information associated with each of the children of a GridLayout.
1920      * <p>
1921      * GridLayout supports both row and column spanning and arbitrary forms of alignment within
1922      * each cell group. The fundamental parameters associated with each cell group are
1923      * gathered into their vertical and horizontal components and stored
1924      * in the {@link #rowSpec} and {@link #columnSpec} layout parameters.
1925      * {@link GridLayout.Spec Specs} are immutable structures
1926      * and may be shared between the layout parameters of different children.
1927      * <p>
1928      * The row and column specs contain the leading and trailing indices along each axis
1929      * and together specify the four grid indices that delimit the cells of this cell group.
1930      * <p>
1931      * The  alignment properties of the row and column specs together specify
1932      * both aspects of alignment within the cell group. It is also possible to specify a child's
1933      * alignment within its cell group by using the {@link GridLayout.LayoutParams#setGravity(int)}
1934      * method.
1935      * <p>
1936      * The weight property is also included in Spec and specifies the proportion of any
1937      * excess space that is due to the associated view.
1938      *
1939      * <h4>WRAP_CONTENT and MATCH_PARENT</h4>
1940      *
1941      * Because the default values of the {@link #width} and {@link #height}
1942      * properties are both {@link #WRAP_CONTENT}, this value never needs to be explicitly
1943      * declared in the layout parameters of GridLayout's children. In addition,
1944      * GridLayout does not distinguish the special size value {@link #MATCH_PARENT} from
1945      * {@link #WRAP_CONTENT}. A component's ability to expand to the size of the parent is
1946      * instead controlled by the principle of <em>flexibility</em>,
1947      * as discussed in {@link GridLayout}.
1948      *
1949      * <h4>Summary</h4>
1950      *
1951      * You should not need to use either of the special size values:
1952      * {@code WRAP_CONTENT} or {@code MATCH_PARENT} when configuring the children of
1953      * a GridLayout.
1954      *
1955      * <h4>Default values</h4>
1956      *
1957      * <ul>
1958      *     <li>{@link #width} = {@link #WRAP_CONTENT}</li>
1959      *     <li>{@link #height} = {@link #WRAP_CONTENT}</li>
1960      *     <li>{@link #topMargin} = 0 when
1961      *          {@link GridLayout#setUseDefaultMargins(boolean) useDefaultMargins} is
1962      *          {@code false}; otherwise {@link #UNDEFINED}, to
1963      *          indicate that a default value should be computed on demand. </li>
1964      *     <li>{@link #leftMargin} = 0 when
1965      *          {@link GridLayout#setUseDefaultMargins(boolean) useDefaultMargins} is
1966      *          {@code false}; otherwise {@link #UNDEFINED}, to
1967      *          indicate that a default value should be computed on demand. </li>
1968      *     <li>{@link #bottomMargin} = 0 when
1969      *          {@link GridLayout#setUseDefaultMargins(boolean) useDefaultMargins} is
1970      *          {@code false}; otherwise {@link #UNDEFINED}, to
1971      *          indicate that a default value should be computed on demand. </li>
1972      *     <li>{@link #rightMargin} = 0 when
1973      *          {@link GridLayout#setUseDefaultMargins(boolean) useDefaultMargins} is
1974      *          {@code false}; otherwise {@link #UNDEFINED}, to
1975      *          indicate that a default value should be computed on demand. </li>
1976      *     <li>{@link #rowSpec}<code>.row</code> = {@link #UNDEFINED} </li>
1977      *     <li>{@link #rowSpec}<code>.rowSpan</code> = 1 </li>
1978      *     <li>{@link #rowSpec}<code>.alignment</code> = {@link #BASELINE} </li>
1979      *     <li>{@link #rowSpec}<code>.weight</code> = 0 </li>
1980      *     <li>{@link #columnSpec}<code>.column</code> = {@link #UNDEFINED} </li>
1981      *     <li>{@link #columnSpec}<code>.columnSpan</code> = 1 </li>
1982      *     <li>{@link #columnSpec}<code>.alignment</code> = {@link #START} </li>
1983      *     <li>{@link #columnSpec}<code>.weight</code> = 0 </li>
1984      * </ul>
1985      *
1986      * See {@link GridLayout} for a more complete description of the conventions
1987      * used by GridLayout in the interpretation of the properties of this class.
1988      *
1989      * @attr ref android.R.styleable#GridLayout_Layout_layout_row
1990      * @attr ref android.R.styleable#GridLayout_Layout_layout_rowSpan
1991      * @attr ref android.R.styleable#GridLayout_Layout_layout_rowWeight
1992      * @attr ref android.R.styleable#GridLayout_Layout_layout_column
1993      * @attr ref android.R.styleable#GridLayout_Layout_layout_columnSpan
1994      * @attr ref android.R.styleable#GridLayout_Layout_layout_columnWeight
1995      * @attr ref android.R.styleable#GridLayout_Layout_layout_gravity
1996      */
1997     public static class LayoutParams extends MarginLayoutParams {
1998 
1999         // Default values
2000 
2001         private static final int DEFAULT_WIDTH = WRAP_CONTENT;
2002         private static final int DEFAULT_HEIGHT = WRAP_CONTENT;
2003         private static final int DEFAULT_MARGIN = UNDEFINED;
2004         private static final int DEFAULT_ROW = UNDEFINED;
2005         private static final int DEFAULT_COLUMN = UNDEFINED;
2006         private static final Interval DEFAULT_SPAN = new Interval(UNDEFINED, UNDEFINED + 1);
2007         private static final int DEFAULT_SPAN_SIZE = DEFAULT_SPAN.size();
2008 
2009         // TypedArray indices
2010 
2011         private static final int MARGIN = R.styleable.ViewGroup_MarginLayout_layout_margin;
2012         private static final int LEFT_MARGIN = R.styleable.ViewGroup_MarginLayout_layout_marginLeft;
2013         private static final int TOP_MARGIN = R.styleable.ViewGroup_MarginLayout_layout_marginTop;
2014         private static final int RIGHT_MARGIN =
2015                 R.styleable.ViewGroup_MarginLayout_layout_marginRight;
2016         private static final int BOTTOM_MARGIN =
2017                 R.styleable.ViewGroup_MarginLayout_layout_marginBottom;
2018         private static final int COLUMN = R.styleable.GridLayout_Layout_layout_column;
2019         private static final int COLUMN_SPAN = R.styleable.GridLayout_Layout_layout_columnSpan;
2020         private static final int COLUMN_WEIGHT = R.styleable.GridLayout_Layout_layout_columnWeight;
2021 
2022         private static final int ROW = R.styleable.GridLayout_Layout_layout_row;
2023         private static final int ROW_SPAN = R.styleable.GridLayout_Layout_layout_rowSpan;
2024         private static final int ROW_WEIGHT = R.styleable.GridLayout_Layout_layout_rowWeight;
2025 
2026         private static final int GRAVITY = R.styleable.GridLayout_Layout_layout_gravity;
2027 
2028         // Instance variables
2029 
2030         /**
2031          * The spec that defines the vertical characteristics of the cell group
2032          * described by these layout parameters.
2033          * If an assignment is made to this field after a measurement or layout operation
2034          * has already taken place, a call to
2035          * {@link ViewGroup#setLayoutParams(ViewGroup.LayoutParams)}
2036          * must be made to notify GridLayout of the change. GridLayout is normally able
2037          * to detect when code fails to observe this rule, issue a warning and take steps to
2038          * compensate for the omission. This facility is implemented on a best effort basis
2039          * and should not be relied upon in production code - so it is best to include the above
2040          * calls to remove the warnings as soon as it is practical.
2041          */
2042         public Spec rowSpec = Spec.UNDEFINED;
2043 
2044         /**
2045          * The spec that defines the horizontal characteristics of the cell group
2046          * described by these layout parameters.
2047          * If an assignment is made to this field after a measurement or layout operation
2048          * has already taken place, a call to
2049          * {@link ViewGroup#setLayoutParams(ViewGroup.LayoutParams)}
2050          * must be made to notify GridLayout of the change. GridLayout is normally able
2051          * to detect when code fails to observe this rule, issue a warning and take steps to
2052          * compensate for the omission. This facility is implemented on a best effort basis
2053          * and should not be relied upon in production code - so it is best to include the above
2054          * calls to remove the warnings as soon as it is practical.
2055          */
2056         public Spec columnSpec = Spec.UNDEFINED;
2057 
2058         // Constructors
2059 
2060         private LayoutParams(
2061                 int width, int height,
2062                 int left, int top, int right, int bottom,
2063                 Spec rowSpec, Spec columnSpec) {
2064             super(width, height);
2065             setMargins(left, top, right, bottom);
2066             this.rowSpec = rowSpec;
2067             this.columnSpec = columnSpec;
2068         }
2069 
2070         /**
2071          * Constructs a new LayoutParams instance for this <code>rowSpec</code>
2072          * and <code>columnSpec</code>. All other fields are initialized with
2073          * default values as defined in {@link LayoutParams}.
2074          *
2075          * @param rowSpec    the rowSpec
2076          * @param columnSpec the columnSpec
2077          */
2078         public LayoutParams(Spec rowSpec, Spec columnSpec) {
2079             this(DEFAULT_WIDTH, DEFAULT_HEIGHT,
2080                     DEFAULT_MARGIN, DEFAULT_MARGIN, DEFAULT_MARGIN, DEFAULT_MARGIN,
2081                     rowSpec, columnSpec);
2082         }
2083 
2084         /**
2085          * Constructs a new LayoutParams with default values as defined in {@link LayoutParams}.
2086          */
2087         public LayoutParams() {
2088             this(Spec.UNDEFINED, Spec.UNDEFINED);
2089         }
2090 
2091         // Copying constructors
2092 
2093         /**
2094          * {@inheritDoc}
2095          */
2096         public LayoutParams(ViewGroup.LayoutParams params) {
2097             super(params);
2098         }
2099 
2100         /**
2101          * {@inheritDoc}
2102          */
2103         public LayoutParams(MarginLayoutParams params) {
2104             super(params);
2105         }
2106 
2107         /**
2108          * Copy constructor. Clones the width, height, margin values, row spec,
2109          * and column spec of the source.
2110          *
2111          * @param source The layout params to copy from.
2112          */
2113         public LayoutParams(LayoutParams source) {
2114             super(source);
2115 
2116             this.rowSpec = source.rowSpec;
2117             this.columnSpec = source.columnSpec;
2118         }
2119 
2120         // AttributeSet constructors
2121 
2122         /**
2123          * {@inheritDoc}
2124          *
2125          * Values not defined in the attribute set take the default values
2126          * defined in {@link LayoutParams}.
2127          */
2128         public LayoutParams(Context context, AttributeSet attrs) {
2129             super(context, attrs);
2130             reInitSuper(context, attrs);
2131             init(context, attrs);
2132         }
2133 
2134         // Implementation
2135 
2136         // Reinitialise the margins using a different default policy than MarginLayoutParams.
2137         // Here we use the value UNDEFINED (as distinct from zero) to represent the undefined state
2138         // so that a layout manager default can be accessed post set up. We need this as, at the
2139         // point of installation, we do not know how many rows/cols there are and therefore
2140         // which elements are positioned next to the container's trailing edges. We need to
2141         // know this as margins around the container's boundary should have different
2142         // defaults to those between peers.
2143 
2144         // This method could be parametrized and moved into MarginLayout.
2145         private void reInitSuper(Context context, AttributeSet attrs) {
2146             TypedArray a =
2147                     context.obtainStyledAttributes(attrs, R.styleable.ViewGroup_MarginLayout);
2148             try {
2149                 int margin = a.getDimensionPixelSize(MARGIN, DEFAULT_MARGIN);
2150 
2151                 this.leftMargin = a.getDimensionPixelSize(LEFT_MARGIN, margin);
2152                 this.topMargin = a.getDimensionPixelSize(TOP_MARGIN, margin);
2153                 this.rightMargin = a.getDimensionPixelSize(RIGHT_MARGIN, margin);
2154                 this.bottomMargin = a.getDimensionPixelSize(BOTTOM_MARGIN, margin);
2155             } finally {
2156                 a.recycle();
2157             }
2158         }
2159 
2160         private void init(Context context, AttributeSet attrs) {
2161             TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.GridLayout_Layout);
2162             try {
2163                 int gravity = a.getInt(GRAVITY, Gravity.NO_GRAVITY);
2164 
2165                 int column = a.getInt(COLUMN, DEFAULT_COLUMN);
2166                 int colSpan = a.getInt(COLUMN_SPAN, DEFAULT_SPAN_SIZE);
2167                 float colWeight = a.getFloat(COLUMN_WEIGHT, Spec.DEFAULT_WEIGHT);
2168                 this.columnSpec = spec(column, colSpan, getAlignment(gravity, true), colWeight);
2169 
2170                 int row = a.getInt(ROW, DEFAULT_ROW);
2171                 int rowSpan = a.getInt(ROW_SPAN, DEFAULT_SPAN_SIZE);
2172                 float rowWeight = a.getFloat(ROW_WEIGHT, Spec.DEFAULT_WEIGHT);
2173                 this.rowSpec = spec(row, rowSpan, getAlignment(gravity, false), rowWeight);
2174             } finally {
2175                 a.recycle();
2176             }
2177         }
2178 
2179         /**
2180          * Describes how the child views are positioned. Default is {@code LEFT | BASELINE}.
2181          * See {@link Gravity}.
2182          *
2183          * @param gravity the new gravity value
2184          *
2185          * @attr ref android.R.styleable#GridLayout_Layout_layout_gravity
2186          */
2187         public void setGravity(int gravity) {
2188             rowSpec = rowSpec.copyWriteAlignment(getAlignment(gravity, false));
2189             columnSpec = columnSpec.copyWriteAlignment(getAlignment(gravity, true));
2190         }
2191 
2192         @Override
2193         protected void setBaseAttributes(TypedArray attributes, int widthAttr, int heightAttr) {
2194             this.width = attributes.getLayoutDimension(widthAttr, DEFAULT_WIDTH);
2195             this.height = attributes.getLayoutDimension(heightAttr, DEFAULT_HEIGHT);
2196         }
2197 
2198         final void setRowSpecSpan(Interval span) {
2199             rowSpec = rowSpec.copyWriteSpan(span);
2200         }
2201 
2202         final void setColumnSpecSpan(Interval span) {
2203             columnSpec = columnSpec.copyWriteSpan(span);
2204         }
2205 
2206         @Override
2207         public boolean equals(Object o) {
2208             if (this == o) return true;
2209             if (o == null || getClass() != o.getClass()) return false;
2210 
2211             LayoutParams that = (LayoutParams) o;
2212 
2213             if (!columnSpec.equals(that.columnSpec)) return false;
2214             if (!rowSpec.equals(that.rowSpec)) return false;
2215 
2216             return true;
2217         }
2218 
2219         @Override
2220         public int hashCode() {
2221             int result = rowSpec.hashCode();
2222             result = 31 * result + columnSpec.hashCode();
2223             return result;
2224         }
2225     }
2226 
2227     /*
2228     In place of a HashMap from span to Int, use an array of key/value pairs - stored in Arcs.
2229     Add the mutables completesCycle flag to avoid creating another hash table for detecting cycles.
2230      */
2231     final static class Arc {
2232         public final Interval span;
2233         public final MutableInt value;
2234         public boolean valid = true;
2235 
2236         public Arc(Interval span, MutableInt value) {
2237             this.span = span;
2238             this.value = value;
2239         }
2240 
2241         @Override
2242         public String toString() {
2243             return span + " " + (!valid ? "+>" : "->") + " " + value;
2244         }
2245     }
2246 
2247     // A mutable Integer - used to avoid heap allocation during the layout operation
2248 
2249     final static class MutableInt {
2250         public int value;
2251 
2252         public MutableInt() {
2253             reset();
2254         }
2255 
2256         public MutableInt(int value) {
2257             this.value = value;
2258         }
2259 
2260         public void reset() {
2261             value = Integer.MIN_VALUE;
2262         }
2263 
2264         @Override
2265         public String toString() {
2266             return Integer.toString(value);
2267         }
2268     }
2269 
2270     final static class Assoc<K, V> extends ArrayList<Pair<K, V>> {
2271         private final Class<K> keyType;
2272         private final Class<V> valueType;
2273 
2274         private Assoc(Class<K> keyType, Class<V> valueType) {
2275             this.keyType = keyType;
2276             this.valueType = valueType;
2277         }
2278 
2279         public static <K, V> Assoc<K, V> of(Class<K> keyType, Class<V> valueType) {
2280             return new Assoc<K, V>(keyType, valueType);
2281         }
2282 
2283         public void put(K key, V value) {
2284             add(Pair.create(key, value));
2285         }
2286 
2287         @SuppressWarnings(value = "unchecked")
2288         public PackedMap<K, V> pack() {
2289             int N = size();
2290             K[] keys = (K[]) Array.newInstance(keyType, N);
2291             V[] values = (V[]) Array.newInstance(valueType, N);
2292             for (int i = 0; i < N; i++) {
2293                 keys[i] = get(i).first;
2294                 values[i] = get(i).second;
2295             }
2296             return new PackedMap<K, V>(keys, values);
2297         }
2298     }
2299 
2300     /*
2301     This data structure is used in place of a Map where we have an index that refers to the order
2302     in which each key/value pairs were added to the map. In this case we store keys and values
2303     in arrays of a length that is equal to the number of unique keys. We also maintain an
2304     array of indexes from insertion order to the compacted arrays of keys and values.
2305 
2306     Note that behavior differs from that of a LinkedHashMap in that repeated entries
2307     *do* get added multiples times. So the length of index is equals to the number of
2308     items added.
2309 
2310     This is useful in the GridLayout class where we can rely on the order of children not
2311     changing during layout - to use integer-based lookup for our internal structures
2312     rather than using (and storing) an implementation of Map<Key, ?>.
2313      */
2314     @SuppressWarnings(value = "unchecked")
2315     final static class PackedMap<K, V> {
2316         public final int[] index;
2317         public final K[] keys;
2318         public final V[] values;
2319 
2320         private PackedMap(K[] keys, V[] values) {
2321             this.index = createIndex(keys);
2322 
2323             this.keys = compact(keys, index);
2324             this.values = compact(values, index);
2325         }
2326 
2327         public V getValue(int i) {
2328             return values[index[i]];
2329         }
2330 
2331         private static <K> int[] createIndex(K[] keys) {
2332             int size = keys.length;
2333             int[] result = new int[size];
2334 
2335             Map<K, Integer> keyToIndex = new HashMap<K, Integer>();
2336             for (int i = 0; i < size; i++) {
2337                 K key = keys[i];
2338                 Integer index = keyToIndex.get(key);
2339                 if (index == null) {
2340                     index = keyToIndex.size();
2341                     keyToIndex.put(key, index);
2342                 }
2343                 result[i] = index;
2344             }
2345             return result;
2346         }
2347 
2348         /*
2349         Create a compact array of keys or values using the supplied index.
2350          */
2351         private static <K> K[] compact(K[] a, int[] index) {
2352             int size = a.length;
2353             Class<?> componentType = a.getClass().getComponentType();
2354             K[] result = (K[]) Array.newInstance(componentType, max2(index, -1) + 1);
2355 
2356             // this overwrite duplicates, retaining the last equivalent entry
2357             for (int i = 0; i < size; i++) {
2358                 result[index[i]] = a[i];
2359             }
2360             return result;
2361         }
2362     }
2363 
2364     /*
2365     For each group (with a given alignment) we need to store the amount of space required
2366     before the alignment point and the amount of space required after it. One side of this
2367     calculation is always 0 for START and END alignments but we don't make use of this.
2368     For CENTER and BASELINE alignments both sides are needed and in the BASELINE case no
2369     simple optimisations are possible.
2370 
2371     The general algorithm therefore is to create a Map (actually a PackedMap) from
2372     group to Bounds and to loop through all Views in the group taking the maximum
2373     of the values for each View.
2374     */
2375     static class Bounds {
2376         public int before;
2377         public int after;
2378         public int flexibility; // we're flexible iff all included specs are flexible
2379 
2380         private Bounds() {
2381             reset();
2382         }
2383 
2384         protected void reset() {
2385             before = Integer.MIN_VALUE;
2386             after = Integer.MIN_VALUE;
2387             flexibility = CAN_STRETCH; // from the above, we're flexible when empty
2388         }
2389 
2390         protected void include(int before, int after) {
2391             this.before = max(this.before, before);
2392             this.after = max(this.after, after);
2393         }
2394 
2395         protected int size(boolean min) {
2396             if (!min) {
2397                 if (canStretch(flexibility)) {
2398                     return MAX_SIZE;
2399                 }
2400             }
2401             return before + after;
2402         }
2403 
2404         protected int getOffset(GridLayout gl, View c, Alignment a, int size, boolean horizontal) {
2405             return before - a.getAlignmentValue(c, size, gl.getLayoutMode());
2406         }
2407 
2408         protected final void include(GridLayout gl, View c, Spec spec, Axis axis, int size) {
2409             this.flexibility &= spec.getFlexibility();
2410             boolean horizontal = axis.horizontal;
2411             Alignment alignment = spec.getAbsoluteAlignment(axis.horizontal);
2412             // todo test this works correctly when the returned value is UNDEFINED
2413             int before = alignment.getAlignmentValue(c, size, gl.getLayoutMode());
2414             include(before, size - before);
2415         }
2416 
2417         @Override
2418         public String toString() {
2419             return "Bounds{" +
2420                     "before=" + before +
2421                     ", after=" + after +
2422                     '}';
2423         }
2424     }
2425 
2426     /**
2427      * An Interval represents a contiguous range of values that lie between
2428      * the interval's {@link #min} and {@link #max} values.
2429      * <p>
2430      * Intervals are immutable so may be passed as values and used as keys in hash tables.
2431      * It is not necessary to have multiple instances of Intervals which have the same
2432      * {@link #min} and {@link #max} values.
2433      * <p>
2434      * Intervals are often written as {@code [min, max]} and represent the set of values
2435      * {@code x} such that {@code min <= x < max}.
2436      */
2437     final static class Interval {
2438         /**
2439          * The minimum value.
2440          */
2441         public final int min;
2442 
2443         /**
2444          * The maximum value.
2445          */
2446         public final int max;
2447 
2448         /**
2449          * Construct a new Interval, {@code interval}, where:
2450          * <ul>
2451          *     <li> {@code interval.min = min} </li>
2452          *     <li> {@code interval.max = max} </li>
2453          * </ul>
2454          *
2455          * @param min the minimum value.
2456          * @param max the maximum value.
2457          */
2458         public Interval(int min, int max) {
2459             this.min = min;
2460             this.max = max;
2461         }
2462 
2463         int size() {
2464             return max - min;
2465         }
2466 
2467         Interval inverse() {
2468             return new Interval(max, min);
2469         }
2470 
2471         /**
2472          * Returns {@code true} if the {@link #getClass class},
2473          * {@link #min} and {@link #max} properties of this Interval and the
2474          * supplied parameter are pairwise equal; {@code false} otherwise.
2475          *
2476          * @param that the object to compare this interval with
2477          *
2478          * @return {@code true} if the specified object is equal to this
2479          *         {@code Interval}, {@code false} otherwise.
2480          */
2481         @Override
2482         public boolean equals(Object that) {
2483             if (this == that) {
2484                 return true;
2485             }
2486             if (that == null || getClass() != that.getClass()) {
2487                 return false;
2488             }
2489 
2490             Interval interval = (Interval) that;
2491 
2492             if (max != interval.max) {
2493                 return false;
2494             }
2495             //noinspection RedundantIfStatement
2496             if (min != interval.min) {
2497                 return false;
2498             }
2499 
2500             return true;
2501         }
2502 
2503         @Override
2504         public int hashCode() {
2505             int result = min;
2506             result = 31 * result + max;
2507             return result;
2508         }
2509 
2510         @Override
2511         public String toString() {
2512             return "[" + min + ", " + max + "]";
2513         }
2514     }
2515 
2516     /**
2517      * A Spec defines the horizontal or vertical characteristics of a group of
2518      * cells. Each spec. defines the <em>grid indices</em> and <em>alignment</em>
2519      * along the appropriate axis.
2520      * <p>
2521      * The <em>grid indices</em> are the leading and trailing edges of this cell group.
2522      * See {@link GridLayout} for a description of the conventions used by GridLayout
2523      * for grid indices.
2524      * <p>
2525      * The <em>alignment</em> property specifies how cells should be aligned in this group.
2526      * For row groups, this specifies the vertical alignment.
2527      * For column groups, this specifies the horizontal alignment.
2528      * <p>
2529      * Use the following static methods to create specs:
2530      * <ul>
2531      *   <li>{@link #spec(int)}</li>
2532      *   <li>{@link #spec(int, int)}</li>
2533      *   <li>{@link #spec(int, Alignment)}</li>
2534      *   <li>{@link #spec(int, int, Alignment)}</li>
2535      *   <li>{@link #spec(int, float)}</li>
2536      *   <li>{@link #spec(int, int, float)}</li>
2537      *   <li>{@link #spec(int, Alignment, float)}</li>
2538      *   <li>{@link #spec(int, int, Alignment, float)}</li>
2539      * </ul>
2540      *
2541      */
2542     public static class Spec {
2543         static final Spec UNDEFINED = spec(GridLayout.UNDEFINED);
2544         static final float DEFAULT_WEIGHT = 0;
2545 
2546         final boolean startDefined;
2547         final Interval span;
2548         final Alignment alignment;
2549         final float weight;
2550 
2551         private Spec(boolean startDefined, Interval span, Alignment alignment, float weight) {
2552             this.startDefined = startDefined;
2553             this.span = span;
2554             this.alignment = alignment;
2555             this.weight = weight;
2556         }
2557 
2558         private Spec(boolean startDefined, int start, int size, Alignment alignment, float weight) {
2559             this(startDefined, new Interval(start, start + size), alignment, weight);
2560         }
2561 
2562         private Alignment getAbsoluteAlignment(boolean horizontal) {
2563             if (alignment != UNDEFINED_ALIGNMENT) {
2564                 return alignment;
2565             }
2566             if (weight == 0f) {
2567                 return horizontal ? START : BASELINE;
2568             }
2569             return FILL;
2570         }
2571 
2572         final Spec copyWriteSpan(Interval span) {
2573             return new Spec(startDefined, span, alignment, weight);
2574         }
2575 
2576         final Spec copyWriteAlignment(Alignment alignment) {
2577             return new Spec(startDefined, span, alignment, weight);
2578         }
2579 
2580         final int getFlexibility() {
2581             return (alignment == UNDEFINED_ALIGNMENT && weight == 0) ? INFLEXIBLE : CAN_STRETCH;
2582         }
2583 
2584         /**
2585          * Returns {@code true} if the {@code class}, {@code alignment} and {@code span}
2586          * properties of this Spec and the supplied parameter are pairwise equal,
2587          * {@code false} otherwise.
2588          *
2589          * @param that the object to compare this spec with
2590          *
2591          * @return {@code true} if the specified object is equal to this
2592          *         {@code Spec}; {@code false} otherwise
2593          */
2594         @Override
2595         public boolean equals(Object that) {
2596             if (this == that) {
2597                 return true;
2598             }
2599             if (that == null || getClass() != that.getClass()) {
2600                 return false;
2601             }
2602 
2603             Spec spec = (Spec) that;
2604 
2605             if (!alignment.equals(spec.alignment)) {
2606                 return false;
2607             }
2608             //noinspection RedundantIfStatement
2609             if (!span.equals(spec.span)) {
2610                 return false;
2611             }
2612 
2613             return true;
2614         }
2615 
2616         @Override
2617         public int hashCode() {
2618             int result = span.hashCode();
2619             result = 31 * result + alignment.hashCode();
2620             return result;
2621         }
2622     }
2623 
2624     /**
2625      * Return a Spec, {@code spec}, where:
2626      * <ul>
2627      *     <li> {@code spec.span = [start, start + size]} </li>
2628      *     <li> {@code spec.alignment = alignment} </li>
2629      *     <li> {@code spec.weight = weight} </li>
2630      * </ul>
2631      * <p>
2632      * To leave the start index undefined, use the value {@link #UNDEFINED}.
2633      *
2634      * @param start     the start
2635      * @param size      the size
2636      * @param alignment the alignment
2637      * @param weight    the weight
2638      */
2639     public static Spec spec(int start, int size, Alignment alignment, float weight) {
2640         return new Spec(start != UNDEFINED, start, size, alignment, weight);
2641     }
2642 
2643     /**
2644      * Equivalent to: {@code spec(start, 1, alignment, weight)}.
2645      *
2646      * @param start     the start
2647      * @param alignment the alignment
2648      * @param weight    the weight
2649      */
2650     public static Spec spec(int start, Alignment alignment, float weight) {
2651         return spec(start, 1, alignment, weight);
2652     }
2653 
2654     /**
2655      * Equivalent to: {@code spec(start, 1, default_alignment, weight)} -
2656      * where {@code default_alignment} is specified in
2657      * {@link android.widget.GridLayout.LayoutParams}.
2658      *
2659      * @param start  the start
2660      * @param size   the size
2661      * @param weight the weight
2662      */
2663     public static Spec spec(int start, int size, float weight) {
2664         return spec(start, size, UNDEFINED_ALIGNMENT, weight);
2665     }
2666 
2667     /**
2668      * Equivalent to: {@code spec(start, 1, weight)}.
2669      *
2670      * @param start  the start
2671      * @param weight the weight
2672      */
2673     public static Spec spec(int start, float weight) {
2674         return spec(start, 1, weight);
2675     }
2676 
2677     /**
2678      * Equivalent to: {@code spec(start, size, alignment, 0f)}.
2679      *
2680      * @param start     the start
2681      * @param size      the size
2682      * @param alignment the alignment
2683      */
2684     public static Spec spec(int start, int size, Alignment alignment) {
2685         return spec(start, size, alignment, Spec.DEFAULT_WEIGHT);
2686     }
2687 
2688     /**
2689      * Return a Spec, {@code spec}, where:
2690      * <ul>
2691      *     <li> {@code spec.span = [start, start + 1]} </li>
2692      *     <li> {@code spec.alignment = alignment} </li>
2693      * </ul>
2694      * <p>
2695      * To leave the start index undefined, use the value {@link #UNDEFINED}.
2696      *
2697      * @param start     the start index
2698      * @param alignment the alignment
2699      *
2700      * @see #spec(int, int, Alignment)
2701      */
2702     public static Spec spec(int start, Alignment alignment) {
2703         return spec(start, 1, alignment);
2704     }
2705 
2706     /**
2707      * Return a Spec, {@code spec}, where:
2708      * <ul>
2709      *     <li> {@code spec.span = [start, start + size]} </li>
2710      * </ul>
2711      * <p>
2712      * To leave the start index undefined, use the value {@link #UNDEFINED}.
2713      *
2714      * @param start     the start
2715      * @param size      the size
2716      *
2717      * @see #spec(int, Alignment)
2718      */
2719     public static Spec spec(int start, int size) {
2720         return spec(start, size, UNDEFINED_ALIGNMENT);
2721     }
2722 
2723     /**
2724      * Return a Spec, {@code spec}, where:
2725      * <ul>
2726      *     <li> {@code spec.span = [start, start + 1]} </li>
2727      * </ul>
2728      * <p>
2729      * To leave the start index undefined, use the value {@link #UNDEFINED}.
2730      *
2731      * @param start     the start index
2732      *
2733      * @see #spec(int, int)
2734      */
2735     public static Spec spec(int start) {
2736         return spec(start, 1);
2737     }
2738 
2739     /**
2740      * Alignments specify where a view should be placed within a cell group and
2741      * what size it should be.
2742      * <p>
2743      * The {@link LayoutParams} class contains a {@link LayoutParams#rowSpec rowSpec}
2744      * and a {@link LayoutParams#columnSpec columnSpec} each of which contains an
2745      * {@code alignment}. Overall placement of the view in the cell
2746      * group is specified by the two alignments which act along each axis independently.
2747      * <p>
2748      *  The GridLayout class defines the most common alignments used in general layout:
2749      * {@link #TOP}, {@link #LEFT}, {@link #BOTTOM}, {@link #RIGHT}, {@link #START},
2750      * {@link #END}, {@link #CENTER}, {@link #BASELINE} and {@link #FILL}.
2751      */
2752     /*
2753      * An Alignment implementation must define {@link #getAlignmentValue(View, int, int)},
2754      * to return the appropriate value for the type of alignment being defined.
2755      * The enclosing algorithms position the children
2756      * so that the locations defined by the alignment values
2757      * are the same for all of the views in a group.
2758      * <p>
2759      */
2760     public static abstract class Alignment {
2761         Alignment() {
2762         }
2763 
2764         abstract int getGravityOffset(View view, int cellDelta);
2765 
2766         /**
2767          * Returns an alignment value. In the case of vertical alignments the value
2768          * returned should indicate the distance from the top of the view to the
2769          * alignment location.
2770          * For horizontal alignments measurement is made from the left edge of the component.
2771          *
2772          * @param view              the view to which this alignment should be applied
2773          * @param viewSize          the measured size of the view
2774          * @param mode              the basis of alignment: CLIP or OPTICAL
2775          * @return the alignment value
2776          */
2777         abstract int getAlignmentValue(View view, int viewSize, int mode);
2778 
2779         /**
2780          * Returns the size of the view specified by this alignment.
2781          * In the case of vertical alignments this method should return a height; for
2782          * horizontal alignments this method should return the width.
2783          * <p>
2784          * The default implementation returns {@code viewSize}.
2785          *
2786          * @param view              the view to which this alignment should be applied
2787          * @param viewSize          the measured size of the view
2788          * @param cellSize          the size of the cell into which this view will be placed
2789          * @return the aligned size
2790          */
2791         int getSizeInCell(View view, int viewSize, int cellSize) {
2792             return viewSize;
2793         }
2794 
2795         Bounds getBounds() {
2796             return new Bounds();
2797         }
2798     }
2799 
2800     static final Alignment UNDEFINED_ALIGNMENT = new Alignment() {
2801         @Override
2802         int getGravityOffset(View view, int cellDelta) {
2803             return UNDEFINED;
2804         }
2805 
2806         @Override
2807         public int getAlignmentValue(View view, int viewSize, int mode) {
2808             return UNDEFINED;
2809         }
2810     };
2811 
2812     /**
2813      * Indicates that a view should be aligned with the <em>start</em>
2814      * edges of the other views in its cell group.
2815      */
2816     private static final Alignment LEADING = new Alignment() {
2817         @Override
2818         int getGravityOffset(View view, int cellDelta) {
2819             return 0;
2820         }
2821 
2822         @Override
2823         public int getAlignmentValue(View view, int viewSize, int mode) {
2824             return 0;
2825         }
2826     };
2827 
2828     /**
2829      * Indicates that a view should be aligned with the <em>end</em>
2830      * edges of the other views in its cell group.
2831      */
2832     private static final Alignment TRAILING = new Alignment() {
2833         @Override
2834         int getGravityOffset(View view, int cellDelta) {
2835             return cellDelta;
2836         }
2837 
2838         @Override
2839         public int getAlignmentValue(View view, int viewSize, int mode) {
2840             return viewSize;
2841         }
2842     };
2843 
2844     /**
2845      * Indicates that a view should be aligned with the <em>top</em>
2846      * edges of the other views in its cell group.
2847      */
2848     public static final Alignment TOP = LEADING;
2849 
2850     /**
2851      * Indicates that a view should be aligned with the <em>bottom</em>
2852      * edges of the other views in its cell group.
2853      */
2854     public static final Alignment BOTTOM = TRAILING;
2855 
2856     /**
2857      * Indicates that a view should be aligned with the <em>start</em>
2858      * edges of the other views in its cell group.
2859      */
2860     public static final Alignment START = LEADING;
2861 
2862     /**
2863      * Indicates that a view should be aligned with the <em>end</em>
2864      * edges of the other views in its cell group.
2865      */
2866     public static final Alignment END = TRAILING;
2867 
2868     private static Alignment createSwitchingAlignment(final Alignment ltr, final Alignment rtl) {
2869         return new Alignment() {
2870             @Override
2871             int getGravityOffset(View view, int cellDelta) {
2872                 return (!view.isLayoutRtl() ? ltr : rtl).getGravityOffset(view, cellDelta);
2873             }
2874 
2875             @Override
2876             public int getAlignmentValue(View view, int viewSize, int mode) {
2877                 return (!view.isLayoutRtl() ? ltr : rtl).getAlignmentValue(view, viewSize, mode);
2878             }
2879         };
2880     }
2881 
2882     /**
2883      * Indicates that a view should be aligned with the <em>left</em>
2884      * edges of the other views in its cell group.
2885      */
2886     public static final Alignment LEFT = createSwitchingAlignment(START, END);
2887 
2888     /**
2889      * Indicates that a view should be aligned with the <em>right</em>
2890      * edges of the other views in its cell group.
2891      */
2892     public static final Alignment RIGHT = createSwitchingAlignment(END, START);
2893 
2894     /**
2895      * Indicates that a view should be <em>centered</em> with the other views in its cell group.
2896      * This constant may be used in both {@link LayoutParams#rowSpec rowSpecs} and {@link
2897      * LayoutParams#columnSpec columnSpecs}.
2898      */
2899     public static final Alignment CENTER = new Alignment() {
2900         @Override
2901         int getGravityOffset(View view, int cellDelta) {
2902             return cellDelta >> 1;
2903         }
2904 
2905         @Override
2906         public int getAlignmentValue(View view, int viewSize, int mode) {
2907             return viewSize >> 1;
2908         }
2909     };
2910 
2911     /**
2912      * Indicates that a view should be aligned with the <em>baselines</em>
2913      * of the other views in its cell group.
2914      * This constant may only be used as an alignment in {@link LayoutParams#rowSpec rowSpecs}.
2915      *
2916      * @see View#getBaseline()
2917      */
2918     public static final Alignment BASELINE = new Alignment() {
2919         @Override
2920         int getGravityOffset(View view, int cellDelta) {
2921             return 0; // baseline gravity is top
2922         }
2923 
2924         @Override
2925         public int getAlignmentValue(View view, int viewSize, int mode) {
2926             if (view.getVisibility() == GONE) {
2927                 return 0;
2928             }
2929             int baseline = view.getBaseline();
2930             return baseline == -1 ? UNDEFINED : baseline;
2931         }
2932 
2933         @Override
2934         public Bounds getBounds() {
2935             return new Bounds() {
2936                 /*
2937                 In a baseline aligned row in which some components define a baseline
2938                 and some don't, we need a third variable to properly account for all
2939                 the sizes. This tracks the maximum size of all the components -
2940                 including those that don't define a baseline.
2941                 */
2942                 private int size;
2943 
2944                 @Override
2945                 protected void reset() {
2946                     super.reset();
2947                     size = Integer.MIN_VALUE;
2948                 }
2949 
2950                 @Override
2951                 protected void include(int before, int after) {
2952                     super.include(before, after);
2953                     size = max(size, before + after);
2954                 }
2955 
2956                 @Override
2957                 protected int size(boolean min) {
2958                     return max(super.size(min), size);
2959                 }
2960 
2961                 @Override
2962                 protected int getOffset(GridLayout gl, View c, Alignment a, int size, boolean hrz) {
2963                     return max(0, super.getOffset(gl, c, a, size, hrz));
2964                 }
2965             };
2966         }
2967     };
2968 
2969     /**
2970      * Indicates that a view should expanded to fit the boundaries of its cell group.
2971      * This constant may be used in both {@link LayoutParams#rowSpec rowSpecs} and
2972      * {@link LayoutParams#columnSpec columnSpecs}.
2973      */
2974     public static final Alignment FILL = new Alignment() {
2975         @Override
2976         int getGravityOffset(View view, int cellDelta) {
2977             return 0;
2978         }
2979 
2980         @Override
2981         public int getAlignmentValue(View view, int viewSize, int mode) {
2982             return UNDEFINED;
2983         }
2984 
2985         @Override
2986         public int getSizeInCell(View view, int viewSize, int cellSize) {
2987             return cellSize;
2988         }
2989     };
2990 
2991     static boolean canStretch(int flexibility) {
2992         return (flexibility & CAN_STRETCH) != 0;
2993     }
2994 
2995     private static final int INFLEXIBLE = 0;
2996     private static final int CAN_STRETCH = 2;
2997 }
2998