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