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