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