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