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