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