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