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