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