GridLayout.java revision 7b7578184567f4e4f0740ce935cc192765410cca
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        if (isLayoutRtl()) {
828            int width = getWidth();
829            graphics.drawLine(width - x1, y1, width - x2, y2, paint);
830        } else {
831            graphics.drawLine(x1, y1, x2, y2, paint);
832        }
833    }
834
835    /**
836     * @hide
837     */
838    @Override
839    protected void onDebugDrawMargins(Canvas canvas, Paint paint) {
840        // Apply defaults, so as to remove UNDEFINED values
841        LayoutParams lp = new LayoutParams();
842        for (int i = 0; i < getChildCount(); i++) {
843            View c = getChildAt(i);
844            lp.setMargins(
845                    getMargin1(c, true, true),
846                    getMargin1(c, false, true),
847                    getMargin1(c, true, false),
848                    getMargin1(c, false, false));
849            lp.onDebugDraw(c, canvas, paint);
850        }
851    }
852
853    /**
854     * @hide
855     */
856    @Override
857    protected void onDebugDraw(Canvas canvas) {
858        Paint paint = new Paint();
859        paint.setStyle(Paint.Style.STROKE);
860        paint.setColor(Color.argb(50, 255, 255, 255));
861
862        Insets insets = getOpticalInsets();
863
864        int top    =               getPaddingTop()    + insets.top;
865        int left   =               getPaddingLeft()   + insets.left;
866        int right  = getWidth()  - getPaddingRight()  - insets.right;
867        int bottom = getHeight() - getPaddingBottom() - insets.bottom;
868
869        int[] xs = horizontalAxis.locations;
870        if (xs != null) {
871            for (int i = 0, length = xs.length; i < length; i++) {
872                int x = left + xs[i];
873                drawLine(canvas, x, top, x, bottom, paint);
874            }
875        }
876
877        int[] ys = verticalAxis.locations;
878        if (ys != null) {
879            for (int i = 0, length = ys.length; i < length; i++) {
880                int y = top + ys[i];
881                drawLine(canvas, left, y, right, y, paint);
882            }
883        }
884
885        super.onDebugDraw(canvas);
886    }
887
888    // Add/remove
889
890    /**
891     * @hide
892     */
893    @Override
894    protected void onViewAdded(View child) {
895        super.onViewAdded(child);
896        invalidateStructure();
897    }
898
899    /**
900     * @hide
901     */
902    @Override
903    protected void onViewRemoved(View child) {
904        super.onViewRemoved(child);
905        invalidateStructure();
906    }
907
908    /**
909     * We need to call invalidateStructure() when a child's GONE flag changes state.
910     * This implementation is a catch-all, invalidating on any change in the visibility flags.
911     *
912     * @hide
913     */
914    @Override
915    protected void onChildVisibilityChanged(View child, int oldVisibility, int newVisibility) {
916        super.onChildVisibilityChanged(child, oldVisibility, newVisibility);
917        if (oldVisibility == GONE || newVisibility == GONE) {
918            invalidateStructure();
919        }
920    }
921
922    private int computeLayoutParamsHashCode() {
923        int result = 1;
924        for (int i = 0, N = getChildCount(); i < N; i++) {
925            View c = getChildAt(i);
926            if (c.getVisibility() == View.GONE) continue;
927            LayoutParams lp = (LayoutParams) c.getLayoutParams();
928            result = 31 * result + lp.hashCode();
929        }
930        return result;
931    }
932
933    private void consistencyCheck() {
934        if (lastLayoutParamsHashCode == UNINITIALIZED_HASH) {
935            validateLayoutParams();
936            lastLayoutParamsHashCode = computeLayoutParamsHashCode();
937        } else if (lastLayoutParamsHashCode != computeLayoutParamsHashCode()) {
938            Log.w(TAG, "The fields of some layout parameters were modified in between layout " +
939                    "operations. Check the javadoc for GridLayout.LayoutParams#rowSpec.");
940            invalidateStructure();
941            consistencyCheck();
942        }
943    }
944
945    // Measurement
946
947    private void measureChildWithMargins2(View child, int parentWidthSpec, int parentHeightSpec,
948            int childWidth, int childHeight) {
949        int childWidthSpec = getChildMeasureSpec(parentWidthSpec,
950                mPaddingLeft + mPaddingRight + getTotalMargin(child, true), childWidth);
951        int childHeightSpec = getChildMeasureSpec(parentHeightSpec,
952                mPaddingTop + mPaddingBottom + getTotalMargin(child, false), childHeight);
953        child.measure(childWidthSpec, childHeightSpec);
954    }
955
956    private void measureChildrenWithMargins(int widthSpec, int heightSpec, boolean firstPass) {
957        for (int i = 0, N = getChildCount(); i < N; i++) {
958            View c = getChildAt(i);
959            if (c.getVisibility() == View.GONE) continue;
960            LayoutParams lp = getLayoutParams(c);
961            if (firstPass) {
962                measureChildWithMargins2(c, widthSpec, heightSpec, lp.width, lp.height);
963            } else {
964                boolean horizontal = (orientation == HORIZONTAL);
965                Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
966                if (spec.alignment == FILL) {
967                    Interval span = spec.span;
968                    Axis axis = horizontal ? horizontalAxis : verticalAxis;
969                    int[] locations = axis.getLocations();
970                    int cellSize = locations[span.max] - locations[span.min];
971                    int viewSize = cellSize - getTotalMargin(c, horizontal);
972                    if (horizontal) {
973                        measureChildWithMargins2(c, widthSpec, heightSpec, viewSize, lp.height);
974                    } else {
975                        measureChildWithMargins2(c, widthSpec, heightSpec, lp.width, viewSize);
976                    }
977                }
978            }
979        }
980    }
981
982    @Override
983    protected void onMeasure(int widthSpec, int heightSpec) {
984        consistencyCheck();
985
986        /** If we have been called by {@link View#measure(int, int)}, one of width or height
987         *  is  likely to have changed. We must invalidate if so. */
988        invalidateValues();
989
990        measureChildrenWithMargins(widthSpec, heightSpec, true);
991
992        int width, height;
993
994        // Use the orientation property to decide which axis should be laid out first.
995        if (orientation == HORIZONTAL) {
996            width = horizontalAxis.getMeasure(widthSpec);
997            measureChildrenWithMargins(widthSpec, heightSpec, false);
998            height = verticalAxis.getMeasure(heightSpec);
999        } else {
1000            height = verticalAxis.getMeasure(heightSpec);
1001            measureChildrenWithMargins(widthSpec, heightSpec, false);
1002            width = horizontalAxis.getMeasure(widthSpec);
1003        }
1004
1005        int hPadding = getPaddingLeft() + getPaddingRight();
1006        int vPadding = getPaddingTop() + getPaddingBottom();
1007
1008        int measuredWidth = Math.max(hPadding + width, getSuggestedMinimumWidth());
1009        int measuredHeight = Math.max(vPadding + height, getSuggestedMinimumHeight());
1010
1011        setMeasuredDimension(
1012                resolveSizeAndState(measuredWidth, widthSpec, 0),
1013                resolveSizeAndState(measuredHeight, heightSpec, 0));
1014    }
1015
1016    private int getMeasurement(View c, boolean horizontal) {
1017        return horizontal ? c.getMeasuredWidth() : c.getMeasuredHeight();
1018    }
1019
1020    final int getMeasurementIncludingMargin(View c, boolean horizontal) {
1021        if (c.getVisibility() == View.GONE) {
1022            return 0;
1023        }
1024        return getMeasurement(c, horizontal) + getTotalMargin(c, horizontal);
1025    }
1026
1027    @Override
1028    public void requestLayout() {
1029        super.requestLayout();
1030        invalidateValues();
1031    }
1032
1033    final Alignment getAlignment(Alignment alignment, boolean horizontal) {
1034        return (alignment != UNDEFINED_ALIGNMENT) ? alignment :
1035                (horizontal ? START : BASELINE);
1036    }
1037
1038    // Layout container
1039
1040    /**
1041     * {@inheritDoc}
1042     */
1043    /*
1044     The layout operation is implemented by delegating the heavy lifting to the
1045     to the mHorizontalAxis and mVerticalAxis instances of the internal Axis class.
1046     Together they compute the locations of the vertical and horizontal lines of
1047     the grid (respectively!).
1048
1049     This method is then left with the simpler task of applying margins, gravity
1050     and sizing to each child view and then placing it in its cell.
1051     */
1052    @Override
1053    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
1054        consistencyCheck();
1055
1056        int targetWidth = right - left;
1057        int targetHeight = bottom - top;
1058
1059        int paddingLeft = getPaddingLeft();
1060        int paddingTop = getPaddingTop();
1061        int paddingRight = getPaddingRight();
1062        int paddingBottom = getPaddingBottom();
1063
1064        horizontalAxis.layout(targetWidth - paddingLeft - paddingRight);
1065        verticalAxis.layout(targetHeight - paddingTop - paddingBottom);
1066
1067        int[] hLocations = horizontalAxis.getLocations();
1068        int[] vLocations = verticalAxis.getLocations();
1069
1070        for (int i = 0, N = getChildCount(); i < N; i++) {
1071            View c = getChildAt(i);
1072            if (c.getVisibility() == View.GONE) continue;
1073            LayoutParams lp = getLayoutParams(c);
1074            Spec columnSpec = lp.columnSpec;
1075            Spec rowSpec = lp.rowSpec;
1076
1077            Interval colSpan = columnSpec.span;
1078            Interval rowSpan = rowSpec.span;
1079
1080            int x1 = hLocations[colSpan.min];
1081            int y1 = vLocations[rowSpan.min];
1082
1083            int x2 = hLocations[colSpan.max];
1084            int y2 = vLocations[rowSpan.max];
1085
1086            int cellWidth = x2 - x1;
1087            int cellHeight = y2 - y1;
1088
1089            int pWidth = getMeasurement(c, true);
1090            int pHeight = getMeasurement(c, false);
1091
1092            Alignment hAlign = getAlignment(columnSpec.alignment, true);
1093            Alignment vAlign = getAlignment(rowSpec.alignment, false);
1094
1095            Bounds boundsX = horizontalAxis.getGroupBounds().getValue(i);
1096            Bounds boundsY = verticalAxis.getGroupBounds().getValue(i);
1097
1098            // Gravity offsets: the location of the alignment group relative to its cell group.
1099            int gravityOffsetX = hAlign.getGravityOffset(c, cellWidth - boundsX.size(true));
1100            int gravityOffsetY = vAlign.getGravityOffset(c, cellHeight - boundsY.size(true));
1101
1102            int leftMargin = getMargin(c, true, true);
1103            int topMargin = getMargin(c, false, true);
1104            int rightMargin = getMargin(c, true, false);
1105            int bottomMargin = getMargin(c, false, false);
1106
1107            int sumMarginsX = leftMargin + rightMargin;
1108            int sumMarginsY = topMargin + bottomMargin;
1109
1110            // Alignment offsets: the location of the view relative to its alignment group.
1111            int alignmentOffsetX = boundsX.getOffset(this, c, hAlign, pWidth + sumMarginsX, true);
1112            int alignmentOffsetY = boundsY.getOffset(this, c, vAlign, pHeight + sumMarginsY, false);
1113
1114            int width = hAlign.getSizeInCell(c, pWidth, cellWidth - sumMarginsX);
1115            int height = vAlign.getSizeInCell(c, pHeight, cellHeight - sumMarginsY);
1116
1117            int dx = x1 + gravityOffsetX + alignmentOffsetX;
1118
1119            int cx = !isLayoutRtl() ? paddingLeft + leftMargin + dx :
1120                    targetWidth - width - paddingRight - rightMargin - dx;
1121            int cy = paddingTop + y1 + gravityOffsetY + alignmentOffsetY + topMargin;
1122
1123            if (width != c.getMeasuredWidth() || height != c.getMeasuredHeight()) {
1124                c.measure(makeMeasureSpec(width, EXACTLY), makeMeasureSpec(height, EXACTLY));
1125            }
1126            c.layout(cx, cy, cx + width, cy + height);
1127        }
1128    }
1129
1130    @Override
1131    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
1132        super.onInitializeAccessibilityEvent(event);
1133        event.setClassName(GridLayout.class.getName());
1134    }
1135
1136    @Override
1137    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
1138        super.onInitializeAccessibilityNodeInfo(info);
1139        info.setClassName(GridLayout.class.getName());
1140    }
1141
1142    // Inner classes
1143
1144    /*
1145     This internal class houses the algorithm for computing the locations of grid lines;
1146     along either the horizontal or vertical axis. A GridLayout uses two instances of this class -
1147     distinguished by the "horizontal" flag which is true for the horizontal axis and false
1148     for the vertical one.
1149     */
1150    final class Axis {
1151        private static final int NEW = 0;
1152        private static final int PENDING = 1;
1153        private static final int COMPLETE = 2;
1154
1155        public final boolean horizontal;
1156
1157        public int definedCount = UNDEFINED;
1158        private int maxIndex = UNDEFINED;
1159
1160        PackedMap<Spec, Bounds> groupBounds;
1161        public boolean groupBoundsValid = false;
1162
1163        PackedMap<Interval, MutableInt> forwardLinks;
1164        public boolean forwardLinksValid = false;
1165
1166        PackedMap<Interval, MutableInt> backwardLinks;
1167        public boolean backwardLinksValid = false;
1168
1169        public int[] leadingMargins;
1170        public boolean leadingMarginsValid = false;
1171
1172        public int[] trailingMargins;
1173        public boolean trailingMarginsValid = false;
1174
1175        public Arc[] arcs;
1176        public boolean arcsValid = false;
1177
1178        public int[] locations;
1179        public boolean locationsValid = false;
1180
1181        boolean orderPreserved = DEFAULT_ORDER_PRESERVED;
1182
1183        private MutableInt parentMin = new MutableInt(0);
1184        private MutableInt parentMax = new MutableInt(-MAX_SIZE);
1185
1186        private Axis(boolean horizontal) {
1187            this.horizontal = horizontal;
1188        }
1189
1190        private int calculateMaxIndex() {
1191            // the number Integer.MIN_VALUE + 1 comes up in undefined cells
1192            int result = -1;
1193            for (int i = 0, N = getChildCount(); i < N; i++) {
1194                View c = getChildAt(i);
1195                LayoutParams params = getLayoutParams(c);
1196                Spec spec = horizontal ? params.columnSpec : params.rowSpec;
1197                Interval span = spec.span;
1198                result = max(result, span.min);
1199                result = max(result, span.max);
1200                result = max(result, span.size());
1201            }
1202            return result == -1 ? UNDEFINED : result;
1203        }
1204
1205        private int getMaxIndex() {
1206            if (maxIndex == UNDEFINED) {
1207                maxIndex = max(0, calculateMaxIndex()); // use zero when there are no children
1208            }
1209            return maxIndex;
1210        }
1211
1212        public int getCount() {
1213            return max(definedCount, getMaxIndex());
1214        }
1215
1216        public void setCount(int count) {
1217            if (count != UNDEFINED && count < getMaxIndex()) {
1218                handleInvalidParams((horizontal ? "column" : "row") +
1219                        "Count must be greater than or equal to the maximum of all grid indices " +
1220                        "(and spans) defined in the LayoutParams of each child");
1221            }
1222            this.definedCount = count;
1223        }
1224
1225        public boolean isOrderPreserved() {
1226            return orderPreserved;
1227        }
1228
1229        public void setOrderPreserved(boolean orderPreserved) {
1230            this.orderPreserved = orderPreserved;
1231            invalidateStructure();
1232        }
1233
1234        private PackedMap<Spec, Bounds> createGroupBounds() {
1235            Assoc<Spec, Bounds> assoc = Assoc.of(Spec.class, Bounds.class);
1236            for (int i = 0, N = getChildCount(); i < N; i++) {
1237                View c = getChildAt(i);
1238                LayoutParams lp = getLayoutParams(c);
1239                Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
1240                Bounds bounds = getAlignment(spec.alignment, horizontal).getBounds();
1241                assoc.put(spec, bounds);
1242            }
1243            return assoc.pack();
1244        }
1245
1246        private void computeGroupBounds() {
1247            Bounds[] values = groupBounds.values;
1248            for (int i = 0; i < values.length; i++) {
1249                values[i].reset();
1250            }
1251            for (int i = 0, N = getChildCount(); i < N; i++) {
1252                View c = getChildAt(i);
1253                LayoutParams lp = getLayoutParams(c);
1254                Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
1255                groupBounds.getValue(i).include(GridLayout.this, c, spec, this);
1256            }
1257        }
1258
1259        public PackedMap<Spec, Bounds> getGroupBounds() {
1260            if (groupBounds == null) {
1261                groupBounds = createGroupBounds();
1262            }
1263            if (!groupBoundsValid) {
1264                computeGroupBounds();
1265                groupBoundsValid = true;
1266            }
1267            return groupBounds;
1268        }
1269
1270        // Add values computed by alignment - taking the max of all alignments in each span
1271        private PackedMap<Interval, MutableInt> createLinks(boolean min) {
1272            Assoc<Interval, MutableInt> result = Assoc.of(Interval.class, MutableInt.class);
1273            Spec[] keys = getGroupBounds().keys;
1274            for (int i = 0, N = keys.length; i < N; i++) {
1275                Interval span = min ? keys[i].span : keys[i].span.inverse();
1276                result.put(span, new MutableInt());
1277            }
1278            return result.pack();
1279        }
1280
1281        private void computeLinks(PackedMap<Interval, MutableInt> links, boolean min) {
1282            MutableInt[] spans = links.values;
1283            for (int i = 0; i < spans.length; i++) {
1284                spans[i].reset();
1285            }
1286
1287            // Use getter to trigger a re-evaluation
1288            Bounds[] bounds = getGroupBounds().values;
1289            for (int i = 0; i < bounds.length; i++) {
1290                int size = bounds[i].size(min);
1291                MutableInt valueHolder = links.getValue(i);
1292                // this effectively takes the max() of the minima and the min() of the maxima
1293                valueHolder.value = max(valueHolder.value, min ? size : -size);
1294            }
1295        }
1296
1297        private PackedMap<Interval, MutableInt> getForwardLinks() {
1298            if (forwardLinks == null) {
1299                forwardLinks = createLinks(true);
1300            }
1301            if (!forwardLinksValid) {
1302                computeLinks(forwardLinks, true);
1303                forwardLinksValid = true;
1304            }
1305            return forwardLinks;
1306        }
1307
1308        private PackedMap<Interval, MutableInt> getBackwardLinks() {
1309            if (backwardLinks == null) {
1310                backwardLinks = createLinks(false);
1311            }
1312            if (!backwardLinksValid) {
1313                computeLinks(backwardLinks, false);
1314                backwardLinksValid = true;
1315            }
1316            return backwardLinks;
1317        }
1318
1319        private void include(List<Arc> arcs, Interval key, MutableInt size,
1320                boolean ignoreIfAlreadyPresent) {
1321            /*
1322            Remove self referential links.
1323            These appear:
1324                . as parental constraints when GridLayout has no children
1325                . when components have been marked as GONE
1326            */
1327            if (key.size() == 0) {
1328                return;
1329            }
1330            // this bit below should really be computed outside here -
1331            // its just to stop default (row/col > 0) constraints obliterating valid entries
1332            if (ignoreIfAlreadyPresent) {
1333                for (Arc arc : arcs) {
1334                    Interval span = arc.span;
1335                    if (span.equals(key)) {
1336                        return;
1337                    }
1338                }
1339            }
1340            arcs.add(new Arc(key, size));
1341        }
1342
1343        private void include(List<Arc> arcs, Interval key, MutableInt size) {
1344            include(arcs, key, size, true);
1345        }
1346
1347        // Group arcs by their first vertex, returning an array of arrays.
1348        // This is linear in the number of arcs.
1349        Arc[][] groupArcsByFirstVertex(Arc[] arcs) {
1350            int N = getCount() + 1; // the number of vertices
1351            Arc[][] result = new Arc[N][];
1352            int[] sizes = new int[N];
1353            for (Arc arc : arcs) {
1354                sizes[arc.span.min]++;
1355            }
1356            for (int i = 0; i < sizes.length; i++) {
1357                result[i] = new Arc[sizes[i]];
1358            }
1359            // reuse the sizes array to hold the current last elements as we insert each arc
1360            Arrays.fill(sizes, 0);
1361            for (Arc arc : arcs) {
1362                int i = arc.span.min;
1363                result[i][sizes[i]++] = arc;
1364            }
1365
1366            return result;
1367        }
1368
1369        private Arc[] topologicalSort(final Arc[] arcs) {
1370            return new Object() {
1371                Arc[] result = new Arc[arcs.length];
1372                int cursor = result.length - 1;
1373                Arc[][] arcsByVertex = groupArcsByFirstVertex(arcs);
1374                int[] visited = new int[getCount() + 1];
1375
1376                void walk(int loc) {
1377                    switch (visited[loc]) {
1378                        case NEW: {
1379                            visited[loc] = PENDING;
1380                            for (Arc arc : arcsByVertex[loc]) {
1381                                walk(arc.span.max);
1382                                result[cursor--] = arc;
1383                            }
1384                            visited[loc] = COMPLETE;
1385                            break;
1386                        }
1387                        case PENDING: {
1388                            // le singe est dans l'arbre
1389                            assert false;
1390                            break;
1391                        }
1392                        case COMPLETE: {
1393                            break;
1394                        }
1395                    }
1396                }
1397
1398                Arc[] sort() {
1399                    for (int loc = 0, N = arcsByVertex.length; loc < N; loc++) {
1400                        walk(loc);
1401                    }
1402                    assert cursor == -1;
1403                    return result;
1404                }
1405            }.sort();
1406        }
1407
1408        private Arc[] topologicalSort(List<Arc> arcs) {
1409            return topologicalSort(arcs.toArray(new Arc[arcs.size()]));
1410        }
1411
1412        private void addComponentSizes(List<Arc> result, PackedMap<Interval, MutableInt> links) {
1413            for (int i = 0; i < links.keys.length; i++) {
1414                Interval key = links.keys[i];
1415                include(result, key, links.values[i], false);
1416            }
1417        }
1418
1419        private Arc[] createArcs() {
1420            List<Arc> mins = new ArrayList<Arc>();
1421            List<Arc> maxs = new ArrayList<Arc>();
1422
1423            // Add the minimum values from the components.
1424            addComponentSizes(mins, getForwardLinks());
1425            // Add the maximum values from the components.
1426            addComponentSizes(maxs, getBackwardLinks());
1427
1428            // Add ordering constraints to prevent row/col sizes from going negative
1429            if (orderPreserved) {
1430                // Add a constraint for every row/col
1431                for (int i = 0; i < getCount(); i++) {
1432                    include(mins, new Interval(i, i + 1), new MutableInt(0));
1433                }
1434            }
1435
1436            // Add the container constraints. Use the version of include that allows
1437            // duplicate entries in case a child spans the entire grid.
1438            int N = getCount();
1439            include(mins, new Interval(0, N), parentMin, false);
1440            include(maxs, new Interval(N, 0), parentMax, false);
1441
1442            // Sort
1443            Arc[] sMins = topologicalSort(mins);
1444            Arc[] sMaxs = topologicalSort(maxs);
1445
1446            return append(sMins, sMaxs);
1447        }
1448
1449        private void computeArcs() {
1450            // getting the links validates the values that are shared by the arc list
1451            getForwardLinks();
1452            getBackwardLinks();
1453        }
1454
1455        public Arc[] getArcs() {
1456            if (arcs == null) {
1457                arcs = createArcs();
1458            }
1459            if (!arcsValid) {
1460                computeArcs();
1461                arcsValid = true;
1462            }
1463            return arcs;
1464        }
1465
1466        private boolean relax(int[] locations, Arc entry) {
1467            if (!entry.valid) {
1468                return false;
1469            }
1470            Interval span = entry.span;
1471            int u = span.min;
1472            int v = span.max;
1473            int value = entry.value.value;
1474            int candidate = locations[u] + value;
1475            if (candidate > locations[v]) {
1476                locations[v] = candidate;
1477                return true;
1478            }
1479            return false;
1480        }
1481
1482        private void init(int[] locations) {
1483            Arrays.fill(locations, 0);
1484        }
1485
1486        private String arcsToString(List<Arc> arcs) {
1487            String var = horizontal ? "x" : "y";
1488            StringBuilder result = new StringBuilder();
1489            boolean first = true;
1490            for (Arc arc : arcs) {
1491                if (first) {
1492                    first = false;
1493                } else {
1494                    result = result.append(", ");
1495                }
1496                int src = arc.span.min;
1497                int dst = arc.span.max;
1498                int value = arc.value.value;
1499                result.append((src < dst) ?
1500                        var + dst + "-" + var + src + ">=" + value :
1501                        var + src + "-" + var + dst + "<=" + -value);
1502
1503            }
1504            return result.toString();
1505        }
1506
1507        private void logError(String axisName, Arc[] arcs, boolean[] culprits0) {
1508            List<Arc> culprits = new ArrayList<Arc>();
1509            List<Arc> removed = new ArrayList<Arc>();
1510            for (int c = 0; c < arcs.length; c++) {
1511                Arc arc = arcs[c];
1512                if (culprits0[c]) {
1513                    culprits.add(arc);
1514                }
1515                if (!arc.valid) {
1516                    removed.add(arc);
1517                }
1518            }
1519            Log.d(TAG, axisName + " constraints: " + arcsToString(culprits) + " are inconsistent; "
1520                    + "permanently removing: " + arcsToString(removed) + ". ");
1521        }
1522
1523        /*
1524        Bellman-Ford variant - modified to reduce typical running time from O(N^2) to O(N)
1525
1526        GridLayout converts its requirements into a system of linear constraints of the
1527        form:
1528
1529        x[i] - x[j] < a[k]
1530
1531        Where the x[i] are variables and the a[k] are constants.
1532
1533        For example, if the variables were instead labeled x, y, z we might have:
1534
1535            x - y < 17
1536            y - z < 23
1537            z - x < 42
1538
1539        This is a special case of the Linear Programming problem that is, in turn,
1540        equivalent to the single-source shortest paths problem on a digraph, for
1541        which the O(n^2) Bellman-Ford algorithm the most commonly used general solution.
1542        */
1543        private void solve(Arc[] arcs, int[] locations) {
1544            String axisName = horizontal ? "horizontal" : "vertical";
1545            int N = getCount() + 1; // The number of vertices is the number of columns/rows + 1.
1546            boolean[] originalCulprits = null;
1547
1548            for (int p = 0; p < arcs.length; p++) {
1549                init(locations);
1550
1551                // We take one extra pass over traditional Bellman-Ford (and omit their final step)
1552                for (int i = 0; i < N; i++) {
1553                    boolean changed = false;
1554                    for (int j = 0, length = arcs.length; j < length; j++) {
1555                        changed |= relax(locations, arcs[j]);
1556                    }
1557                    if (!changed) {
1558                        if (originalCulprits != null) {
1559                            logError(axisName, arcs, originalCulprits);
1560                        }
1561                        return;
1562                    }
1563                }
1564
1565                boolean[] culprits = new boolean[arcs.length];
1566                for (int i = 0; i < N; i++) {
1567                    for (int j = 0, length = arcs.length; j < length; j++) {
1568                        culprits[j] |= relax(locations, arcs[j]);
1569                    }
1570                }
1571
1572                if (p == 0) {
1573                    originalCulprits = culprits;
1574                }
1575
1576                for (int i = 0; i < arcs.length; i++) {
1577                    if (culprits[i]) {
1578                        Arc arc = arcs[i];
1579                        // Only remove max values, min values alone cannot be inconsistent
1580                        if (arc.span.min < arc.span.max) {
1581                            continue;
1582                        }
1583                        arc.valid = false;
1584                        break;
1585                    }
1586                }
1587            }
1588        }
1589
1590        private void computeMargins(boolean leading) {
1591            int[] margins = leading ? leadingMargins : trailingMargins;
1592            for (int i = 0, N = getChildCount(); i < N; i++) {
1593                View c = getChildAt(i);
1594                if (c.getVisibility() == View.GONE) continue;
1595                LayoutParams lp = getLayoutParams(c);
1596                Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
1597                Interval span = spec.span;
1598                int index = leading ? span.min : span.max;
1599                margins[index] = max(margins[index], getMargin1(c, horizontal, leading));
1600            }
1601        }
1602
1603        // External entry points
1604
1605        public int[] getLeadingMargins() {
1606            if (leadingMargins == null) {
1607                leadingMargins = new int[getCount() + 1];
1608            }
1609            if (!leadingMarginsValid) {
1610                computeMargins(true);
1611                leadingMarginsValid = true;
1612            }
1613            return leadingMargins;
1614        }
1615
1616        public int[] getTrailingMargins() {
1617            if (trailingMargins == null) {
1618                trailingMargins = new int[getCount() + 1];
1619            }
1620            if (!trailingMarginsValid) {
1621                computeMargins(false);
1622                trailingMarginsValid = true;
1623            }
1624            return trailingMargins;
1625        }
1626
1627        private void computeLocations(int[] a) {
1628            solve(getArcs(), a);
1629            if (!orderPreserved) {
1630                // Solve returns the smallest solution to the constraint system for which all
1631                // values are positive. One value is therefore zero - though if the row/col
1632                // order is not preserved this may not be the first vertex. For consistency,
1633                // translate all the values so that they measure the distance from a[0]; the
1634                // leading edge of the parent. After this transformation some values may be
1635                // negative.
1636                int a0 = a[0];
1637                for (int i = 0, N = a.length; i < N; i++) {
1638                    a[i] = a[i] - a0;
1639                }
1640            }
1641        }
1642
1643        public int[] getLocations() {
1644            if (locations == null) {
1645                int N = getCount() + 1;
1646                locations = new int[N];
1647            }
1648            if (!locationsValid) {
1649                computeLocations(locations);
1650                locationsValid = true;
1651            }
1652            return locations;
1653        }
1654
1655        private int size(int[] locations) {
1656            // The parental edges are attached to vertices 0 and N - even when order is not
1657            // being preserved and other vertices fall outside this range. Measure the distance
1658            // between vertices 0 and N, assuming that locations[0] = 0.
1659            return locations[getCount()];
1660        }
1661
1662        private void setParentConstraints(int min, int max) {
1663            parentMin.value = min;
1664            parentMax.value = -max;
1665            locationsValid = false;
1666        }
1667
1668        private int getMeasure(int min, int max) {
1669            setParentConstraints(min, max);
1670            return size(getLocations());
1671        }
1672
1673        public int getMeasure(int measureSpec) {
1674            int mode = MeasureSpec.getMode(measureSpec);
1675            int size = MeasureSpec.getSize(measureSpec);
1676            switch (mode) {
1677                case MeasureSpec.UNSPECIFIED: {
1678                    return getMeasure(0, MAX_SIZE);
1679                }
1680                case MeasureSpec.EXACTLY: {
1681                    return getMeasure(size, size);
1682                }
1683                case MeasureSpec.AT_MOST: {
1684                    return getMeasure(0, size);
1685                }
1686                default: {
1687                    assert false;
1688                    return 0;
1689                }
1690            }
1691        }
1692
1693        public void layout(int size) {
1694            setParentConstraints(size, size);
1695            getLocations();
1696        }
1697
1698        public void invalidateStructure() {
1699            maxIndex = UNDEFINED;
1700
1701            groupBounds = null;
1702            forwardLinks = null;
1703            backwardLinks = null;
1704
1705            leadingMargins = null;
1706            trailingMargins = null;
1707            arcs = null;
1708
1709            locations = null;
1710
1711            invalidateValues();
1712        }
1713
1714        public void invalidateValues() {
1715            groupBoundsValid = false;
1716            forwardLinksValid = false;
1717            backwardLinksValid = false;
1718
1719            leadingMarginsValid = false;
1720            trailingMarginsValid = false;
1721            arcsValid = false;
1722
1723            locationsValid = false;
1724        }
1725    }
1726
1727    /**
1728     * Layout information associated with each of the children of a GridLayout.
1729     * <p>
1730     * GridLayout supports both row and column spanning and arbitrary forms of alignment within
1731     * each cell group. The fundamental parameters associated with each cell group are
1732     * gathered into their vertical and horizontal components and stored
1733     * in the {@link #rowSpec} and {@link #columnSpec} layout parameters.
1734     * {@link GridLayout.Spec Specs} are immutable structures
1735     * and may be shared between the layout parameters of different children.
1736     * <p>
1737     * The row and column specs contain the leading and trailing indices along each axis
1738     * and together specify the four grid indices that delimit the cells of this cell group.
1739     * <p>
1740     * The  alignment properties of the row and column specs together specify
1741     * both aspects of alignment within the cell group. It is also possible to specify a child's
1742     * alignment within its cell group by using the {@link GridLayout.LayoutParams#setGravity(int)}
1743     * method.
1744     *
1745     * <h4>WRAP_CONTENT and MATCH_PARENT</h4>
1746     *
1747     * Because the default values of the {@link #width} and {@link #height}
1748     * properties are both {@link #WRAP_CONTENT}, this value never needs to be explicitly
1749     * declared in the layout parameters of GridLayout's children. In addition,
1750     * GridLayout does not distinguish the special size value {@link #MATCH_PARENT} from
1751     * {@link #WRAP_CONTENT}. A component's ability to expand to the size of the parent is
1752     * instead controlled by the principle of <em>flexibility</em>,
1753     * as discussed in {@link GridLayout}.
1754     *
1755     * <h4>Summary</h4>
1756     *
1757     * You should not need to use either of the special size values:
1758     * {@code WRAP_CONTENT} or {@code MATCH_PARENT} when configuring the children of
1759     * a GridLayout.
1760     *
1761     * <h4>Default values</h4>
1762     *
1763     * <ul>
1764     *     <li>{@link #width} = {@link #WRAP_CONTENT}</li>
1765     *     <li>{@link #height} = {@link #WRAP_CONTENT}</li>
1766     *     <li>{@link #topMargin} = 0 when
1767     *          {@link GridLayout#setUseDefaultMargins(boolean) useDefaultMargins} is
1768     *          {@code false}; otherwise {@link #UNDEFINED}, to
1769     *          indicate that a default value should be computed on demand. </li>
1770     *     <li>{@link #leftMargin} = 0 when
1771     *          {@link GridLayout#setUseDefaultMargins(boolean) useDefaultMargins} is
1772     *          {@code false}; otherwise {@link #UNDEFINED}, to
1773     *          indicate that a default value should be computed on demand. </li>
1774     *     <li>{@link #bottomMargin} = 0 when
1775     *          {@link GridLayout#setUseDefaultMargins(boolean) useDefaultMargins} is
1776     *          {@code false}; otherwise {@link #UNDEFINED}, to
1777     *          indicate that a default value should be computed on demand. </li>
1778     *     <li>{@link #rightMargin} = 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 #rowSpec}<code>.row</code> = {@link #UNDEFINED} </li>
1783     *     <li>{@link #rowSpec}<code>.rowSpan</code> = 1 </li>
1784     *     <li>{@link #rowSpec}<code>.alignment</code> = {@link #BASELINE} </li>
1785     *     <li>{@link #columnSpec}<code>.column</code> = {@link #UNDEFINED} </li>
1786     *     <li>{@link #columnSpec}<code>.columnSpan</code> = 1 </li>
1787     *     <li>{@link #columnSpec}<code>.alignment</code> = {@link #START} </li>
1788     * </ul>
1789     *
1790     * See {@link GridLayout} for a more complete description of the conventions
1791     * used by GridLayout in the interpretation of the properties of this class.
1792     *
1793     * @attr ref android.R.styleable#GridLayout_Layout_layout_row
1794     * @attr ref android.R.styleable#GridLayout_Layout_layout_rowSpan
1795     * @attr ref android.R.styleable#GridLayout_Layout_layout_column
1796     * @attr ref android.R.styleable#GridLayout_Layout_layout_columnSpan
1797     * @attr ref android.R.styleable#GridLayout_Layout_layout_gravity
1798     */
1799    public static class LayoutParams extends MarginLayoutParams {
1800
1801        // Default values
1802
1803        private static final int DEFAULT_WIDTH = WRAP_CONTENT;
1804        private static final int DEFAULT_HEIGHT = WRAP_CONTENT;
1805        private static final int DEFAULT_MARGIN = UNDEFINED;
1806        private static final int DEFAULT_ROW = UNDEFINED;
1807        private static final int DEFAULT_COLUMN = UNDEFINED;
1808        private static final Interval DEFAULT_SPAN = new Interval(UNDEFINED, UNDEFINED + 1);
1809        private static final int DEFAULT_SPAN_SIZE = DEFAULT_SPAN.size();
1810
1811        // TypedArray indices
1812
1813        private static final int MARGIN = R.styleable.ViewGroup_MarginLayout_layout_margin;
1814        private static final int LEFT_MARGIN = R.styleable.ViewGroup_MarginLayout_layout_marginLeft;
1815        private static final int TOP_MARGIN = R.styleable.ViewGroup_MarginLayout_layout_marginTop;
1816        private static final int RIGHT_MARGIN =
1817                R.styleable.ViewGroup_MarginLayout_layout_marginRight;
1818        private static final int BOTTOM_MARGIN =
1819                R.styleable.ViewGroup_MarginLayout_layout_marginBottom;
1820
1821        private static final int COLUMN = R.styleable.GridLayout_Layout_layout_column;
1822        private static final int COLUMN_SPAN = R.styleable.GridLayout_Layout_layout_columnSpan;
1823
1824        private static final int ROW = R.styleable.GridLayout_Layout_layout_row;
1825        private static final int ROW_SPAN = R.styleable.GridLayout_Layout_layout_rowSpan;
1826
1827        private static final int GRAVITY = R.styleable.GridLayout_Layout_layout_gravity;
1828
1829        // Instance variables
1830
1831        /**
1832         * The spec that defines the vertical characteristics of the cell group
1833         * described by these layout parameters.
1834         * If an assignment is made to this field after a measurement or layout operation
1835         * has already taken place, a call to
1836         * {@link ViewGroup#setLayoutParams(ViewGroup.LayoutParams)}
1837         * must be made to notify GridLayout of the change. GridLayout is normally able
1838         * to detect when code fails to observe this rule, issue a warning and take steps to
1839         * compensate for the omission. This facility is implemented on a best effort basis
1840         * and should not be relied upon in production code - so it is best to include the above
1841         * calls to remove the warnings as soon as it is practical.
1842         */
1843        public Spec rowSpec = Spec.UNDEFINED;
1844
1845        /**
1846         * The spec that defines the horizontal characteristics of the cell group
1847         * described by these layout parameters.
1848         * If an assignment is made to this field after a measurement or layout operation
1849         * has already taken place, a call to
1850         * {@link ViewGroup#setLayoutParams(ViewGroup.LayoutParams)}
1851         * must be made to notify GridLayout of the change. GridLayout is normally able
1852         * to detect when code fails to observe this rule, issue a warning and take steps to
1853         * compensate for the omission. This facility is implemented on a best effort basis
1854         * and should not be relied upon in production code - so it is best to include the above
1855         * calls to remove the warnings as soon as it is practical.
1856         */
1857        public Spec columnSpec = Spec.UNDEFINED;
1858
1859        // Constructors
1860
1861        private LayoutParams(
1862                int width, int height,
1863                int left, int top, int right, int bottom,
1864                Spec rowSpec, Spec columnSpec) {
1865            super(width, height);
1866            setMargins(left, top, right, bottom);
1867            this.rowSpec = rowSpec;
1868            this.columnSpec = columnSpec;
1869        }
1870
1871        /**
1872         * Constructs a new LayoutParams instance for this <code>rowSpec</code>
1873         * and <code>columnSpec</code>. All other fields are initialized with
1874         * default values as defined in {@link LayoutParams}.
1875         *
1876         * @param rowSpec    the rowSpec
1877         * @param columnSpec the columnSpec
1878         */
1879        public LayoutParams(Spec rowSpec, Spec columnSpec) {
1880            this(DEFAULT_WIDTH, DEFAULT_HEIGHT,
1881                    DEFAULT_MARGIN, DEFAULT_MARGIN, DEFAULT_MARGIN, DEFAULT_MARGIN,
1882                    rowSpec, columnSpec);
1883        }
1884
1885        /**
1886         * Constructs a new LayoutParams with default values as defined in {@link LayoutParams}.
1887         */
1888        public LayoutParams() {
1889            this(Spec.UNDEFINED, Spec.UNDEFINED);
1890        }
1891
1892        // Copying constructors
1893
1894        /**
1895         * {@inheritDoc}
1896         */
1897        public LayoutParams(ViewGroup.LayoutParams params) {
1898            super(params);
1899        }
1900
1901        /**
1902         * {@inheritDoc}
1903         */
1904        public LayoutParams(MarginLayoutParams params) {
1905            super(params);
1906        }
1907
1908        /**
1909         * {@inheritDoc}
1910         */
1911        public LayoutParams(LayoutParams that) {
1912            super(that);
1913            this.rowSpec = that.rowSpec;
1914            this.columnSpec = that.columnSpec;
1915        }
1916
1917        // AttributeSet constructors
1918
1919        /**
1920         * {@inheritDoc}
1921         *
1922         * Values not defined in the attribute set take the default values
1923         * defined in {@link LayoutParams}.
1924         */
1925        public LayoutParams(Context context, AttributeSet attrs) {
1926            super(context, attrs);
1927            reInitSuper(context, attrs);
1928            init(context, attrs);
1929        }
1930
1931        // Implementation
1932
1933        // Reinitialise the margins using a different default policy than MarginLayoutParams.
1934        // Here we use the value UNDEFINED (as distinct from zero) to represent the undefined state
1935        // so that a layout manager default can be accessed post set up. We need this as, at the
1936        // point of installation, we do not know how many rows/cols there are and therefore
1937        // which elements are positioned next to the container's trailing edges. We need to
1938        // know this as margins around the container's boundary should have different
1939        // defaults to those between peers.
1940
1941        // This method could be parametrized and moved into MarginLayout.
1942        private void reInitSuper(Context context, AttributeSet attrs) {
1943            TypedArray a =
1944                    context.obtainStyledAttributes(attrs, R.styleable.ViewGroup_MarginLayout);
1945            try {
1946                int margin = a.getDimensionPixelSize(MARGIN, DEFAULT_MARGIN);
1947
1948                this.leftMargin = a.getDimensionPixelSize(LEFT_MARGIN, margin);
1949                this.topMargin = a.getDimensionPixelSize(TOP_MARGIN, margin);
1950                this.rightMargin = a.getDimensionPixelSize(RIGHT_MARGIN, margin);
1951                this.bottomMargin = a.getDimensionPixelSize(BOTTOM_MARGIN, margin);
1952            } finally {
1953                a.recycle();
1954            }
1955        }
1956
1957        private void init(Context context, AttributeSet attrs) {
1958            TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.GridLayout_Layout);
1959            try {
1960                int gravity = a.getInt(GRAVITY, Gravity.NO_GRAVITY);
1961
1962                int column = a.getInt(COLUMN, DEFAULT_COLUMN);
1963                int colSpan = a.getInt(COLUMN_SPAN, DEFAULT_SPAN_SIZE);
1964                this.columnSpec = spec(column, colSpan, getAlignment(gravity, true));
1965
1966                int row = a.getInt(ROW, DEFAULT_ROW);
1967                int rowSpan = a.getInt(ROW_SPAN, DEFAULT_SPAN_SIZE);
1968                this.rowSpec = spec(row, rowSpan, getAlignment(gravity, false));
1969            } finally {
1970                a.recycle();
1971            }
1972        }
1973
1974        /**
1975         * Describes how the child views are positioned. Default is {@code LEFT | BASELINE}.
1976         * See {@link Gravity}.
1977         *
1978         * @param gravity the new gravity value
1979         *
1980         * @attr ref android.R.styleable#GridLayout_Layout_layout_gravity
1981         */
1982        public void setGravity(int gravity) {
1983            rowSpec = rowSpec.copyWriteAlignment(getAlignment(gravity, false));
1984            columnSpec = columnSpec.copyWriteAlignment(getAlignment(gravity, true));
1985        }
1986
1987        @Override
1988        protected void setBaseAttributes(TypedArray attributes, int widthAttr, int heightAttr) {
1989            this.width = attributes.getLayoutDimension(widthAttr, DEFAULT_WIDTH);
1990            this.height = attributes.getLayoutDimension(heightAttr, DEFAULT_HEIGHT);
1991        }
1992
1993        final void setRowSpecSpan(Interval span) {
1994            rowSpec = rowSpec.copyWriteSpan(span);
1995        }
1996
1997        final void setColumnSpecSpan(Interval span) {
1998            columnSpec = columnSpec.copyWriteSpan(span);
1999        }
2000
2001        @Override
2002        public boolean equals(Object o) {
2003            if (this == o) return true;
2004            if (o == null || getClass() != o.getClass()) return false;
2005
2006            LayoutParams that = (LayoutParams) o;
2007
2008            if (!columnSpec.equals(that.columnSpec)) return false;
2009            if (!rowSpec.equals(that.rowSpec)) return false;
2010
2011            return true;
2012        }
2013
2014        @Override
2015        public int hashCode() {
2016            int result = rowSpec.hashCode();
2017            result = 31 * result + columnSpec.hashCode();
2018            return result;
2019        }
2020    }
2021
2022    /*
2023    In place of a HashMap from span to Int, use an array of key/value pairs - stored in Arcs.
2024    Add the mutables completesCycle flag to avoid creating another hash table for detecting cycles.
2025     */
2026    final static class Arc {
2027        public final Interval span;
2028        public final MutableInt value;
2029        public boolean valid = true;
2030
2031        public Arc(Interval span, MutableInt value) {
2032            this.span = span;
2033            this.value = value;
2034        }
2035
2036        @Override
2037        public String toString() {
2038            return span + " " + (!valid ? "+>" : "->") + " " + value;
2039        }
2040    }
2041
2042    // A mutable Integer - used to avoid heap allocation during the layout operation
2043
2044    final static class MutableInt {
2045        public int value;
2046
2047        public MutableInt() {
2048            reset();
2049        }
2050
2051        public MutableInt(int value) {
2052            this.value = value;
2053        }
2054
2055        public void reset() {
2056            value = Integer.MIN_VALUE;
2057        }
2058
2059        @Override
2060        public String toString() {
2061            return Integer.toString(value);
2062        }
2063    }
2064
2065    final static class Assoc<K, V> extends ArrayList<Pair<K, V>> {
2066        private final Class<K> keyType;
2067        private final Class<V> valueType;
2068
2069        private Assoc(Class<K> keyType, Class<V> valueType) {
2070            this.keyType = keyType;
2071            this.valueType = valueType;
2072        }
2073
2074        public static <K, V> Assoc<K, V> of(Class<K> keyType, Class<V> valueType) {
2075            return new Assoc<K, V>(keyType, valueType);
2076        }
2077
2078        public void put(K key, V value) {
2079            add(Pair.create(key, value));
2080        }
2081
2082        @SuppressWarnings(value = "unchecked")
2083        public PackedMap<K, V> pack() {
2084            int N = size();
2085            K[] keys = (K[]) Array.newInstance(keyType, N);
2086            V[] values = (V[]) Array.newInstance(valueType, N);
2087            for (int i = 0; i < N; i++) {
2088                keys[i] = get(i).first;
2089                values[i] = get(i).second;
2090            }
2091            return new PackedMap<K, V>(keys, values);
2092        }
2093    }
2094
2095    /*
2096    This data structure is used in place of a Map where we have an index that refers to the order
2097    in which each key/value pairs were added to the map. In this case we store keys and values
2098    in arrays of a length that is equal to the number of unique keys. We also maintain an
2099    array of indexes from insertion order to the compacted arrays of keys and values.
2100
2101    Note that behavior differs from that of a LinkedHashMap in that repeated entries
2102    *do* get added multiples times. So the length of index is equals to the number of
2103    items added.
2104
2105    This is useful in the GridLayout class where we can rely on the order of children not
2106    changing during layout - to use integer-based lookup for our internal structures
2107    rather than using (and storing) an implementation of Map<Key, ?>.
2108     */
2109    @SuppressWarnings(value = "unchecked")
2110    final static class PackedMap<K, V> {
2111        public final int[] index;
2112        public final K[] keys;
2113        public final V[] values;
2114
2115        private PackedMap(K[] keys, V[] values) {
2116            this.index = createIndex(keys);
2117
2118            this.keys = compact(keys, index);
2119            this.values = compact(values, index);
2120        }
2121
2122        public V getValue(int i) {
2123            return values[index[i]];
2124        }
2125
2126        private static <K> int[] createIndex(K[] keys) {
2127            int size = keys.length;
2128            int[] result = new int[size];
2129
2130            Map<K, Integer> keyToIndex = new HashMap<K, Integer>();
2131            for (int i = 0; i < size; i++) {
2132                K key = keys[i];
2133                Integer index = keyToIndex.get(key);
2134                if (index == null) {
2135                    index = keyToIndex.size();
2136                    keyToIndex.put(key, index);
2137                }
2138                result[i] = index;
2139            }
2140            return result;
2141        }
2142
2143        /*
2144        Create a compact array of keys or values using the supplied index.
2145         */
2146        private static <K> K[] compact(K[] a, int[] index) {
2147            int size = a.length;
2148            Class<?> componentType = a.getClass().getComponentType();
2149            K[] result = (K[]) Array.newInstance(componentType, max2(index, -1) + 1);
2150
2151            // this overwrite duplicates, retaining the last equivalent entry
2152            for (int i = 0; i < size; i++) {
2153                result[index[i]] = a[i];
2154            }
2155            return result;
2156        }
2157    }
2158
2159    /*
2160    For each group (with a given alignment) we need to store the amount of space required
2161    before the alignment point and the amount of space required after it. One side of this
2162    calculation is always 0 for START and END alignments but we don't make use of this.
2163    For CENTER and BASELINE alignments both sides are needed and in the BASELINE case no
2164    simple optimisations are possible.
2165
2166    The general algorithm therefore is to create a Map (actually a PackedMap) from
2167    group to Bounds and to loop through all Views in the group taking the maximum
2168    of the values for each View.
2169    */
2170    static class Bounds {
2171        public int before;
2172        public int after;
2173        public int flexibility; // we're flexible iff all included specs are flexible
2174
2175        private Bounds() {
2176            reset();
2177        }
2178
2179        protected void reset() {
2180            before = Integer.MIN_VALUE;
2181            after = Integer.MIN_VALUE;
2182            flexibility = CAN_STRETCH; // from the above, we're flexible when empty
2183        }
2184
2185        protected void include(int before, int after) {
2186            this.before = max(this.before, before);
2187            this.after = max(this.after, after);
2188        }
2189
2190        protected int size(boolean min) {
2191            if (!min) {
2192                if (canStretch(flexibility)) {
2193                    return MAX_SIZE;
2194                }
2195            }
2196            return before + after;
2197        }
2198
2199        protected int getOffset(GridLayout gl, View c, Alignment a, int size, boolean horizontal) {
2200            return before - a.getAlignmentValue(c, size, gl.getLayoutMode());
2201        }
2202
2203        protected final void include(GridLayout gl, View c, Spec spec, Axis axis) {
2204            this.flexibility &= spec.getFlexibility();
2205            boolean horizontal = axis.horizontal;
2206            int size = gl.getMeasurementIncludingMargin(c, horizontal);
2207            Alignment alignment = gl.getAlignment(spec.alignment, horizontal);
2208            // todo test this works correctly when the returned value is UNDEFINED
2209            int before = alignment.getAlignmentValue(c, size, gl.getLayoutMode());
2210            include(before, size - before);
2211        }
2212
2213        @Override
2214        public String toString() {
2215            return "Bounds{" +
2216                    "before=" + before +
2217                    ", after=" + after +
2218                    '}';
2219        }
2220    }
2221
2222    /**
2223     * An Interval represents a contiguous range of values that lie between
2224     * the interval's {@link #min} and {@link #max} values.
2225     * <p>
2226     * Intervals are immutable so may be passed as values and used as keys in hash tables.
2227     * It is not necessary to have multiple instances of Intervals which have the same
2228     * {@link #min} and {@link #max} values.
2229     * <p>
2230     * Intervals are often written as {@code [min, max]} and represent the set of values
2231     * {@code x} such that {@code min <= x < max}.
2232     */
2233    final static class Interval {
2234        /**
2235         * The minimum value.
2236         */
2237        public final int min;
2238
2239        /**
2240         * The maximum value.
2241         */
2242        public final int max;
2243
2244        /**
2245         * Construct a new Interval, {@code interval}, where:
2246         * <ul>
2247         *     <li> {@code interval.min = min} </li>
2248         *     <li> {@code interval.max = max} </li>
2249         * </ul>
2250         *
2251         * @param min the minimum value.
2252         * @param max the maximum value.
2253         */
2254        public Interval(int min, int max) {
2255            this.min = min;
2256            this.max = max;
2257        }
2258
2259        int size() {
2260            return max - min;
2261        }
2262
2263        Interval inverse() {
2264            return new Interval(max, min);
2265        }
2266
2267        /**
2268         * Returns {@code true} if the {@link #getClass class},
2269         * {@link #min} and {@link #max} properties of this Interval and the
2270         * supplied parameter are pairwise equal; {@code false} otherwise.
2271         *
2272         * @param that the object to compare this interval with
2273         *
2274         * @return {@code true} if the specified object is equal to this
2275         *         {@code Interval}, {@code false} otherwise.
2276         */
2277        @Override
2278        public boolean equals(Object that) {
2279            if (this == that) {
2280                return true;
2281            }
2282            if (that == null || getClass() != that.getClass()) {
2283                return false;
2284            }
2285
2286            Interval interval = (Interval) that;
2287
2288            if (max != interval.max) {
2289                return false;
2290            }
2291            //noinspection RedundantIfStatement
2292            if (min != interval.min) {
2293                return false;
2294            }
2295
2296            return true;
2297        }
2298
2299        @Override
2300        public int hashCode() {
2301            int result = min;
2302            result = 31 * result + max;
2303            return result;
2304        }
2305
2306        @Override
2307        public String toString() {
2308            return "[" + min + ", " + max + "]";
2309        }
2310    }
2311
2312    /**
2313     * A Spec defines the horizontal or vertical characteristics of a group of
2314     * cells. Each spec. defines the <em>grid indices</em> and <em>alignment</em>
2315     * along the appropriate axis.
2316     * <p>
2317     * The <em>grid indices</em> are the leading and trailing edges of this cell group.
2318     * See {@link GridLayout} for a description of the conventions used by GridLayout
2319     * for grid indices.
2320     * <p>
2321     * The <em>alignment</em> property specifies how cells should be aligned in this group.
2322     * For row groups, this specifies the vertical alignment.
2323     * For column groups, this specifies the horizontal alignment.
2324     * <p>
2325     * Use the following static methods to create specs:
2326     * <ul>
2327     *   <li>{@link #spec(int)}</li>
2328     *   <li>{@link #spec(int, int)}</li>
2329     *   <li>{@link #spec(int, Alignment)}</li>
2330     *   <li>{@link #spec(int, int, Alignment)}</li>
2331     * </ul>
2332     *
2333     */
2334    public static class Spec {
2335        static final Spec UNDEFINED = spec(GridLayout.UNDEFINED);
2336
2337        final boolean startDefined;
2338        final Interval span;
2339        final Alignment alignment;
2340
2341        private Spec(boolean startDefined, Interval span, Alignment alignment) {
2342            this.startDefined = startDefined;
2343            this.span = span;
2344            this.alignment = alignment;
2345        }
2346
2347        private Spec(boolean startDefined, int start, int size, Alignment alignment) {
2348            this(startDefined, new Interval(start, start + size), alignment);
2349        }
2350
2351        final Spec copyWriteSpan(Interval span) {
2352            return new Spec(startDefined, span, alignment);
2353        }
2354
2355        final Spec copyWriteAlignment(Alignment alignment) {
2356            return new Spec(startDefined, span, alignment);
2357        }
2358
2359        final int getFlexibility() {
2360            return (alignment == UNDEFINED_ALIGNMENT) ? INFLEXIBLE : CAN_STRETCH;
2361        }
2362
2363        /**
2364         * Returns {@code true} if the {@code class}, {@code alignment} and {@code span}
2365         * properties of this Spec and the supplied parameter are pairwise equal,
2366         * {@code false} otherwise.
2367         *
2368         * @param that the object to compare this spec with
2369         *
2370         * @return {@code true} if the specified object is equal to this
2371         *         {@code Spec}; {@code false} otherwise
2372         */
2373        @Override
2374        public boolean equals(Object that) {
2375            if (this == that) {
2376                return true;
2377            }
2378            if (that == null || getClass() != that.getClass()) {
2379                return false;
2380            }
2381
2382            Spec spec = (Spec) that;
2383
2384            if (!alignment.equals(spec.alignment)) {
2385                return false;
2386            }
2387            //noinspection RedundantIfStatement
2388            if (!span.equals(spec.span)) {
2389                return false;
2390            }
2391
2392            return true;
2393        }
2394
2395        @Override
2396        public int hashCode() {
2397            int result = span.hashCode();
2398            result = 31 * result + alignment.hashCode();
2399            return result;
2400        }
2401    }
2402
2403    /**
2404     * Return a Spec, {@code spec}, where:
2405     * <ul>
2406     *     <li> {@code spec.span = [start, start + size]} </li>
2407     *     <li> {@code spec.alignment = alignment} </li>
2408     * </ul>
2409     * <p>
2410     * To leave the start index undefined, use the value {@link #UNDEFINED}.
2411     *
2412     * @param start     the start
2413     * @param size      the size
2414     * @param alignment the alignment
2415     */
2416    public static Spec spec(int start, int size, Alignment alignment) {
2417        return new Spec(start != UNDEFINED, start, size, alignment);
2418    }
2419
2420    /**
2421     * Return a Spec, {@code spec}, where:
2422     * <ul>
2423     *     <li> {@code spec.span = [start, start + 1]} </li>
2424     *     <li> {@code spec.alignment = alignment} </li>
2425     * </ul>
2426     * <p>
2427     * To leave the start index undefined, use the value {@link #UNDEFINED}.
2428     *
2429     * @param start     the start index
2430     * @param alignment the alignment
2431     *
2432     * @see #spec(int, int, Alignment)
2433     */
2434    public static Spec spec(int start, Alignment alignment) {
2435        return spec(start, 1, alignment);
2436    }
2437
2438    /**
2439     * Return a Spec, {@code spec}, where:
2440     * <ul>
2441     *     <li> {@code spec.span = [start, start + size]} </li>
2442     * </ul>
2443     * <p>
2444     * To leave the start index undefined, use the value {@link #UNDEFINED}.
2445     *
2446     * @param start     the start
2447     * @param size      the size
2448     *
2449     * @see #spec(int, Alignment)
2450     */
2451    public static Spec spec(int start, int size) {
2452        return spec(start, size, UNDEFINED_ALIGNMENT);
2453    }
2454
2455    /**
2456     * Return a Spec, {@code spec}, where:
2457     * <ul>
2458     *     <li> {@code spec.span = [start, start + 1]} </li>
2459     * </ul>
2460     * <p>
2461     * To leave the start index undefined, use the value {@link #UNDEFINED}.
2462     *
2463     * @param start     the start index
2464     *
2465     * @see #spec(int, int)
2466     */
2467    public static Spec spec(int start) {
2468        return spec(start, 1);
2469    }
2470
2471    /**
2472     * Alignments specify where a view should be placed within a cell group and
2473     * what size it should be.
2474     * <p>
2475     * The {@link LayoutParams} class contains a {@link LayoutParams#rowSpec rowSpec}
2476     * and a {@link LayoutParams#columnSpec columnSpec} each of which contains an
2477     * {@code alignment}. Overall placement of the view in the cell
2478     * group is specified by the two alignments which act along each axis independently.
2479     * <p>
2480     *  The GridLayout class defines the most common alignments used in general layout:
2481     * {@link #TOP}, {@link #LEFT}, {@link #BOTTOM}, {@link #RIGHT}, {@link #START},
2482     * {@link #END}, {@link #CENTER}, {@link #BASELINE} and {@link #FILL}.
2483     */
2484    /*
2485     * An Alignment implementation must define {@link #getAlignmentValue(View, int, int)},
2486     * to return the appropriate value for the type of alignment being defined.
2487     * The enclosing algorithms position the children
2488     * so that the locations defined by the alignment values
2489     * are the same for all of the views in a group.
2490     * <p>
2491     */
2492    public static abstract class Alignment {
2493        Alignment() {
2494        }
2495
2496        abstract int getGravityOffset(View view, int cellDelta);
2497
2498        /**
2499         * Returns an alignment value. In the case of vertical alignments the value
2500         * returned should indicate the distance from the top of the view to the
2501         * alignment location.
2502         * For horizontal alignments measurement is made from the left edge of the component.
2503         *
2504         * @param view              the view to which this alignment should be applied
2505         * @param viewSize          the measured size of the view
2506         * @param mode              the basis of alignment: CLIP or OPTICAL
2507         * @return the alignment value
2508         */
2509        abstract int getAlignmentValue(View view, int viewSize, int mode);
2510
2511        /**
2512         * Returns the size of the view specified by this alignment.
2513         * In the case of vertical alignments this method should return a height; for
2514         * horizontal alignments this method should return the width.
2515         * <p>
2516         * The default implementation returns {@code viewSize}.
2517         *
2518         * @param view              the view to which this alignment should be applied
2519         * @param viewSize          the measured size of the view
2520         * @param cellSize          the size of the cell into which this view will be placed
2521         * @return the aligned size
2522         */
2523        int getSizeInCell(View view, int viewSize, int cellSize) {
2524            return viewSize;
2525        }
2526
2527        Bounds getBounds() {
2528            return new Bounds();
2529        }
2530    }
2531
2532    static final Alignment UNDEFINED_ALIGNMENT = new Alignment() {
2533        @Override
2534        int getGravityOffset(View view, int cellDelta) {
2535            return UNDEFINED;
2536        }
2537
2538        @Override
2539        public int getAlignmentValue(View view, int viewSize, int mode) {
2540            return UNDEFINED;
2541        }
2542    };
2543
2544    /**
2545     * Indicates that a view should be aligned with the <em>start</em>
2546     * edges of the other views in its cell group.
2547     */
2548    private static final Alignment LEADING = new Alignment() {
2549        @Override
2550        int getGravityOffset(View view, int cellDelta) {
2551            return 0;
2552        }
2553
2554        @Override
2555        public int getAlignmentValue(View view, int viewSize, int mode) {
2556            return 0;
2557        }
2558    };
2559
2560    /**
2561     * Indicates that a view should be aligned with the <em>end</em>
2562     * edges of the other views in its cell group.
2563     */
2564    private static final Alignment TRAILING = new Alignment() {
2565        @Override
2566        int getGravityOffset(View view, int cellDelta) {
2567            return cellDelta;
2568        }
2569
2570        @Override
2571        public int getAlignmentValue(View view, int viewSize, int mode) {
2572            return viewSize;
2573        }
2574    };
2575
2576    /**
2577     * Indicates that a view should be aligned with the <em>top</em>
2578     * edges of the other views in its cell group.
2579     */
2580    public static final Alignment TOP = LEADING;
2581
2582    /**
2583     * Indicates that a view should be aligned with the <em>bottom</em>
2584     * edges of the other views in its cell group.
2585     */
2586    public static final Alignment BOTTOM = TRAILING;
2587
2588    /**
2589     * Indicates that a view should be aligned with the <em>start</em>
2590     * edges of the other views in its cell group.
2591     */
2592    public static final Alignment START = LEADING;
2593
2594    /**
2595     * Indicates that a view should be aligned with the <em>end</em>
2596     * edges of the other views in its cell group.
2597     */
2598    public static final Alignment END = TRAILING;
2599
2600    private static Alignment createSwitchingAlignment(final Alignment ltr, final Alignment rtl) {
2601        return new Alignment() {
2602            @Override
2603            int getGravityOffset(View view, int cellDelta) {
2604                return (!view.isLayoutRtl() ? ltr : rtl).getGravityOffset(view, cellDelta);
2605            }
2606
2607            @Override
2608            public int getAlignmentValue(View view, int viewSize, int mode) {
2609                return (!view.isLayoutRtl() ? ltr : rtl).getAlignmentValue(view, viewSize, mode);
2610            }
2611        };
2612    }
2613
2614    /**
2615     * Indicates that a view should be aligned with the <em>left</em>
2616     * edges of the other views in its cell group.
2617     */
2618    public static final Alignment LEFT = createSwitchingAlignment(START, END);
2619
2620    /**
2621     * Indicates that a view should be aligned with the <em>right</em>
2622     * edges of the other views in its cell group.
2623     */
2624    public static final Alignment RIGHT = createSwitchingAlignment(END, START);
2625
2626    /**
2627     * Indicates that a view should be <em>centered</em> with the other views in its cell group.
2628     * This constant may be used in both {@link LayoutParams#rowSpec rowSpecs} and {@link
2629     * LayoutParams#columnSpec columnSpecs}.
2630     */
2631    public static final Alignment CENTER = new Alignment() {
2632        @Override
2633        int getGravityOffset(View view, int cellDelta) {
2634            return cellDelta >> 1;
2635        }
2636
2637        @Override
2638        public int getAlignmentValue(View view, int viewSize, int mode) {
2639            return viewSize >> 1;
2640        }
2641    };
2642
2643    /**
2644     * Indicates that a view should be aligned with the <em>baselines</em>
2645     * of the other views in its cell group.
2646     * This constant may only be used as an alignment in {@link LayoutParams#rowSpec rowSpecs}.
2647     *
2648     * @see View#getBaseline()
2649     */
2650    public static final Alignment BASELINE = new Alignment() {
2651        @Override
2652        int getGravityOffset(View view, int cellDelta) {
2653            return 0; // baseline gravity is top
2654        }
2655
2656        @Override
2657        public int getAlignmentValue(View view, int viewSize, int mode) {
2658            int baseline = view.getBaseline();
2659            return baseline == -1 ? UNDEFINED : baseline;
2660        }
2661
2662        @Override
2663        public Bounds getBounds() {
2664            return new Bounds() {
2665                /*
2666                In a baseline aligned row in which some components define a baseline
2667                and some don't, we need a third variable to properly account for all
2668                the sizes. This tracks the maximum size of all the components -
2669                including those that don't define a baseline.
2670                */
2671                private int size;
2672
2673                @Override
2674                protected void reset() {
2675                    super.reset();
2676                    size = Integer.MIN_VALUE;
2677                }
2678
2679                @Override
2680                protected void include(int before, int after) {
2681                    super.include(before, after);
2682                    size = max(size, before + after);
2683                }
2684
2685                @Override
2686                protected int size(boolean min) {
2687                    return max(super.size(min), size);
2688                }
2689
2690                @Override
2691                protected int getOffset(GridLayout gl, View c, Alignment a, int size, boolean hrz) {
2692                    return max(0, super.getOffset(gl, c, a, size, hrz));
2693                }
2694            };
2695        }
2696    };
2697
2698    /**
2699     * Indicates that a view should expanded to fit the boundaries of its cell group.
2700     * This constant may be used in both {@link LayoutParams#rowSpec rowSpecs} and
2701     * {@link LayoutParams#columnSpec columnSpecs}.
2702     */
2703    public static final Alignment FILL = new Alignment() {
2704        @Override
2705        int getGravityOffset(View view, int cellDelta) {
2706            return 0;
2707        }
2708
2709        @Override
2710        public int getAlignmentValue(View view, int viewSize, int mode) {
2711            return UNDEFINED;
2712        }
2713
2714        @Override
2715        public int getSizeInCell(View view, int viewSize, int cellSize) {
2716            return cellSize;
2717        }
2718    };
2719
2720    static boolean canStretch(int flexibility) {
2721        return (flexibility & CAN_STRETCH) != 0;
2722    }
2723
2724    private static final int INFLEXIBLE = 0;
2725    private static final int CAN_STRETCH = 2;
2726}
2727