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