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