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