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