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.Paint;
23import android.support.v4.view.GravityCompat;
24import android.support.v4.view.ViewCompat;
25import android.support.v4.view.ViewGroupCompat;
26import android.util.AttributeSet;
27import android.util.Log;
28import android.util.LogPrinter;
29import android.util.Pair;
30import android.util.Printer;
31import android.view.Gravity;
32import android.view.View;
33import android.view.ViewGroup;
34import android.view.accessibility.AccessibilityEvent;
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.support.v7.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.view.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 mHorizontalAxis = new Axis(true);
255    final Axis mVerticalAxis = new Axis(false);
256    int mOrientation = DEFAULT_ORIENTATION;
257    boolean mUseDefaultMargins = DEFAULT_USE_DEFAULT_MARGINS;
258    int mAlignmentMode = DEFAULT_ALIGNMENT_MODE;
259    int mDefaultGap;
260    int mLastLayoutParamsHashCode = UNINITIALIZED_HASH;
261    Printer mPrinter = 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        mDefaultGap = 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 mOrientation;
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.mOrientation != orientation) {
353            this.mOrientation = 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 mVerticalAxis.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        mVerticalAxis.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 mHorizontalAxis.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        mHorizontalAxis.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 mUseDefaultMargins;
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.mUseDefaultMargins = 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 mAlignmentMode;
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.mAlignmentMode = 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 mVerticalAxis.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        mVerticalAxis.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 mHorizontalAxis.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        mHorizontalAxis.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 mPrinter;
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.mPrinter = (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 | GravityCompat.RELATIVE_LAYOUT_DIRECTION):
633                return START;
634            case (AXIS_SPECIFIED | AXIS_PULL_AFTER | GravityCompat.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.support.v7.widget.Space.class) {
644            return 0;
645        }
646        return mDefaultGap / 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 (!mUseDefaultMargins) {
655            return 0;
656        }
657        Spec spec = horizontal ? p.columnSpec : p.rowSpec;
658        Axis axis = horizontal ? mHorizontalAxis : mVerticalAxis;
659        Interval span = spec.span;
660        boolean leading1 = (horizontal && isLayoutRtlCompat()) ? !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 boolean isLayoutRtlCompat() {
675        return ViewCompat.getLayoutDirection(this) == ViewCompat.LAYOUT_DIRECTION_RTL;
676    }
677
678    private int getMargin(View view, boolean horizontal, boolean leading) {
679        if (mAlignmentMode == ALIGN_MARGINS) {
680            return getMargin1(view, horizontal, leading);
681        } else {
682            Axis axis = horizontal ? mHorizontalAxis : mVerticalAxis;
683            int[] margins = leading ? axis.getLeadingMargins() : axis.getTrailingMargins();
684            LayoutParams lp = getLayoutParams(view);
685            Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
686            int index = leading ? spec.span.min : spec.span.max;
687            return margins[index];
688        }
689    }
690
691    private int getTotalMargin(View child, boolean horizontal) {
692        return getMargin(child, horizontal, true) + getMargin(child, horizontal, false);
693    }
694
695    private static boolean fits(int[] a, int value, int start, int end) {
696        if (end > a.length) {
697            return false;
698        }
699        for (int i = start; i < end; i++) {
700            if (a[i] > value) {
701                return false;
702            }
703        }
704        return true;
705    }
706
707    private static void procrusteanFill(int[] a, int start, int end, int value) {
708        int length = a.length;
709        Arrays.fill(a, Math.min(start, length), Math.min(end, length), value);
710    }
711
712    private static void setCellGroup(LayoutParams lp, int row, int rowSpan, int col, int colSpan) {
713        lp.setRowSpecSpan(new Interval(row, row + rowSpan));
714        lp.setColumnSpecSpan(new Interval(col, col + colSpan));
715    }
716
717    // Logic to avert infinite loops by ensuring that the cells can be placed somewhere.
718    private static int clip(Interval minorRange, boolean minorWasDefined, int count) {
719        int size = minorRange.size();
720        if (count == 0) {
721            return size;
722        }
723        int min = minorWasDefined ? min(minorRange.min, count) : 0;
724        return min(size, count - min);
725    }
726
727    // install default indices for cells that don't define them
728    private void validateLayoutParams() {
729        final boolean horizontal = (mOrientation == HORIZONTAL);
730        final Axis axis = horizontal ? mHorizontalAxis : mVerticalAxis;
731        final int count = (axis.definedCount != UNDEFINED) ? axis.definedCount : 0;
732
733        int major = 0;
734        int minor = 0;
735        int[] maxSizes = new int[count];
736
737        for (int i = 0, N = getChildCount(); i < N; i++) {
738            LayoutParams lp = (LayoutParams) getChildAt(i).getLayoutParams();
739
740            final Spec majorSpec = horizontal ? lp.rowSpec : lp.columnSpec;
741            final Interval majorRange = majorSpec.span;
742            final boolean majorWasDefined = majorSpec.startDefined;
743            final int majorSpan = majorRange.size();
744            if (majorWasDefined) {
745                major = majorRange.min;
746            }
747
748            final Spec minorSpec = horizontal ? lp.columnSpec : lp.rowSpec;
749            final Interval minorRange = minorSpec.span;
750            final boolean minorWasDefined = minorSpec.startDefined;
751            final int minorSpan = clip(minorRange, minorWasDefined, count);
752            if (minorWasDefined) {
753                minor = minorRange.min;
754            }
755
756            if (count != 0) {
757                // Find suitable row/col values when at least one is undefined.
758                if (!majorWasDefined || !minorWasDefined) {
759                    while (!fits(maxSizes, major, minor, minor + minorSpan)) {
760                        if (minorWasDefined) {
761                            major++;
762                        } else {
763                            if (minor + minorSpan <= count) {
764                                minor++;
765                            } else {
766                                minor = 0;
767                                major++;
768                            }
769                        }
770                    }
771                }
772                procrusteanFill(maxSizes, minor, minor + minorSpan, major + majorSpan);
773            }
774
775            if (horizontal) {
776                setCellGroup(lp, major, majorSpan, minor, minorSpan);
777            } else {
778                setCellGroup(lp, minor, minorSpan, major, majorSpan);
779            }
780
781            minor = minor + minorSpan;
782        }
783    }
784
785    private void invalidateStructure() {
786        mLastLayoutParamsHashCode = UNINITIALIZED_HASH;
787        if (mHorizontalAxis != null) mHorizontalAxis.invalidateStructure();
788        if (mVerticalAxis != null) mVerticalAxis.invalidateStructure();
789        // This can end up being done twice. Better twice than not at all.
790        invalidateValues();
791    }
792
793    private void invalidateValues() {
794        // Need null check because requestLayout() is called in View's initializer,
795        // before we are set up.
796        if (mHorizontalAxis != null && mVerticalAxis != null) {
797            mHorizontalAxis.invalidateValues();
798            mVerticalAxis.invalidateValues();
799        }
800    }
801
802    final LayoutParams getLayoutParams(View c) {
803        return (LayoutParams) c.getLayoutParams();
804    }
805
806    private static void handleInvalidParams(String msg) {
807        throw new IllegalArgumentException(msg + ". ");
808    }
809
810    private void checkLayoutParams(LayoutParams lp, boolean horizontal) {
811        String groupName = horizontal ? "column" : "row";
812        Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
813        Interval span = spec.span;
814        if (span.min != UNDEFINED && span.min < 0) {
815            handleInvalidParams(groupName + " indices must be positive");
816        }
817        Axis axis = horizontal ? mHorizontalAxis : mVerticalAxis;
818        int count = axis.definedCount;
819        if (count != UNDEFINED) {
820            if (span.max > count) {
821                handleInvalidParams(groupName +
822                        " indices (start + span) mustn't exceed the " + groupName + " count");
823            }
824            if (span.size() > count) {
825                handleInvalidParams(groupName + " span mustn't exceed the " + groupName + " count");
826            }
827        }
828    }
829
830    @Override
831    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
832        if (!(p instanceof LayoutParams)) {
833            return false;
834        }
835        LayoutParams lp = (LayoutParams) p;
836
837        checkLayoutParams(lp, true);
838        checkLayoutParams(lp, false);
839
840        return true;
841    }
842
843    @Override
844    protected LayoutParams generateDefaultLayoutParams() {
845        return new LayoutParams();
846    }
847
848    @Override
849    public LayoutParams generateLayoutParams(AttributeSet attrs) {
850        return new LayoutParams(getContext(), attrs);
851    }
852
853    @Override
854    protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
855        return new LayoutParams(p);
856    }
857
858    // Draw grid
859
860    private void drawLine(Canvas graphics, int x1, int y1, int x2, int y2, Paint paint) {
861        if (isLayoutRtlCompat()) {
862            int width = getWidth();
863            graphics.drawLine(width - x1, y1, width - x2, y2, paint);
864        } else {
865            graphics.drawLine(x1, y1, x2, y2, paint);
866        }
867    }
868
869    private int computeLayoutParamsHashCode() {
870        int result = 1;
871        for (int i = 0, N = getChildCount(); i < N; i++) {
872            View c = getChildAt(i);
873            if (c.getVisibility() == View.GONE) continue;
874            LayoutParams lp = (LayoutParams) c.getLayoutParams();
875            result = 31 * result + lp.hashCode();
876        }
877        return result;
878    }
879
880    private void consistencyCheck() {
881        if (mLastLayoutParamsHashCode == UNINITIALIZED_HASH) {
882            validateLayoutParams();
883            mLastLayoutParamsHashCode = computeLayoutParamsHashCode();
884        } else if (mLastLayoutParamsHashCode != computeLayoutParamsHashCode()) {
885            mPrinter.println("The fields of some layout parameters were modified in between "
886                    + "layout operations. Check the javadoc for GridLayout.LayoutParams#rowSpec.");
887            invalidateStructure();
888            consistencyCheck();
889        }
890    }
891
892    // Measurement
893
894    // Note: padding has already been removed from the supplied specs
895    private void measureChildWithMargins2(View child, int parentWidthSpec, int parentHeightSpec,
896                                          int childWidth, int childHeight) {
897        int childWidthSpec = getChildMeasureSpec(parentWidthSpec,
898                getTotalMargin(child, true), childWidth);
899        int childHeightSpec = getChildMeasureSpec(parentHeightSpec,
900                getTotalMargin(child, false), childHeight);
901        child.measure(childWidthSpec, childHeightSpec);
902    }
903
904    // Note: padding has already been removed from the supplied specs
905    private void measureChildrenWithMargins(int widthSpec, int heightSpec, boolean firstPass) {
906        for (int i = 0, N = getChildCount(); i < N; i++) {
907            View c = getChildAt(i);
908            if (c.getVisibility() == View.GONE) continue;
909            LayoutParams lp = getLayoutParams(c);
910            if (firstPass) {
911                measureChildWithMargins2(c, widthSpec, heightSpec, lp.width, lp.height);
912            } else {
913                boolean horizontal = (mOrientation == HORIZONTAL);
914                Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
915                if (spec.alignment == FILL) {
916                    Interval span = spec.span;
917                    Axis axis = horizontal ? mHorizontalAxis : mVerticalAxis;
918                    int[] locations = axis.getLocations();
919                    int cellSize = locations[span.max] - locations[span.min];
920                    int viewSize = cellSize - getTotalMargin(c, horizontal);
921                    if (horizontal) {
922                        measureChildWithMargins2(c, widthSpec, heightSpec, viewSize, lp.height);
923                    } else {
924                        measureChildWithMargins2(c, widthSpec, heightSpec, lp.width, viewSize);
925                    }
926                }
927            }
928        }
929    }
930
931    static int adjust(int measureSpec, int delta) {
932        return makeMeasureSpec(
933                MeasureSpec.getSize(measureSpec + delta),  MeasureSpec.getMode(measureSpec));
934    }
935
936    @Override
937    protected void onMeasure(int widthSpec, int heightSpec) {
938        consistencyCheck();
939
940        /** If we have been called by {@link View#measure(int, int)}, one of width or height
941         *  is  likely to have changed. We must invalidate if so. */
942        invalidateValues();
943
944        int hPadding = getPaddingLeft() + getPaddingRight();
945        int vPadding = getPaddingTop()  + getPaddingBottom();
946
947        int widthSpecSansPadding =  adjust( widthSpec, -hPadding);
948        int heightSpecSansPadding = adjust(heightSpec, -vPadding);
949
950        measureChildrenWithMargins(widthSpecSansPadding, heightSpecSansPadding, true);
951
952        int widthSansPadding;
953        int heightSansPadding;
954
955        // Use the orientation property to decide which axis should be laid out first.
956        if (mOrientation == HORIZONTAL) {
957            widthSansPadding = mHorizontalAxis.getMeasure(widthSpecSansPadding);
958            measureChildrenWithMargins(widthSpecSansPadding, heightSpecSansPadding, false);
959            heightSansPadding = mVerticalAxis.getMeasure(heightSpecSansPadding);
960        } else {
961            heightSansPadding = mVerticalAxis.getMeasure(heightSpecSansPadding);
962            measureChildrenWithMargins(widthSpecSansPadding, heightSpecSansPadding, false);
963            widthSansPadding = mHorizontalAxis.getMeasure(widthSpecSansPadding);
964        }
965
966        int measuredWidth  = Math.max(widthSansPadding  + hPadding, getSuggestedMinimumWidth());
967        int measuredHeight = Math.max(heightSansPadding + vPadding, getSuggestedMinimumHeight());
968
969        setMeasuredDimension(
970                ViewCompat.resolveSizeAndState(measuredWidth, widthSpec, 0),
971                ViewCompat.resolveSizeAndState(measuredHeight, heightSpec, 0));
972    }
973
974    private int getMeasurement(View c, boolean horizontal) {
975        return horizontal ? c.getMeasuredWidth() : c.getMeasuredHeight();
976    }
977
978    final int getMeasurementIncludingMargin(View c, boolean horizontal) {
979        if (c.getVisibility() == View.GONE) {
980            return 0;
981        }
982        return getMeasurement(c, horizontal) + getTotalMargin(c, horizontal);
983    }
984
985    @Override
986    public void requestLayout() {
987        super.requestLayout();
988        invalidateStructure();
989    }
990
991    final Alignment getAlignment(Alignment alignment, boolean horizontal) {
992        return (alignment != UNDEFINED_ALIGNMENT) ? alignment :
993                (horizontal ? START : BASELINE);
994    }
995
996    // Layout container
997
998    /**
999     * {@inheritDoc}
1000     */
1001    /*
1002     The layout operation is implemented by delegating the heavy lifting to the
1003     to the mHorizontalAxis and mVerticalAxis instances of the internal Axis class.
1004     Together they compute the locations of the vertical and horizontal lines of
1005     the grid (respectively!).
1006
1007     This method is then left with the simpler task of applying margins, gravity
1008     and sizing to each child view and then placing it in its cell.
1009     */
1010    @Override
1011    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
1012        consistencyCheck();
1013
1014        int targetWidth = right - left;
1015        int targetHeight = bottom - top;
1016
1017        int paddingLeft = getPaddingLeft();
1018        int paddingTop = getPaddingTop();
1019        int paddingRight = getPaddingRight();
1020        int paddingBottom = getPaddingBottom();
1021
1022        mHorizontalAxis.layout(targetWidth - paddingLeft - paddingRight);
1023        mVerticalAxis.layout(targetHeight - paddingTop - paddingBottom);
1024
1025        int[] hLocations = mHorizontalAxis.getLocations();
1026        int[] vLocations = mVerticalAxis.getLocations();
1027
1028        for (int i = 0, N = getChildCount(); i < N; i++) {
1029            View c = getChildAt(i);
1030            if (c.getVisibility() == View.GONE) continue;
1031            LayoutParams lp = getLayoutParams(c);
1032            Spec columnSpec = lp.columnSpec;
1033            Spec rowSpec = lp.rowSpec;
1034
1035            Interval colSpan = columnSpec.span;
1036            Interval rowSpan = rowSpec.span;
1037
1038            int x1 = hLocations[colSpan.min];
1039            int y1 = vLocations[rowSpan.min];
1040
1041            int x2 = hLocations[colSpan.max];
1042            int y2 = vLocations[rowSpan.max];
1043
1044            int cellWidth = x2 - x1;
1045            int cellHeight = y2 - y1;
1046
1047            int pWidth = getMeasurement(c, true);
1048            int pHeight = getMeasurement(c, false);
1049
1050            Alignment hAlign = getAlignment(columnSpec.alignment, true);
1051            Alignment vAlign = getAlignment(rowSpec.alignment, false);
1052
1053            Bounds boundsX = mHorizontalAxis.getGroupBounds().getValue(i);
1054            Bounds boundsY = mVerticalAxis.getGroupBounds().getValue(i);
1055
1056            // Gravity offsets: the location of the alignment group relative to its cell group.
1057            int gravityOffsetX = hAlign.getGravityOffset(c, cellWidth - boundsX.size(true));
1058            int gravityOffsetY = vAlign.getGravityOffset(c, cellHeight - boundsY.size(true));
1059
1060            int leftMargin = getMargin(c, true, true);
1061            int topMargin = getMargin(c, false, true);
1062            int rightMargin = getMargin(c, true, false);
1063            int bottomMargin = getMargin(c, false, false);
1064
1065            int sumMarginsX = leftMargin + rightMargin;
1066            int sumMarginsY = topMargin + bottomMargin;
1067
1068            // Alignment offsets: the location of the view relative to its alignment group.
1069            int alignmentOffsetX = boundsX.getOffset(this, c, hAlign, pWidth + sumMarginsX, true);
1070            int alignmentOffsetY = boundsY.getOffset(this, c, vAlign, pHeight + sumMarginsY, false);
1071
1072            int width = hAlign.getSizeInCell(c, pWidth, cellWidth - sumMarginsX);
1073            int height = vAlign.getSizeInCell(c, pHeight, cellHeight - sumMarginsY);
1074
1075            int dx = x1 + gravityOffsetX + alignmentOffsetX;
1076
1077            int cx = !isLayoutRtlCompat() ? paddingLeft + leftMargin + dx :
1078                    targetWidth - width - paddingRight - rightMargin - dx;
1079            int cy = paddingTop + y1 + gravityOffsetY + alignmentOffsetY + topMargin;
1080
1081            if (width != c.getMeasuredWidth() || height != c.getMeasuredHeight()) {
1082                c.measure(makeMeasureSpec(width, EXACTLY), makeMeasureSpec(height, EXACTLY));
1083            }
1084            c.layout(cx, cy, cx + width, cy + height);
1085        }
1086    }
1087
1088    // Inner classes
1089
1090    /*
1091     This internal class houses the algorithm for computing the locations of grid lines;
1092     along either the horizontal or vertical axis. A GridLayout uses two instances of this class -
1093     distinguished by the "horizontal" flag which is true for the horizontal axis and false
1094     for the vertical one.
1095     */
1096    final class Axis {
1097        private static final int NEW = 0;
1098        private static final int PENDING = 1;
1099        private static final int COMPLETE = 2;
1100
1101        public final boolean horizontal;
1102
1103        public int definedCount = UNDEFINED;
1104        private int maxIndex = UNDEFINED;
1105
1106        PackedMap<Spec, Bounds> groupBounds;
1107        public boolean groupBoundsValid = false;
1108
1109        PackedMap<Interval, MutableInt> forwardLinks;
1110        public boolean forwardLinksValid = false;
1111
1112        PackedMap<Interval, MutableInt> backwardLinks;
1113        public boolean backwardLinksValid = false;
1114
1115        public int[] leadingMargins;
1116        public boolean leadingMarginsValid = false;
1117
1118        public int[] trailingMargins;
1119        public boolean trailingMarginsValid = false;
1120
1121        public Arc[] arcs;
1122        public boolean arcsValid = false;
1123
1124        public int[] locations;
1125        public boolean locationsValid = false;
1126
1127        boolean orderPreserved = DEFAULT_ORDER_PRESERVED;
1128
1129        private MutableInt parentMin = new MutableInt(0);
1130        private MutableInt parentMax = new MutableInt(-MAX_SIZE);
1131
1132        private Axis(boolean horizontal) {
1133            this.horizontal = horizontal;
1134        }
1135
1136        private int calculateMaxIndex() {
1137            // the number Integer.MIN_VALUE + 1 comes up in undefined cells
1138            int result = -1;
1139            for (int i = 0, N = getChildCount(); i < N; i++) {
1140                View c = getChildAt(i);
1141                LayoutParams params = getLayoutParams(c);
1142                Spec spec = horizontal ? params.columnSpec : params.rowSpec;
1143                Interval span = spec.span;
1144                result = max(result, span.min);
1145                result = max(result, span.max);
1146                result = max(result, span.size());
1147            }
1148            return result == -1 ? UNDEFINED : result;
1149        }
1150
1151        private int getMaxIndex() {
1152            if (maxIndex == UNDEFINED) {
1153                maxIndex = max(0, calculateMaxIndex()); // use zero when there are no children
1154            }
1155            return maxIndex;
1156        }
1157
1158        public int getCount() {
1159            return max(definedCount, getMaxIndex());
1160        }
1161
1162        public void setCount(int count) {
1163            if (count != UNDEFINED && count < getMaxIndex()) {
1164                handleInvalidParams((horizontal ? "column" : "row") +
1165                        "Count must be greater than or equal to the maximum of all grid indices " +
1166                        "(and spans) defined in the LayoutParams of each child");
1167            }
1168            this.definedCount = count;
1169        }
1170
1171        public boolean isOrderPreserved() {
1172            return orderPreserved;
1173        }
1174
1175        public void setOrderPreserved(boolean orderPreserved) {
1176            this.orderPreserved = orderPreserved;
1177            invalidateStructure();
1178        }
1179
1180        private PackedMap<Spec, Bounds> createGroupBounds() {
1181            Assoc<Spec, Bounds> assoc = Assoc.of(Spec.class, Bounds.class);
1182            for (int i = 0, N = getChildCount(); i < N; i++) {
1183                View c = getChildAt(i);
1184                // we must include views that are GONE here, see introductory javadoc
1185                LayoutParams lp = getLayoutParams(c);
1186                Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
1187                Bounds bounds = getAlignment(spec.alignment, horizontal).getBounds();
1188                assoc.put(spec, bounds);
1189            }
1190            return assoc.pack();
1191        }
1192
1193        private void computeGroupBounds() {
1194            Bounds[] values = groupBounds.values;
1195            for (int i = 0; i < values.length; i++) {
1196                values[i].reset();
1197            }
1198            for (int i = 0, N = getChildCount(); i < N; i++) {
1199                View c = getChildAt(i);
1200                // we must include views that are GONE here, see introductory javadoc
1201                LayoutParams lp = getLayoutParams(c);
1202                Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
1203                groupBounds.getValue(i).include(GridLayout.this, c, spec, this);
1204            }
1205        }
1206
1207        public PackedMap<Spec, Bounds> getGroupBounds() {
1208            if (groupBounds == null) {
1209                groupBounds = createGroupBounds();
1210            }
1211            if (!groupBoundsValid) {
1212                computeGroupBounds();
1213                groupBoundsValid = true;
1214            }
1215            return groupBounds;
1216        }
1217
1218        // Add values computed by alignment - taking the max of all alignments in each span
1219        private PackedMap<Interval, MutableInt> createLinks(boolean min) {
1220            Assoc<Interval, MutableInt> result = Assoc.of(Interval.class, MutableInt.class);
1221            Spec[] keys = getGroupBounds().keys;
1222            for (int i = 0, N = keys.length; i < N; i++) {
1223                Interval span = min ? keys[i].span : keys[i].span.inverse();
1224                result.put(span, new MutableInt());
1225            }
1226            return result.pack();
1227        }
1228
1229        private void computeLinks(PackedMap<Interval, MutableInt> links, boolean min) {
1230            MutableInt[] spans = links.values;
1231            for (int i = 0; i < spans.length; i++) {
1232                spans[i].reset();
1233            }
1234
1235            // Use getter to trigger a re-evaluation
1236            Bounds[] bounds = getGroupBounds().values;
1237            for (int i = 0; i < bounds.length; i++) {
1238                int size = bounds[i].size(min);
1239                MutableInt valueHolder = links.getValue(i);
1240                // this effectively takes the max() of the minima and the min() of the maxima
1241                valueHolder.value = max(valueHolder.value, min ? size : -size);
1242            }
1243        }
1244
1245        private PackedMap<Interval, MutableInt> getForwardLinks() {
1246            if (forwardLinks == null) {
1247                forwardLinks = createLinks(true);
1248            }
1249            if (!forwardLinksValid) {
1250                computeLinks(forwardLinks, true);
1251                forwardLinksValid = true;
1252            }
1253            return forwardLinks;
1254        }
1255
1256        private PackedMap<Interval, MutableInt> getBackwardLinks() {
1257            if (backwardLinks == null) {
1258                backwardLinks = createLinks(false);
1259            }
1260            if (!backwardLinksValid) {
1261                computeLinks(backwardLinks, false);
1262                backwardLinksValid = true;
1263            }
1264            return backwardLinks;
1265        }
1266
1267        private void include(List<Arc> arcs, Interval key, MutableInt size,
1268                             boolean ignoreIfAlreadyPresent) {
1269            /*
1270            Remove self referential links.
1271            These appear:
1272                . as parental constraints when GridLayout has no children
1273                . when components have been marked as GONE
1274            */
1275            if (key.size() == 0) {
1276                return;
1277            }
1278            // this bit below should really be computed outside here -
1279            // its just to stop default (row/col > 0) constraints obliterating valid entries
1280            if (ignoreIfAlreadyPresent) {
1281                for (Arc arc : arcs) {
1282                    Interval span = arc.span;
1283                    if (span.equals(key)) {
1284                        return;
1285                    }
1286                }
1287            }
1288            arcs.add(new Arc(key, size));
1289        }
1290
1291        private void include(List<Arc> arcs, Interval key, MutableInt size) {
1292            include(arcs, key, size, true);
1293        }
1294
1295        // Group arcs by their first vertex, returning an array of arrays.
1296        // This is linear in the number of arcs.
1297        Arc[][] groupArcsByFirstVertex(Arc[] arcs) {
1298            int N = getCount() + 1; // the number of vertices
1299            Arc[][] result = new Arc[N][];
1300            int[] sizes = new int[N];
1301            for (Arc arc : arcs) {
1302                sizes[arc.span.min]++;
1303            }
1304            for (int i = 0; i < sizes.length; i++) {
1305                result[i] = new Arc[sizes[i]];
1306            }
1307            // reuse the sizes array to hold the current last elements as we insert each arc
1308            Arrays.fill(sizes, 0);
1309            for (Arc arc : arcs) {
1310                int i = arc.span.min;
1311                result[i][sizes[i]++] = arc;
1312            }
1313
1314            return result;
1315        }
1316
1317        private Arc[] topologicalSort(final Arc[] arcs) {
1318            return new Object() {
1319                Arc[] result = new Arc[arcs.length];
1320                int cursor = result.length - 1;
1321                Arc[][] arcsByVertex = groupArcsByFirstVertex(arcs);
1322                int[] visited = new int[getCount() + 1];
1323
1324                void walk(int loc) {
1325                    switch (visited[loc]) {
1326                        case NEW: {
1327                            visited[loc] = PENDING;
1328                            for (Arc arc : arcsByVertex[loc]) {
1329                                walk(arc.span.max);
1330                                result[cursor--] = arc;
1331                            }
1332                            visited[loc] = COMPLETE;
1333                            break;
1334                        }
1335                        case PENDING: {
1336                            // le singe est dans l'arbre
1337                            assert false;
1338                            break;
1339                        }
1340                        case COMPLETE: {
1341                            break;
1342                        }
1343                    }
1344                }
1345
1346                Arc[] sort() {
1347                    for (int loc = 0, N = arcsByVertex.length; loc < N; loc++) {
1348                        walk(loc);
1349                    }
1350                    assert cursor == -1;
1351                    return result;
1352                }
1353            }.sort();
1354        }
1355
1356        private Arc[] topologicalSort(List<Arc> arcs) {
1357            return topologicalSort(arcs.toArray(new Arc[arcs.size()]));
1358        }
1359
1360        private void addComponentSizes(List<Arc> result, PackedMap<Interval, MutableInt> links) {
1361            for (int i = 0; i < links.keys.length; i++) {
1362                Interval key = links.keys[i];
1363                include(result, key, links.values[i], false);
1364            }
1365        }
1366
1367        private Arc[] createArcs() {
1368            List<Arc> mins = new ArrayList<Arc>();
1369            List<Arc> maxs = new ArrayList<Arc>();
1370
1371            // Add the minimum values from the components.
1372            addComponentSizes(mins, getForwardLinks());
1373            // Add the maximum values from the components.
1374            addComponentSizes(maxs, getBackwardLinks());
1375
1376            // Add ordering constraints to prevent row/col sizes from going negative
1377            if (orderPreserved) {
1378                // Add a constraint for every row/col
1379                for (int i = 0; i < getCount(); i++) {
1380                    include(mins, new Interval(i, i + 1), new MutableInt(0));
1381                }
1382            }
1383
1384            // Add the container constraints. Use the version of include that allows
1385            // duplicate entries in case a child spans the entire grid.
1386            int N = getCount();
1387            include(mins, new Interval(0, N), parentMin, false);
1388            include(maxs, new Interval(N, 0), parentMax, false);
1389
1390            // Sort
1391            Arc[] sMins = topologicalSort(mins);
1392            Arc[] sMaxs = topologicalSort(maxs);
1393
1394            return append(sMins, sMaxs);
1395        }
1396
1397        private void computeArcs() {
1398            // getting the links validates the values that are shared by the arc list
1399            getForwardLinks();
1400            getBackwardLinks();
1401        }
1402
1403        public Arc[] getArcs() {
1404            if (arcs == null) {
1405                arcs = createArcs();
1406            }
1407            if (!arcsValid) {
1408                computeArcs();
1409                arcsValid = true;
1410            }
1411            return arcs;
1412        }
1413
1414        private boolean relax(int[] locations, Arc entry) {
1415            if (!entry.valid) {
1416                return false;
1417            }
1418            Interval span = entry.span;
1419            int u = span.min;
1420            int v = span.max;
1421            int value = entry.value.value;
1422            int candidate = locations[u] + value;
1423            if (candidate > locations[v]) {
1424                locations[v] = candidate;
1425                return true;
1426            }
1427            return false;
1428        }
1429
1430        private void init(int[] locations) {
1431            Arrays.fill(locations, 0);
1432        }
1433
1434        private String arcsToString(List<Arc> arcs) {
1435            String var = horizontal ? "x" : "y";
1436            StringBuilder result = new StringBuilder();
1437            boolean first = true;
1438            for (Arc arc : arcs) {
1439                if (first) {
1440                    first = false;
1441                } else {
1442                    result = result.append(", ");
1443                }
1444                int src = arc.span.min;
1445                int dst = arc.span.max;
1446                int value = arc.value.value;
1447                result.append((src < dst) ?
1448                        var + dst + "-" + var + src + ">=" + value :
1449                        var + src + "-" + var + dst + "<=" + -value);
1450
1451            }
1452            return result.toString();
1453        }
1454
1455        private void logError(String axisName, Arc[] arcs, boolean[] culprits0) {
1456            List<Arc> culprits = new ArrayList<Arc>();
1457            List<Arc> removed = new ArrayList<Arc>();
1458            for (int c = 0; c < arcs.length; c++) {
1459                Arc arc = arcs[c];
1460                if (culprits0[c]) {
1461                    culprits.add(arc);
1462                }
1463                if (!arc.valid) {
1464                    removed.add(arc);
1465                }
1466            }
1467            mPrinter.println(axisName + " constraints: " + arcsToString(culprits) +
1468                    " are inconsistent; permanently removing: " + arcsToString(removed) + ". ");
1469        }
1470
1471        /*
1472        Bellman-Ford variant - modified to reduce typical running time from O(N^2) to O(N)
1473
1474        GridLayout converts its requirements into a system of linear constraints of the
1475        form:
1476
1477        x[i] - x[j] < a[k]
1478
1479        Where the x[i] are variables and the a[k] are constants.
1480
1481        For example, if the variables were instead labeled x, y, z we might have:
1482
1483            x - y < 17
1484            y - z < 23
1485            z - x < 42
1486
1487        This is a special case of the Linear Programming problem that is, in turn,
1488        equivalent to the single-source shortest paths problem on a digraph, for
1489        which the O(n^2) Bellman-Ford algorithm the most commonly used general solution.
1490        */
1491        private void solve(Arc[] arcs, int[] locations) {
1492            String axisName = horizontal ? "horizontal" : "vertical";
1493            int N = getCount() + 1; // The number of vertices is the number of columns/rows + 1.
1494            boolean[] originalCulprits = null;
1495
1496            for (int p = 0; p < arcs.length; p++) {
1497                init(locations);
1498
1499                // We take one extra pass over traditional Bellman-Ford (and omit their final step)
1500                for (int i = 0; i < N; i++) {
1501                    boolean changed = false;
1502                    for (int j = 0, length = arcs.length; j < length; j++) {
1503                        changed |= relax(locations, arcs[j]);
1504                    }
1505                    if (!changed) {
1506                        if (originalCulprits != null) {
1507                            logError(axisName, arcs, originalCulprits);
1508                        }
1509                        return;
1510                    }
1511                }
1512
1513                boolean[] culprits = new boolean[arcs.length];
1514                for (int i = 0; i < N; i++) {
1515                    for (int j = 0, length = arcs.length; j < length; j++) {
1516                        culprits[j] |= relax(locations, arcs[j]);
1517                    }
1518                }
1519
1520                if (p == 0) {
1521                    originalCulprits = culprits;
1522                }
1523
1524                for (int i = 0; i < arcs.length; i++) {
1525                    if (culprits[i]) {
1526                        Arc arc = arcs[i];
1527                        // Only remove max values, min values alone cannot be inconsistent
1528                        if (arc.span.min < arc.span.max) {
1529                            continue;
1530                        }
1531                        arc.valid = false;
1532                        break;
1533                    }
1534                }
1535            }
1536        }
1537
1538        private void computeMargins(boolean leading) {
1539            int[] margins = leading ? leadingMargins : trailingMargins;
1540            for (int i = 0, N = getChildCount(); i < N; i++) {
1541                View c = getChildAt(i);
1542                if (c.getVisibility() == View.GONE) continue;
1543                LayoutParams lp = getLayoutParams(c);
1544                Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
1545                Interval span = spec.span;
1546                int index = leading ? span.min : span.max;
1547                margins[index] = max(margins[index], getMargin1(c, horizontal, leading));
1548            }
1549        }
1550
1551        // External entry points
1552
1553        public int[] getLeadingMargins() {
1554            if (leadingMargins == null) {
1555                leadingMargins = new int[getCount() + 1];
1556            }
1557            if (!leadingMarginsValid) {
1558                computeMargins(true);
1559                leadingMarginsValid = true;
1560            }
1561            return leadingMargins;
1562        }
1563
1564        public int[] getTrailingMargins() {
1565            if (trailingMargins == null) {
1566                trailingMargins = new int[getCount() + 1];
1567            }
1568            if (!trailingMarginsValid) {
1569                computeMargins(false);
1570                trailingMarginsValid = true;
1571            }
1572            return trailingMargins;
1573        }
1574
1575        private void computeLocations(int[] a) {
1576            solve(getArcs(), a);
1577            if (!orderPreserved) {
1578                // Solve returns the smallest solution to the constraint system for which all
1579                // values are positive. One value is therefore zero - though if the row/col
1580                // order is not preserved this may not be the first vertex. For consistency,
1581                // translate all the values so that they measure the distance from a[0]; the
1582                // leading edge of the parent. After this transformation some values may be
1583                // negative.
1584                int a0 = a[0];
1585                for (int i = 0, N = a.length; i < N; i++) {
1586                    a[i] = a[i] - a0;
1587                }
1588            }
1589        }
1590
1591        public int[] getLocations() {
1592            if (locations == null) {
1593                int N = getCount() + 1;
1594                locations = new int[N];
1595            }
1596            if (!locationsValid) {
1597                computeLocations(locations);
1598                locationsValid = true;
1599            }
1600            return locations;
1601        }
1602
1603        private int size(int[] locations) {
1604            // The parental edges are attached to vertices 0 and N - even when order is not
1605            // being preserved and other vertices fall outside this range. Measure the distance
1606            // between vertices 0 and N, assuming that locations[0] = 0.
1607            return locations[getCount()];
1608        }
1609
1610        private void setParentConstraints(int min, int max) {
1611            parentMin.value = min;
1612            parentMax.value = -max;
1613            locationsValid = false;
1614        }
1615
1616        private int getMeasure(int min, int max) {
1617            setParentConstraints(min, max);
1618            return size(getLocations());
1619        }
1620
1621        public int getMeasure(int measureSpec) {
1622            int mode = MeasureSpec.getMode(measureSpec);
1623            int size = MeasureSpec.getSize(measureSpec);
1624            switch (mode) {
1625                case MeasureSpec.UNSPECIFIED: {
1626                    return getMeasure(0, MAX_SIZE);
1627                }
1628                case MeasureSpec.EXACTLY: {
1629                    return getMeasure(size, size);
1630                }
1631                case MeasureSpec.AT_MOST: {
1632                    return getMeasure(0, size);
1633                }
1634                default: {
1635                    assert false;
1636                    return 0;
1637                }
1638            }
1639        }
1640
1641        public void layout(int size) {
1642            setParentConstraints(size, size);
1643            getLocations();
1644        }
1645
1646        public void invalidateStructure() {
1647            maxIndex = UNDEFINED;
1648
1649            groupBounds = null;
1650            forwardLinks = null;
1651            backwardLinks = null;
1652
1653            leadingMargins = null;
1654            trailingMargins = null;
1655            arcs = null;
1656
1657            locations = null;
1658
1659            invalidateValues();
1660        }
1661
1662        public void invalidateValues() {
1663            groupBoundsValid = false;
1664            forwardLinksValid = false;
1665            backwardLinksValid = false;
1666
1667            leadingMarginsValid = false;
1668            trailingMarginsValid = false;
1669            arcsValid = false;
1670
1671            locationsValid = false;
1672        }
1673    }
1674
1675    /**
1676     * Layout information associated with each of the children of a GridLayout.
1677     * <p>
1678     * GridLayout supports both row and column spanning and arbitrary forms of alignment within
1679     * each cell group. The fundamental parameters associated with each cell group are
1680     * gathered into their vertical and horizontal components and stored
1681     * in the {@link #rowSpec} and {@link #columnSpec} layout parameters.
1682     * {@link GridLayout.Spec Specs} are immutable structures
1683     * and may be shared between the layout parameters of different children.
1684     * <p>
1685     * The row and column specs contain the leading and trailing indices along each axis
1686     * and together specify the four grid indices that delimit the cells of this cell group.
1687     * <p>
1688     * The  alignment properties of the row and column specs together specify
1689     * both aspects of alignment within the cell group. It is also possible to specify a child's
1690     * alignment within its cell group by using the {@link GridLayout.LayoutParams#setGravity(int)}
1691     * method.
1692     *
1693     * <h4>WRAP_CONTENT and MATCH_PARENT</h4>
1694     *
1695     * Because the default values of the {@link #width} and {@link #height}
1696     * properties are both {@link #WRAP_CONTENT}, this value never needs to be explicitly
1697     * declared in the layout parameters of GridLayout's children. In addition,
1698     * GridLayout does not distinguish the special size value {@link #MATCH_PARENT} from
1699     * {@link #WRAP_CONTENT}. A component's ability to expand to the size of the parent is
1700     * instead controlled by the principle of <em>flexibility</em>,
1701     * as discussed in {@link GridLayout}.
1702     *
1703     * <h4>Summary</h4>
1704     *
1705     * You should not need to use either of the special size values:
1706     * {@code WRAP_CONTENT} or {@code MATCH_PARENT} when configuring the children of
1707     * a GridLayout.
1708     *
1709     * <h4>Default values</h4>
1710     *
1711     * <ul>
1712     *     <li>{@link #width} = {@link #WRAP_CONTENT}</li>
1713     *     <li>{@link #height} = {@link #WRAP_CONTENT}</li>
1714     *     <li>{@link #topMargin} = 0 when
1715     *          {@link GridLayout#setUseDefaultMargins(boolean) useDefaultMargins} is
1716     *          {@code false}; otherwise {@link #UNDEFINED}, to
1717     *          indicate that a default value should be computed on demand. </li>
1718     *     <li>{@link #leftMargin} = 0 when
1719     *          {@link GridLayout#setUseDefaultMargins(boolean) useDefaultMargins} is
1720     *          {@code false}; otherwise {@link #UNDEFINED}, to
1721     *          indicate that a default value should be computed on demand. </li>
1722     *     <li>{@link #bottomMargin} = 0 when
1723     *          {@link GridLayout#setUseDefaultMargins(boolean) useDefaultMargins} is
1724     *          {@code false}; otherwise {@link #UNDEFINED}, to
1725     *          indicate that a default value should be computed on demand. </li>
1726     *     <li>{@link #rightMargin} = 0 when
1727     *          {@link GridLayout#setUseDefaultMargins(boolean) useDefaultMargins} is
1728     *          {@code false}; otherwise {@link #UNDEFINED}, to
1729     *          indicate that a default value should be computed on demand. </li>
1730     *     <li>{@link #rowSpec}<code>.row</code> = {@link #UNDEFINED} </li>
1731     *     <li>{@link #rowSpec}<code>.rowSpan</code> = 1 </li>
1732     *     <li>{@link #rowSpec}<code>.alignment</code> = {@link #BASELINE} </li>
1733     *     <li>{@link #columnSpec}<code>.column</code> = {@link #UNDEFINED} </li>
1734     *     <li>{@link #columnSpec}<code>.columnSpan</code> = 1 </li>
1735     *     <li>{@link #columnSpec}<code>.alignment</code> = {@link #START} </li>
1736     * </ul>
1737     *
1738     * See {@link GridLayout} for a more complete description of the conventions
1739     * used by GridLayout in the interpretation of the properties of this class.
1740     *
1741     * @attr ref android.R.styleable#GridLayout_Layout_layout_row
1742     * @attr ref android.R.styleable#GridLayout_Layout_layout_rowSpan
1743     * @attr ref android.R.styleable#GridLayout_Layout_layout_column
1744     * @attr ref android.R.styleable#GridLayout_Layout_layout_columnSpan
1745     * @attr ref android.R.styleable#GridLayout_Layout_layout_gravity
1746     */
1747    public static class LayoutParams extends MarginLayoutParams {
1748
1749        // Default values
1750
1751        private static final int DEFAULT_WIDTH = WRAP_CONTENT;
1752        private static final int DEFAULT_HEIGHT = WRAP_CONTENT;
1753        private static final int DEFAULT_MARGIN = UNDEFINED;
1754        private static final int DEFAULT_ROW = UNDEFINED;
1755        private static final int DEFAULT_COLUMN = UNDEFINED;
1756        private static final Interval DEFAULT_SPAN = new Interval(UNDEFINED, UNDEFINED + 1);
1757        private static final int DEFAULT_SPAN_SIZE = DEFAULT_SPAN.size();
1758
1759        // TypedArray indices
1760
1761        private static final int MARGIN = R.styleable.GridLayout_Layout_android_layout_margin;
1762        private static final int LEFT_MARGIN = R.styleable.GridLayout_Layout_android_layout_marginLeft;
1763        private static final int TOP_MARGIN = R.styleable.GridLayout_Layout_android_layout_marginTop;
1764        private static final int RIGHT_MARGIN = R.styleable.GridLayout_Layout_android_layout_marginRight;
1765        private static final int BOTTOM_MARGIN = R.styleable.GridLayout_Layout_android_layout_marginBottom;
1766
1767        private static final int COLUMN = R.styleable.GridLayout_Layout_layout_column;
1768        private static final int COLUMN_SPAN = R.styleable.GridLayout_Layout_layout_columnSpan;
1769
1770        private static final int ROW = R.styleable.GridLayout_Layout_layout_row;
1771        private static final int ROW_SPAN = R.styleable.GridLayout_Layout_layout_rowSpan;
1772
1773        private static final int GRAVITY = R.styleable.GridLayout_Layout_layout_gravity;
1774
1775        // Instance variables
1776
1777        /**
1778         * The spec that defines the vertical characteristics of the cell group
1779         * described by these layout parameters.
1780         * If an assignment is made to this field after a measurement or layout operation
1781         * has already taken place, a call to
1782         * {@link ViewGroup#setLayoutParams(ViewGroup.LayoutParams)}
1783         * must be made to notify GridLayout of the change. GridLayout is normally able
1784         * to detect when code fails to observe this rule, issue a warning and take steps to
1785         * compensate for the omission. This facility is implemented on a best effort basis
1786         * and should not be relied upon in production code - so it is best to include the above
1787         * calls to remove the warnings as soon as it is practical.
1788         */
1789        public Spec rowSpec = Spec.UNDEFINED;
1790
1791        /**
1792         * The spec that defines the horizontal characteristics of the cell group
1793         * described by these layout parameters.
1794         * If an assignment is made to this field after a measurement or layout operation
1795         * has already taken place, a call to
1796         * {@link ViewGroup#setLayoutParams(ViewGroup.LayoutParams)}
1797         * must be made to notify GridLayout of the change. GridLayout is normally able
1798         * to detect when code fails to observe this rule, issue a warning and take steps to
1799         * compensate for the omission. This facility is implemented on a best effort basis
1800         * and should not be relied upon in production code - so it is best to include the above
1801         * calls to remove the warnings as soon as it is practical.
1802         */
1803        public Spec columnSpec = Spec.UNDEFINED;
1804
1805        // Constructors
1806
1807        private LayoutParams(
1808                int width, int height,
1809                int left, int top, int right, int bottom,
1810                Spec rowSpec, Spec columnSpec) {
1811            super(width, height);
1812            setMargins(left, top, right, bottom);
1813            this.rowSpec = rowSpec;
1814            this.columnSpec = columnSpec;
1815        }
1816
1817        /**
1818         * Constructs a new LayoutParams instance for this <code>rowSpec</code>
1819         * and <code>columnSpec</code>. All other fields are initialized with
1820         * default values as defined in {@link LayoutParams}.
1821         *
1822         * @param rowSpec    the rowSpec
1823         * @param columnSpec the columnSpec
1824         */
1825        public LayoutParams(Spec rowSpec, Spec columnSpec) {
1826            this(DEFAULT_WIDTH, DEFAULT_HEIGHT,
1827                    DEFAULT_MARGIN, DEFAULT_MARGIN, DEFAULT_MARGIN, DEFAULT_MARGIN,
1828                    rowSpec, columnSpec);
1829        }
1830
1831        /**
1832         * Constructs a new LayoutParams with default values as defined in {@link LayoutParams}.
1833         */
1834        public LayoutParams() {
1835            this(Spec.UNDEFINED, Spec.UNDEFINED);
1836        }
1837
1838        // Copying constructors
1839
1840        /**
1841         * {@inheritDoc}
1842         */
1843        public LayoutParams(ViewGroup.LayoutParams params) {
1844            super(params);
1845        }
1846
1847        /**
1848         * {@inheritDoc}
1849         */
1850        public LayoutParams(MarginLayoutParams params) {
1851            super(params);
1852        }
1853
1854        /**
1855         * {@inheritDoc}
1856         */
1857        public LayoutParams(LayoutParams that) {
1858            super(that);
1859            this.rowSpec = that.rowSpec;
1860            this.columnSpec = that.columnSpec;
1861        }
1862
1863        // AttributeSet constructors
1864
1865        /**
1866         * {@inheritDoc}
1867         *
1868         * Values not defined in the attribute set take the default values
1869         * defined in {@link LayoutParams}.
1870         */
1871        public LayoutParams(Context context, AttributeSet attrs) {
1872            super(context, attrs);
1873            reInitSuper(context, attrs);
1874            init(context, attrs);
1875        }
1876
1877        // Implementation
1878
1879        // Reinitialise the margins using a different default policy than MarginLayoutParams.
1880        // Here we use the value UNDEFINED (as distinct from zero) to represent the undefined state
1881        // so that a layout manager default can be accessed post set up. We need this as, at the
1882        // point of installation, we do not know how many rows/cols there are and therefore
1883        // which elements are positioned next to the container's trailing edges. We need to
1884        // know this as margins around the container's boundary should have different
1885        // defaults to those between peers.
1886
1887        // This method could be parametrized and moved into MarginLayout.
1888        private void reInitSuper(Context context, AttributeSet attrs) {
1889            TypedArray a =
1890                    context.obtainStyledAttributes(attrs, R.styleable.GridLayout_Layout);
1891            try {
1892                int margin = a.getDimensionPixelSize(MARGIN, DEFAULT_MARGIN);
1893
1894                this.leftMargin = a.getDimensionPixelSize(LEFT_MARGIN, margin);
1895                this.topMargin = a.getDimensionPixelSize(TOP_MARGIN, margin);
1896                this.rightMargin = a.getDimensionPixelSize(RIGHT_MARGIN, margin);
1897                this.bottomMargin = a.getDimensionPixelSize(BOTTOM_MARGIN, margin);
1898            } finally {
1899                a.recycle();
1900            }
1901        }
1902
1903        private void init(Context context, AttributeSet attrs) {
1904            TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.GridLayout_Layout);
1905            try {
1906                int gravity = a.getInt(GRAVITY, Gravity.NO_GRAVITY);
1907
1908                int column = a.getInt(COLUMN, DEFAULT_COLUMN);
1909                int colSpan = a.getInt(COLUMN_SPAN, DEFAULT_SPAN_SIZE);
1910                this.columnSpec = spec(column, colSpan, getAlignment(gravity, true));
1911
1912                int row = a.getInt(ROW, DEFAULT_ROW);
1913                int rowSpan = a.getInt(ROW_SPAN, DEFAULT_SPAN_SIZE);
1914                this.rowSpec = spec(row, rowSpan, getAlignment(gravity, false));
1915            } finally {
1916                a.recycle();
1917            }
1918        }
1919
1920        /**
1921         * Describes how the child views are positioned. Default is {@code LEFT | BASELINE}.
1922         * See {@link Gravity}.
1923         *
1924         * @param gravity the new gravity value
1925         *
1926         * @attr ref android.R.styleable#GridLayout_Layout_layout_gravity
1927         */
1928        public void setGravity(int gravity) {
1929            rowSpec = rowSpec.copyWriteAlignment(getAlignment(gravity, false));
1930            columnSpec = columnSpec.copyWriteAlignment(getAlignment(gravity, true));
1931        }
1932
1933        @Override
1934        protected void setBaseAttributes(TypedArray attributes, int widthAttr, int heightAttr) {
1935            this.width = attributes.getLayoutDimension(widthAttr, DEFAULT_WIDTH);
1936            this.height = attributes.getLayoutDimension(heightAttr, DEFAULT_HEIGHT);
1937        }
1938
1939        final void setRowSpecSpan(Interval span) {
1940            rowSpec = rowSpec.copyWriteSpan(span);
1941        }
1942
1943        final void setColumnSpecSpan(Interval span) {
1944            columnSpec = columnSpec.copyWriteSpan(span);
1945        }
1946
1947        @Override
1948        public boolean equals(Object o) {
1949            if (this == o) return true;
1950            if (o == null || getClass() != o.getClass()) return false;
1951
1952            LayoutParams that = (LayoutParams) o;
1953
1954            if (!columnSpec.equals(that.columnSpec)) return false;
1955            if (!rowSpec.equals(that.rowSpec)) return false;
1956
1957            return true;
1958        }
1959
1960        @Override
1961        public int hashCode() {
1962            int result = rowSpec.hashCode();
1963            result = 31 * result + columnSpec.hashCode();
1964            return result;
1965        }
1966    }
1967
1968    /*
1969    In place of a HashMap from span to Int, use an array of key/value pairs - stored in Arcs.
1970    Add the mutables completesCycle flag to avoid creating another hash table for detecting cycles.
1971     */
1972    final static class Arc {
1973        public final Interval span;
1974        public final MutableInt value;
1975        public boolean valid = true;
1976
1977        public Arc(Interval span, MutableInt value) {
1978            this.span = span;
1979            this.value = value;
1980        }
1981
1982        @Override
1983        public String toString() {
1984            return span + " " + (!valid ? "+>" : "->") + " " + value;
1985        }
1986    }
1987
1988    // A mutable Integer - used to avoid heap allocation during the layout operation
1989
1990    final static class MutableInt {
1991        public int value;
1992
1993        public MutableInt() {
1994            reset();
1995        }
1996
1997        public MutableInt(int value) {
1998            this.value = value;
1999        }
2000
2001        public void reset() {
2002            value = Integer.MIN_VALUE;
2003        }
2004
2005        @Override
2006        public String toString() {
2007            return Integer.toString(value);
2008        }
2009    }
2010
2011    final static class Assoc<K, V> extends ArrayList<Pair<K, V>> {
2012        private final Class<K> keyType;
2013        private final Class<V> valueType;
2014
2015        private Assoc(Class<K> keyType, Class<V> valueType) {
2016            this.keyType = keyType;
2017            this.valueType = valueType;
2018        }
2019
2020        public static <K, V> Assoc<K, V> of(Class<K> keyType, Class<V> valueType) {
2021            return new Assoc<K, V>(keyType, valueType);
2022        }
2023
2024        public void put(K key, V value) {
2025            add(Pair.create(key, value));
2026        }
2027
2028        @SuppressWarnings(value = "unchecked")
2029        public PackedMap<K, V> pack() {
2030            int N = size();
2031            K[] keys = (K[]) Array.newInstance(keyType, N);
2032            V[] values = (V[]) Array.newInstance(valueType, N);
2033            for (int i = 0; i < N; i++) {
2034                keys[i] = get(i).first;
2035                values[i] = get(i).second;
2036            }
2037            return new PackedMap<K, V>(keys, values);
2038        }
2039    }
2040
2041    /*
2042    This data structure is used in place of a Map where we have an index that refers to the order
2043    in which each key/value pairs were added to the map. In this case we store keys and values
2044    in arrays of a length that is equal to the number of unique keys. We also maintain an
2045    array of indexes from insertion order to the compacted arrays of keys and values.
2046
2047    Note that behavior differs from that of a LinkedHashMap in that repeated entries
2048    *do* get added multiples times. So the length of index is equals to the number of
2049    items added.
2050
2051    This is useful in the GridLayout class where we can rely on the order of children not
2052    changing during layout - to use integer-based lookup for our internal structures
2053    rather than using (and storing) an implementation of Map<Key, ?>.
2054     */
2055    @SuppressWarnings(value = "unchecked")
2056    final static class PackedMap<K, V> {
2057        public final int[] index;
2058        public final K[] keys;
2059        public final V[] values;
2060
2061        private PackedMap(K[] keys, V[] values) {
2062            this.index = createIndex(keys);
2063
2064            this.keys = compact(keys, index);
2065            this.values = compact(values, index);
2066        }
2067
2068        public V getValue(int i) {
2069            return values[index[i]];
2070        }
2071
2072        private static <K> int[] createIndex(K[] keys) {
2073            int size = keys.length;
2074            int[] result = new int[size];
2075
2076            Map<K, Integer> keyToIndex = new HashMap<K, Integer>();
2077            for (int i = 0; i < size; i++) {
2078                K key = keys[i];
2079                Integer index = keyToIndex.get(key);
2080                if (index == null) {
2081                    index = keyToIndex.size();
2082                    keyToIndex.put(key, index);
2083                }
2084                result[i] = index;
2085            }
2086            return result;
2087        }
2088
2089        /*
2090        Create a compact array of keys or values using the supplied index.
2091         */
2092        private static <K> K[] compact(K[] a, int[] index) {
2093            int size = a.length;
2094            Class<?> componentType = a.getClass().getComponentType();
2095            K[] result = (K[]) Array.newInstance(componentType, max2(index, -1) + 1);
2096
2097            // this overwrite duplicates, retaining the last equivalent entry
2098            for (int i = 0; i < size; i++) {
2099                result[index[i]] = a[i];
2100            }
2101            return result;
2102        }
2103    }
2104
2105    /*
2106    For each group (with a given alignment) we need to store the amount of space required
2107    before the alignment point and the amount of space required after it. One side of this
2108    calculation is always 0 for START and END alignments but we don't make use of this.
2109    For CENTER and BASELINE alignments both sides are needed and in the BASELINE case no
2110    simple optimisations are possible.
2111
2112    The general algorithm therefore is to create a Map (actually a PackedMap) from
2113    group to Bounds and to loop through all Views in the group taking the maximum
2114    of the values for each View.
2115    */
2116    static class Bounds {
2117        public int before;
2118        public int after;
2119        public int flexibility; // we're flexible iff all included specs are flexible
2120
2121        private Bounds() {
2122            reset();
2123        }
2124
2125        protected void reset() {
2126            before = Integer.MIN_VALUE;
2127            after = Integer.MIN_VALUE;
2128            flexibility = CAN_STRETCH; // from the above, we're flexible when empty
2129        }
2130
2131        protected void include(int before, int after) {
2132            this.before = max(this.before, before);
2133            this.after = max(this.after, after);
2134        }
2135
2136        protected int size(boolean min) {
2137            if (!min) {
2138                if (canStretch(flexibility)) {
2139                    return MAX_SIZE;
2140                }
2141            }
2142            return before + after;
2143        }
2144
2145        protected int getOffset(GridLayout gl, View c, Alignment a, int size, boolean horizontal) {
2146            return before - a.getAlignmentValue(c, size, ViewGroupCompat.getLayoutMode(gl));
2147        }
2148
2149        protected final void include(GridLayout gl, View c, Spec spec, Axis axis) {
2150            this.flexibility &= spec.getFlexibility();
2151            boolean horizontal = axis.horizontal;
2152            int size = gl.getMeasurementIncludingMargin(c, horizontal);
2153            Alignment alignment = gl.getAlignment(spec.alignment, horizontal);
2154            // todo test this works correctly when the returned value is UNDEFINED
2155            int before = alignment.getAlignmentValue(c, size, ViewGroupCompat.getLayoutMode(gl));
2156            include(before, size - before);
2157        }
2158
2159        @Override
2160        public String toString() {
2161            return "Bounds{" +
2162                    "before=" + before +
2163                    ", after=" + after +
2164                    '}';
2165        }
2166    }
2167
2168    /**
2169     * An Interval represents a contiguous range of values that lie between
2170     * the interval's {@link #min} and {@link #max} values.
2171     * <p>
2172     * Intervals are immutable so may be passed as values and used as keys in hash tables.
2173     * It is not necessary to have multiple instances of Intervals which have the same
2174     * {@link #min} and {@link #max} values.
2175     * <p>
2176     * Intervals are often written as {@code [min, max]} and represent the set of values
2177     * {@code x} such that {@code min <= x < max}.
2178     */
2179    final static class Interval {
2180        /**
2181         * The minimum value.
2182         */
2183        public final int min;
2184
2185        /**
2186         * The maximum value.
2187         */
2188        public final int max;
2189
2190        /**
2191         * Construct a new Interval, {@code interval}, where:
2192         * <ul>
2193         *     <li> {@code interval.min = min} </li>
2194         *     <li> {@code interval.max = max} </li>
2195         * </ul>
2196         *
2197         * @param min the minimum value.
2198         * @param max the maximum value.
2199         */
2200        public Interval(int min, int max) {
2201            this.min = min;
2202            this.max = max;
2203        }
2204
2205        int size() {
2206            return max - min;
2207        }
2208
2209        Interval inverse() {
2210            return new Interval(max, min);
2211        }
2212
2213        /**
2214         * Returns {@code true} if the {@link #getClass class},
2215         * {@link #min} and {@link #max} properties of this Interval and the
2216         * supplied parameter are pairwise equal; {@code false} otherwise.
2217         *
2218         * @param that the object to compare this interval with
2219         *
2220         * @return {@code true} if the specified object is equal to this
2221         *         {@code Interval}, {@code false} otherwise.
2222         */
2223        @Override
2224        public boolean equals(Object that) {
2225            if (this == that) {
2226                return true;
2227            }
2228            if (that == null || getClass() != that.getClass()) {
2229                return false;
2230            }
2231
2232            Interval interval = (Interval) that;
2233
2234            if (max != interval.max) {
2235                return false;
2236            }
2237            //noinspection RedundantIfStatement
2238            if (min != interval.min) {
2239                return false;
2240            }
2241
2242            return true;
2243        }
2244
2245        @Override
2246        public int hashCode() {
2247            int result = min;
2248            result = 31 * result + max;
2249            return result;
2250        }
2251
2252        @Override
2253        public String toString() {
2254            return "[" + min + ", " + max + "]";
2255        }
2256    }
2257
2258    /**
2259     * A Spec defines the horizontal or vertical characteristics of a group of
2260     * cells. Each spec. defines the <em>grid indices</em> and <em>alignment</em>
2261     * along the appropriate axis.
2262     * <p>
2263     * The <em>grid indices</em> are the leading and trailing edges of this cell group.
2264     * See {@link GridLayout} for a description of the conventions used by GridLayout
2265     * for grid indices.
2266     * <p>
2267     * The <em>alignment</em> property specifies how cells should be aligned in this group.
2268     * For row groups, this specifies the vertical alignment.
2269     * For column groups, this specifies the horizontal alignment.
2270     * <p>
2271     * Use the following static methods to create specs:
2272     * <ul>
2273     *   <li>{@link #spec(int)}</li>
2274     *   <li>{@link #spec(int, int)}</li>
2275     *   <li>{@link #spec(int, Alignment)}</li>
2276     *   <li>{@link #spec(int, int, Alignment)}</li>
2277     * </ul>
2278     *
2279     */
2280    public static class Spec {
2281        static final Spec UNDEFINED = spec(GridLayout.UNDEFINED);
2282
2283        final boolean startDefined;
2284        final Interval span;
2285        final Alignment alignment;
2286
2287        private Spec(boolean startDefined, Interval span, Alignment alignment) {
2288            this.startDefined = startDefined;
2289            this.span = span;
2290            this.alignment = alignment;
2291        }
2292
2293        private Spec(boolean startDefined, int start, int size, Alignment alignment) {
2294            this(startDefined, new Interval(start, start + size), alignment);
2295        }
2296
2297        final Spec copyWriteSpan(Interval span) {
2298            return new Spec(startDefined, span, alignment);
2299        }
2300
2301        final Spec copyWriteAlignment(Alignment alignment) {
2302            return new Spec(startDefined, span, alignment);
2303        }
2304
2305        final int getFlexibility() {
2306            return (alignment == UNDEFINED_ALIGNMENT) ? INFLEXIBLE : CAN_STRETCH;
2307        }
2308
2309        /**
2310         * Returns {@code true} if the {@code class}, {@code alignment} and {@code span}
2311         * properties of this Spec and the supplied parameter are pairwise equal,
2312         * {@code false} otherwise.
2313         *
2314         * @param that the object to compare this spec with
2315         *
2316         * @return {@code true} if the specified object is equal to this
2317         *         {@code Spec}; {@code false} otherwise
2318         */
2319        @Override
2320        public boolean equals(Object that) {
2321            if (this == that) {
2322                return true;
2323            }
2324            if (that == null || getClass() != that.getClass()) {
2325                return false;
2326            }
2327
2328            Spec spec = (Spec) that;
2329
2330            if (!alignment.equals(spec.alignment)) {
2331                return false;
2332            }
2333            //noinspection RedundantIfStatement
2334            if (!span.equals(spec.span)) {
2335                return false;
2336            }
2337
2338            return true;
2339        }
2340
2341        @Override
2342        public int hashCode() {
2343            int result = span.hashCode();
2344            result = 31 * result + alignment.hashCode();
2345            return result;
2346        }
2347    }
2348
2349    /**
2350     * Return a Spec, {@code spec}, where:
2351     * <ul>
2352     *     <li> {@code spec.span = [start, start + size]} </li>
2353     *     <li> {@code spec.alignment = alignment} </li>
2354     * </ul>
2355     * <p>
2356     * To leave the start index undefined, use the value {@link #UNDEFINED}.
2357     *
2358     * @param start     the start
2359     * @param size      the size
2360     * @param alignment the alignment
2361     */
2362    public static Spec spec(int start, int size, Alignment alignment) {
2363        return new Spec(start != UNDEFINED, start, size, alignment);
2364    }
2365
2366    /**
2367     * Return a Spec, {@code spec}, where:
2368     * <ul>
2369     *     <li> {@code spec.span = [start, start + 1]} </li>
2370     *     <li> {@code spec.alignment = alignment} </li>
2371     * </ul>
2372     * <p>
2373     * To leave the start index undefined, use the value {@link #UNDEFINED}.
2374     *
2375     * @param start     the start index
2376     * @param alignment the alignment
2377     *
2378     * @see #spec(int, int, Alignment)
2379     */
2380    public static Spec spec(int start, Alignment alignment) {
2381        return spec(start, 1, alignment);
2382    }
2383
2384    /**
2385     * Return a Spec, {@code spec}, where:
2386     * <ul>
2387     *     <li> {@code spec.span = [start, start + size]} </li>
2388     * </ul>
2389     * <p>
2390     * To leave the start index undefined, use the value {@link #UNDEFINED}.
2391     *
2392     * @param start     the start
2393     * @param size      the size
2394     *
2395     * @see #spec(int, Alignment)
2396     */
2397    public static Spec spec(int start, int size) {
2398        return spec(start, size, UNDEFINED_ALIGNMENT);
2399    }
2400
2401    /**
2402     * Return a Spec, {@code spec}, where:
2403     * <ul>
2404     *     <li> {@code spec.span = [start, start + 1]} </li>
2405     * </ul>
2406     * <p>
2407     * To leave the start index undefined, use the value {@link #UNDEFINED}.
2408     *
2409     * @param start     the start index
2410     *
2411     * @see #spec(int, int)
2412     */
2413    public static Spec spec(int start) {
2414        return spec(start, 1);
2415    }
2416
2417    /**
2418     * Alignments specify where a view should be placed within a cell group and
2419     * what size it should be.
2420     * <p>
2421     * The {@link LayoutParams} class contains a {@link LayoutParams#rowSpec rowSpec}
2422     * and a {@link LayoutParams#columnSpec columnSpec} each of which contains an
2423     * {@code alignment}. Overall placement of the view in the cell
2424     * group is specified by the two alignments which act along each axis independently.
2425     * <p>
2426     *  The GridLayout class defines the most common alignments used in general layout:
2427     * {@link #TOP}, {@link #LEFT}, {@link #BOTTOM}, {@link #RIGHT}, {@link #START},
2428     * {@link #END}, {@link #CENTER}, {@link #BASELINE} and {@link #FILL}.
2429     */
2430    /*
2431     * An Alignment implementation must define {@link #getAlignmentValue(View, int, int)},
2432     * to return the appropriate value for the type of alignment being defined.
2433     * The enclosing algorithms position the children
2434     * so that the locations defined by the alignment values
2435     * are the same for all of the views in a group.
2436     * <p>
2437     */
2438    public static abstract class Alignment {
2439        Alignment() {
2440        }
2441
2442        abstract int getGravityOffset(View view, int cellDelta);
2443
2444        /**
2445         * Returns an alignment value. In the case of vertical alignments the value
2446         * returned should indicate the distance from the top of the view to the
2447         * alignment location.
2448         * For horizontal alignments measurement is made from the left edge of the component.
2449         *
2450         * @param view              the view to which this alignment should be applied
2451         * @param viewSize          the measured size of the view
2452         * @param mode              the basis of alignment: CLIP or OPTICAL
2453         * @return the alignment value
2454         */
2455        abstract int getAlignmentValue(View view, int viewSize, int mode);
2456
2457        /**
2458         * Returns the size of the view specified by this alignment.
2459         * In the case of vertical alignments this method should return a height; for
2460         * horizontal alignments this method should return the width.
2461         * <p>
2462         * The default implementation returns {@code viewSize}.
2463         *
2464         * @param view              the view to which this alignment should be applied
2465         * @param viewSize          the measured size of the view
2466         * @param cellSize          the size of the cell into which this view will be placed
2467         * @return the aligned size
2468         */
2469        int getSizeInCell(View view, int viewSize, int cellSize) {
2470            return viewSize;
2471        }
2472
2473        Bounds getBounds() {
2474            return new Bounds();
2475        }
2476    }
2477
2478    static final Alignment UNDEFINED_ALIGNMENT = new Alignment() {
2479        @Override
2480        int getGravityOffset(View view, int cellDelta) {
2481            return UNDEFINED;
2482        }
2483
2484        @Override
2485        public int getAlignmentValue(View view, int viewSize, int mode) {
2486            return UNDEFINED;
2487        }
2488    };
2489
2490    /**
2491     * Indicates that a view should be aligned with the <em>start</em>
2492     * edges of the other views in its cell group.
2493     */
2494    private static final Alignment LEADING = new Alignment() {
2495        @Override
2496        int getGravityOffset(View view, int cellDelta) {
2497            return 0;
2498        }
2499
2500        @Override
2501        public int getAlignmentValue(View view, int viewSize, int mode) {
2502            return 0;
2503        }
2504    };
2505
2506    /**
2507     * Indicates that a view should be aligned with the <em>end</em>
2508     * edges of the other views in its cell group.
2509     */
2510    private static final Alignment TRAILING = new Alignment() {
2511        @Override
2512        int getGravityOffset(View view, int cellDelta) {
2513            return cellDelta;
2514        }
2515
2516        @Override
2517        public int getAlignmentValue(View view, int viewSize, int mode) {
2518            return viewSize;
2519        }
2520    };
2521
2522    /**
2523     * Indicates that a view should be aligned with the <em>top</em>
2524     * edges of the other views in its cell group.
2525     */
2526    public static final Alignment TOP = LEADING;
2527
2528    /**
2529     * Indicates that a view should be aligned with the <em>bottom</em>
2530     * edges of the other views in its cell group.
2531     */
2532    public static final Alignment BOTTOM = TRAILING;
2533
2534    /**
2535     * Indicates that a view should be aligned with the <em>start</em>
2536     * edges of the other views in its cell group.
2537     */
2538    public static final Alignment START = LEADING;
2539
2540    /**
2541     * Indicates that a view should be aligned with the <em>end</em>
2542     * edges of the other views in its cell group.
2543     */
2544    public static final Alignment END = TRAILING;
2545
2546    private static Alignment createSwitchingAlignment(final Alignment ltr, final Alignment rtl) {
2547        return new Alignment() {
2548            @Override
2549            int getGravityOffset(View view, int cellDelta) {
2550                boolean isLayoutRtl = ViewCompat.getLayoutDirection(view) ==
2551                        ViewCompat.LAYOUT_DIRECTION_RTL;
2552                return (!isLayoutRtl ? ltr : rtl).getGravityOffset(view, cellDelta);
2553            }
2554
2555            @Override
2556            public int getAlignmentValue(View view, int viewSize, int mode) {
2557                boolean isLayoutRtl = ViewCompat.getLayoutDirection(view) ==
2558                        ViewCompat.LAYOUT_DIRECTION_RTL;
2559                return (!isLayoutRtl ? ltr : rtl).getAlignmentValue(view, viewSize, mode);
2560            }
2561        };
2562    }
2563
2564    /**
2565     * Indicates that a view should be aligned with the <em>left</em>
2566     * edges of the other views in its cell group.
2567     */
2568    public static final Alignment LEFT = createSwitchingAlignment(START, END);
2569
2570    /**
2571     * Indicates that a view should be aligned with the <em>right</em>
2572     * edges of the other views in its cell group.
2573     */
2574    public static final Alignment RIGHT = createSwitchingAlignment(END, START);
2575
2576    /**
2577     * Indicates that a view should be <em>centered</em> with the other views in its cell group.
2578     * This constant may be used in both {@link LayoutParams#rowSpec rowSpecs} and {@link
2579     * LayoutParams#columnSpec columnSpecs}.
2580     */
2581    public static final Alignment CENTER = new Alignment() {
2582        @Override
2583        int getGravityOffset(View view, int cellDelta) {
2584            return cellDelta >> 1;
2585        }
2586
2587        @Override
2588        public int getAlignmentValue(View view, int viewSize, int mode) {
2589            return viewSize >> 1;
2590        }
2591    };
2592
2593    /**
2594     * Indicates that a view should be aligned with the <em>baselines</em>
2595     * of the other views in its cell group.
2596     * This constant may only be used as an alignment in {@link LayoutParams#rowSpec rowSpecs}.
2597     *
2598     * @see View#getBaseline()
2599     */
2600    public static final Alignment BASELINE = new Alignment() {
2601        @Override
2602        int getGravityOffset(View view, int cellDelta) {
2603            return 0; // baseline gravity is top
2604        }
2605
2606        @Override
2607        public int getAlignmentValue(View view, int viewSize, int mode) {
2608            if (view.getVisibility() == GONE) {
2609                return 0;
2610            }
2611            int baseline = view.getBaseline();
2612            return baseline == -1 ? UNDEFINED : baseline;
2613        }
2614
2615        @Override
2616        public Bounds getBounds() {
2617            return new Bounds() {
2618                /*
2619                In a baseline aligned row in which some components define a baseline
2620                and some don't, we need a third variable to properly account for all
2621                the sizes. This tracks the maximum size of all the components -
2622                including those that don't define a baseline.
2623                */
2624                private int size;
2625
2626                @Override
2627                protected void reset() {
2628                    super.reset();
2629                    size = Integer.MIN_VALUE;
2630                }
2631
2632                @Override
2633                protected void include(int before, int after) {
2634                    super.include(before, after);
2635                    size = max(size, before + after);
2636                }
2637
2638                @Override
2639                protected int size(boolean min) {
2640                    return max(super.size(min), size);
2641                }
2642
2643                @Override
2644                protected int getOffset(GridLayout gl, View c, Alignment a, int size, boolean hrz) {
2645                    return max(0, super.getOffset(gl, c, a, size, hrz));
2646                }
2647            };
2648        }
2649    };
2650
2651    /**
2652     * Indicates that a view should expanded to fit the boundaries of its cell group.
2653     * This constant may be used in both {@link LayoutParams#rowSpec rowSpecs} and
2654     * {@link LayoutParams#columnSpec columnSpecs}.
2655     */
2656    public static final Alignment FILL = new Alignment() {
2657        @Override
2658        int getGravityOffset(View view, int cellDelta) {
2659            return 0;
2660        }
2661
2662        @Override
2663        public int getAlignmentValue(View view, int viewSize, int mode) {
2664            return UNDEFINED;
2665        }
2666
2667        @Override
2668        public int getSizeInCell(View view, int viewSize, int cellSize) {
2669            return cellSize;
2670        }
2671    };
2672
2673    static boolean canStretch(int flexibility) {
2674        return (flexibility & CAN_STRETCH) != 0;
2675    }
2676
2677    private static final int INFLEXIBLE = 0;
2678    private static final int CAN_STRETCH = 2;
2679}
2680