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