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