GridLayout.java revision 98d5f04f91580bf8b517c17132eeb8d42008b61d
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 boolean solve(Arc[] arcs, int[] locations) {
1617            return solve(arcs, locations, true);
1618        }
1619
1620        private boolean solve(Arc[] arcs, int[] locations, boolean modifyOnError) {
1621            String axisName = horizontal ? "horizontal" : "vertical";
1622            int N = getCount() + 1; // The number of vertices is the number of columns/rows + 1.
1623            boolean[] originalCulprits = null;
1624
1625            for (int p = 0; p < arcs.length; p++) {
1626                init(locations);
1627
1628                // We take one extra pass over traditional Bellman-Ford (and omit their final step)
1629                for (int i = 0; i < N; i++) {
1630                    boolean changed = false;
1631                    for (int j = 0, length = arcs.length; j < length; j++) {
1632                        changed |= relax(locations, arcs[j]);
1633                    }
1634                    if (!changed) {
1635                        if (originalCulprits != null) {
1636                            logError(axisName, arcs, originalCulprits);
1637                        }
1638                        return true;
1639                    }
1640                }
1641
1642                if (!modifyOnError) {
1643                    return false; // cannot solve with these constraints
1644                }
1645
1646                boolean[] culprits = new boolean[arcs.length];
1647                for (int i = 0; i < N; i++) {
1648                    for (int j = 0, length = arcs.length; j < length; j++) {
1649                        culprits[j] |= relax(locations, arcs[j]);
1650                    }
1651                }
1652
1653                if (p == 0) {
1654                    originalCulprits = culprits;
1655                }
1656
1657                for (int i = 0; i < arcs.length; i++) {
1658                    if (culprits[i]) {
1659                        Arc arc = arcs[i];
1660                        // Only remove max values, min values alone cannot be inconsistent
1661                        if (arc.span.min < arc.span.max) {
1662                            continue;
1663                        }
1664                        arc.valid = false;
1665                        break;
1666                    }
1667                }
1668            }
1669            return true;
1670        }
1671
1672        private void computeMargins(boolean leading) {
1673            int[] margins = leading ? leadingMargins : trailingMargins;
1674            for (int i = 0, N = getChildCount(); i < N; i++) {
1675                View c = getChildAt(i);
1676                if (c.getVisibility() == View.GONE) continue;
1677                LayoutParams lp = getLayoutParams(c);
1678                Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
1679                Interval span = spec.span;
1680                int index = leading ? span.min : span.max;
1681                margins[index] = max(margins[index], getMargin1(c, horizontal, leading));
1682            }
1683        }
1684
1685        // External entry points
1686
1687        public int[] getLeadingMargins() {
1688            if (leadingMargins == null) {
1689                leadingMargins = new int[getCount() + 1];
1690            }
1691            if (!leadingMarginsValid) {
1692                computeMargins(true);
1693                leadingMarginsValid = true;
1694            }
1695            return leadingMargins;
1696        }
1697
1698        public int[] getTrailingMargins() {
1699            if (trailingMargins == null) {
1700                trailingMargins = new int[getCount() + 1];
1701            }
1702            if (!trailingMarginsValid) {
1703                computeMargins(false);
1704                trailingMarginsValid = true;
1705            }
1706            return trailingMargins;
1707        }
1708
1709        private boolean solve(int[] a) {
1710            return solve(getArcs(), a);
1711        }
1712
1713        private boolean computeHasWeights() {
1714            for (int i = 0, N = getChildCount(); i < N; i++) {
1715                LayoutParams lp = getLayoutParams(getChildAt(i));
1716                Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
1717                if (spec.weight != 0) {
1718                    return true;
1719                }
1720            }
1721            return false;
1722        }
1723
1724        private boolean hasWeights() {
1725            if (!hasWeightsValid) {
1726                hasWeights = computeHasWeights();
1727                hasWeightsValid = true;
1728            }
1729            return hasWeights;
1730        }
1731
1732        public int[] getOriginalMeasurements() {
1733            if (originalMeasurements == null) {
1734                originalMeasurements = new int[getChildCount()];
1735            }
1736            return originalMeasurements;
1737        }
1738
1739        private void recordOriginalMeasurement(int i) {
1740            if (hasWeights()) {
1741                getOriginalMeasurements()[i] = getMeasurementIncludingMargin(getChildAt(i), horizontal);
1742            }
1743        }
1744
1745        public int[] getDeltas() {
1746            if (deltas == null) {
1747                deltas = new int[getChildCount()];
1748            }
1749            return deltas;
1750        }
1751
1752        private void shareOutDelta(int totalDelta, float totalWeight) {
1753            Arrays.fill(deltas, 0);
1754            for (int i = 0, N = getChildCount(); i < N; i++) {
1755                View c = getChildAt(i);
1756                LayoutParams lp = getLayoutParams(c);
1757                Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
1758                float weight = spec.weight;
1759                if (weight != 0) {
1760                    int delta = Math.round((weight * totalDelta / totalWeight));
1761                    deltas[i] = delta;
1762                    // the two adjustments below are to counter the above rounding and avoid
1763                    // off-by-ones at the end
1764                    totalDelta -= delta;
1765                    totalWeight -= weight;
1766                }
1767            }
1768        }
1769
1770        private void solveAndDistributeSpace(int[] a) {
1771            Arrays.fill(getDeltas(), 0);
1772            solve(a);
1773            int deltaMax = parentMin.value * getChildCount() + 1; //exclusive
1774            if (deltaMax < 2) {
1775                return; //don't have any delta to distribute
1776            }
1777            int deltaMin = 0; //inclusive
1778
1779            float totalWeight = calculateTotalWeight();
1780
1781            int validDelta = -1; //delta for which a solution exists
1782            boolean validSolution = true;
1783            // do a binary search to find the max delta that won't conflict with constraints
1784            while(deltaMin < deltaMax) {
1785                final int delta = (deltaMin + deltaMax) / 2;
1786                invalidateValues();
1787                shareOutDelta(delta, totalWeight);
1788                validSolution = solve(getArcs(), a, false);
1789                if (validSolution) {
1790                    validDelta = delta;
1791                    deltaMin = delta + 1;
1792                } else {
1793                    deltaMax = delta;
1794                }
1795            }
1796            if (validDelta > 0 && !validSolution) {
1797                // last solution was not successful but we have a successful one. Use it.
1798                invalidateValues();
1799                shareOutDelta(validDelta, totalWeight);
1800                solve(a);
1801            }
1802        }
1803
1804        private float calculateTotalWeight() {
1805            float totalWeight = 0f;
1806            for (int i = 0, N = getChildCount(); i < N; i++) {
1807                View c = getChildAt(i);
1808                LayoutParams lp = getLayoutParams(c);
1809                Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
1810                totalWeight += spec.weight;
1811            }
1812            return totalWeight;
1813        }
1814
1815        private void computeLocations(int[] a) {
1816            if (!hasWeights()) {
1817                solve(a);
1818            } else {
1819                solveAndDistributeSpace(a);
1820            }
1821            if (!orderPreserved) {
1822                // Solve returns the smallest solution to the constraint system for which all
1823                // values are positive. One value is therefore zero - though if the row/col
1824                // order is not preserved this may not be the first vertex. For consistency,
1825                // translate all the values so that they measure the distance from a[0]; the
1826                // leading edge of the parent. After this transformation some values may be
1827                // negative.
1828                int a0 = a[0];
1829                for (int i = 0, N = a.length; i < N; i++) {
1830                    a[i] = a[i] - a0;
1831                }
1832            }
1833        }
1834
1835        public int[] getLocations() {
1836            if (locations == null) {
1837                int N = getCount() + 1;
1838                locations = new int[N];
1839            }
1840            if (!locationsValid) {
1841                computeLocations(locations);
1842                locationsValid = true;
1843            }
1844            return locations;
1845        }
1846
1847        private int size(int[] locations) {
1848            // The parental edges are attached to vertices 0 and N - even when order is not
1849            // being preserved and other vertices fall outside this range. Measure the distance
1850            // between vertices 0 and N, assuming that locations[0] = 0.
1851            return locations[getCount()];
1852        }
1853
1854        private void setParentConstraints(int min, int max) {
1855            parentMin.value = min;
1856            parentMax.value = -max;
1857            locationsValid = false;
1858        }
1859
1860        private int getMeasure(int min, int max) {
1861            setParentConstraints(min, max);
1862            return size(getLocations());
1863        }
1864
1865        public int getMeasure(int measureSpec) {
1866            int mode = MeasureSpec.getMode(measureSpec);
1867            int size = MeasureSpec.getSize(measureSpec);
1868            switch (mode) {
1869                case MeasureSpec.UNSPECIFIED: {
1870                    return getMeasure(0, MAX_SIZE);
1871                }
1872                case MeasureSpec.EXACTLY: {
1873                    return getMeasure(size, size);
1874                }
1875                case MeasureSpec.AT_MOST: {
1876                    return getMeasure(0, size);
1877                }
1878                default: {
1879                    assert false;
1880                    return 0;
1881                }
1882            }
1883        }
1884
1885        public void layout(int size) {
1886            setParentConstraints(size, size);
1887            getLocations();
1888        }
1889
1890        public void invalidateStructure() {
1891            maxIndex = UNDEFINED;
1892
1893            groupBounds = null;
1894            forwardLinks = null;
1895            backwardLinks = null;
1896
1897            leadingMargins = null;
1898            trailingMargins = null;
1899            arcs = null;
1900
1901            locations = null;
1902
1903            originalMeasurements = null;
1904            deltas = null;
1905            hasWeightsValid = false;
1906
1907            invalidateValues();
1908        }
1909
1910        public void invalidateValues() {
1911            groupBoundsValid = false;
1912            forwardLinksValid = false;
1913            backwardLinksValid = false;
1914
1915            leadingMarginsValid = false;
1916            trailingMarginsValid = false;
1917            arcsValid = false;
1918
1919            locationsValid = false;
1920        }
1921    }
1922
1923    /**
1924     * Layout information associated with each of the children of a GridLayout.
1925     * <p>
1926     * GridLayout supports both row and column spanning and arbitrary forms of alignment within
1927     * each cell group. The fundamental parameters associated with each cell group are
1928     * gathered into their vertical and horizontal components and stored
1929     * in the {@link #rowSpec} and {@link #columnSpec} layout parameters.
1930     * {@link GridLayout.Spec Specs} are immutable structures
1931     * and may be shared between the layout parameters of different children.
1932     * <p>
1933     * The row and column specs contain the leading and trailing indices along each axis
1934     * and together specify the four grid indices that delimit the cells of this cell group.
1935     * <p>
1936     * The  alignment properties of the row and column specs together specify
1937     * both aspects of alignment within the cell group. It is also possible to specify a child's
1938     * alignment within its cell group by using the {@link GridLayout.LayoutParams#setGravity(int)}
1939     * method.
1940     * <p>
1941     * The weight property is also included in Spec and specifies the proportion of any
1942     * excess space that is due to the associated view.
1943     *
1944     * <h4>WRAP_CONTENT and MATCH_PARENT</h4>
1945     *
1946     * Because the default values of the {@link #width} and {@link #height}
1947     * properties are both {@link #WRAP_CONTENT}, this value never needs to be explicitly
1948     * declared in the layout parameters of GridLayout's children. In addition,
1949     * GridLayout does not distinguish the special size value {@link #MATCH_PARENT} from
1950     * {@link #WRAP_CONTENT}. A component's ability to expand to the size of the parent is
1951     * instead controlled by the principle of <em>flexibility</em>,
1952     * as discussed in {@link GridLayout}.
1953     *
1954     * <h4>Summary</h4>
1955     *
1956     * You should not need to use either of the special size values:
1957     * {@code WRAP_CONTENT} or {@code MATCH_PARENT} when configuring the children of
1958     * a GridLayout.
1959     *
1960     * <h4>Default values</h4>
1961     *
1962     * <ul>
1963     *     <li>{@link #width} = {@link #WRAP_CONTENT}</li>
1964     *     <li>{@link #height} = {@link #WRAP_CONTENT}</li>
1965     *     <li>{@link #topMargin} = 0 when
1966     *          {@link GridLayout#setUseDefaultMargins(boolean) useDefaultMargins} is
1967     *          {@code false}; otherwise {@link #UNDEFINED}, to
1968     *          indicate that a default value should be computed on demand. </li>
1969     *     <li>{@link #leftMargin} = 0 when
1970     *          {@link GridLayout#setUseDefaultMargins(boolean) useDefaultMargins} is
1971     *          {@code false}; otherwise {@link #UNDEFINED}, to
1972     *          indicate that a default value should be computed on demand. </li>
1973     *     <li>{@link #bottomMargin} = 0 when
1974     *          {@link GridLayout#setUseDefaultMargins(boolean) useDefaultMargins} is
1975     *          {@code false}; otherwise {@link #UNDEFINED}, to
1976     *          indicate that a default value should be computed on demand. </li>
1977     *     <li>{@link #rightMargin} = 0 when
1978     *          {@link GridLayout#setUseDefaultMargins(boolean) useDefaultMargins} is
1979     *          {@code false}; otherwise {@link #UNDEFINED}, to
1980     *          indicate that a default value should be computed on demand. </li>
1981     *     <li>{@link #rowSpec}<code>.row</code> = {@link #UNDEFINED} </li>
1982     *     <li>{@link #rowSpec}<code>.rowSpan</code> = 1 </li>
1983     *     <li>{@link #rowSpec}<code>.alignment</code> = {@link #BASELINE} </li>
1984     *     <li>{@link #rowSpec}<code>.weight</code> = 0 </li>
1985     *     <li>{@link #columnSpec}<code>.column</code> = {@link #UNDEFINED} </li>
1986     *     <li>{@link #columnSpec}<code>.columnSpan</code> = 1 </li>
1987     *     <li>{@link #columnSpec}<code>.alignment</code> = {@link #START} </li>
1988     *     <li>{@link #columnSpec}<code>.weight</code> = 0 </li>
1989     * </ul>
1990     *
1991     * See {@link GridLayout} for a more complete description of the conventions
1992     * used by GridLayout in the interpretation of the properties of this class.
1993     *
1994     * @attr ref android.R.styleable#GridLayout_Layout_layout_row
1995     * @attr ref android.R.styleable#GridLayout_Layout_layout_rowSpan
1996     * @attr ref android.R.styleable#GridLayout_Layout_layout_rowWeight
1997     * @attr ref android.R.styleable#GridLayout_Layout_layout_column
1998     * @attr ref android.R.styleable#GridLayout_Layout_layout_columnSpan
1999     * @attr ref android.R.styleable#GridLayout_Layout_layout_columnWeight
2000     * @attr ref android.R.styleable#GridLayout_Layout_layout_gravity
2001     */
2002    public static class LayoutParams extends MarginLayoutParams {
2003
2004        // Default values
2005
2006        private static final int DEFAULT_WIDTH = WRAP_CONTENT;
2007        private static final int DEFAULT_HEIGHT = WRAP_CONTENT;
2008        private static final int DEFAULT_MARGIN = UNDEFINED;
2009        private static final int DEFAULT_ROW = UNDEFINED;
2010        private static final int DEFAULT_COLUMN = UNDEFINED;
2011        private static final Interval DEFAULT_SPAN = new Interval(UNDEFINED, UNDEFINED + 1);
2012        private static final int DEFAULT_SPAN_SIZE = DEFAULT_SPAN.size();
2013
2014        // TypedArray indices
2015
2016        private static final int MARGIN = R.styleable.ViewGroup_MarginLayout_layout_margin;
2017        private static final int LEFT_MARGIN = R.styleable.ViewGroup_MarginLayout_layout_marginLeft;
2018        private static final int TOP_MARGIN = R.styleable.ViewGroup_MarginLayout_layout_marginTop;
2019        private static final int RIGHT_MARGIN =
2020                R.styleable.ViewGroup_MarginLayout_layout_marginRight;
2021        private static final int BOTTOM_MARGIN =
2022                R.styleable.ViewGroup_MarginLayout_layout_marginBottom;
2023
2024        private static final int COLUMN = R.styleable.GridLayout_Layout_layout_column;
2025        private static final int COLUMN_SPAN = R.styleable.GridLayout_Layout_layout_columnSpan;
2026        private static final int COLUMN_WEIGHT = R.styleable.GridLayout_Layout_layout_columnWeight;
2027
2028        private static final int ROW = R.styleable.GridLayout_Layout_layout_row;
2029        private static final int ROW_SPAN = R.styleable.GridLayout_Layout_layout_rowSpan;
2030        private static final int ROW_WEIGHT = R.styleable.GridLayout_Layout_layout_rowWeight;
2031
2032        private static final int GRAVITY = R.styleable.GridLayout_Layout_layout_gravity;
2033
2034        // Instance variables
2035
2036        /**
2037         * The spec that defines the vertical characteristics of the cell group
2038         * described by these layout parameters.
2039         * If an assignment is made to this field after a measurement or layout operation
2040         * has already taken place, a call to
2041         * {@link ViewGroup#setLayoutParams(ViewGroup.LayoutParams)}
2042         * must be made to notify GridLayout of the change. GridLayout is normally able
2043         * to detect when code fails to observe this rule, issue a warning and take steps to
2044         * compensate for the omission. This facility is implemented on a best effort basis
2045         * and should not be relied upon in production code - so it is best to include the above
2046         * calls to remove the warnings as soon as it is practical.
2047         */
2048        public Spec rowSpec = Spec.UNDEFINED;
2049
2050        /**
2051         * The spec that defines the horizontal characteristics of the cell group
2052         * described by these layout parameters.
2053         * If an assignment is made to this field after a measurement or layout operation
2054         * has already taken place, a call to
2055         * {@link ViewGroup#setLayoutParams(ViewGroup.LayoutParams)}
2056         * must be made to notify GridLayout of the change. GridLayout is normally able
2057         * to detect when code fails to observe this rule, issue a warning and take steps to
2058         * compensate for the omission. This facility is implemented on a best effort basis
2059         * and should not be relied upon in production code - so it is best to include the above
2060         * calls to remove the warnings as soon as it is practical.
2061         */
2062        public Spec columnSpec = Spec.UNDEFINED;
2063
2064        // Constructors
2065
2066        private LayoutParams(
2067                int width, int height,
2068                int left, int top, int right, int bottom,
2069                Spec rowSpec, Spec columnSpec) {
2070            super(width, height);
2071            setMargins(left, top, right, bottom);
2072            this.rowSpec = rowSpec;
2073            this.columnSpec = columnSpec;
2074        }
2075
2076        /**
2077         * Constructs a new LayoutParams instance for this <code>rowSpec</code>
2078         * and <code>columnSpec</code>. All other fields are initialized with
2079         * default values as defined in {@link LayoutParams}.
2080         *
2081         * @param rowSpec    the rowSpec
2082         * @param columnSpec the columnSpec
2083         */
2084        public LayoutParams(Spec rowSpec, Spec columnSpec) {
2085            this(DEFAULT_WIDTH, DEFAULT_HEIGHT,
2086                    DEFAULT_MARGIN, DEFAULT_MARGIN, DEFAULT_MARGIN, DEFAULT_MARGIN,
2087                    rowSpec, columnSpec);
2088        }
2089
2090        /**
2091         * Constructs a new LayoutParams with default values as defined in {@link LayoutParams}.
2092         */
2093        public LayoutParams() {
2094            this(Spec.UNDEFINED, Spec.UNDEFINED);
2095        }
2096
2097        // Copying constructors
2098
2099        /**
2100         * {@inheritDoc}
2101         */
2102        public LayoutParams(ViewGroup.LayoutParams params) {
2103            super(params);
2104        }
2105
2106        /**
2107         * {@inheritDoc}
2108         */
2109        public LayoutParams(MarginLayoutParams params) {
2110            super(params);
2111        }
2112
2113        /**
2114         * Copy constructor. Clones the width, height, margin values, row spec,
2115         * and column spec of the source.
2116         *
2117         * @param source The layout params to copy from.
2118         */
2119        public LayoutParams(LayoutParams source) {
2120            super(source);
2121
2122            this.rowSpec = source.rowSpec;
2123            this.columnSpec = source.columnSpec;
2124        }
2125
2126        // AttributeSet constructors
2127
2128        /**
2129         * {@inheritDoc}
2130         *
2131         * Values not defined in the attribute set take the default values
2132         * defined in {@link LayoutParams}.
2133         */
2134        public LayoutParams(Context context, AttributeSet attrs) {
2135            super(context, attrs);
2136            reInitSuper(context, attrs);
2137            init(context, attrs);
2138        }
2139
2140        // Implementation
2141
2142        // Reinitialise the margins using a different default policy than MarginLayoutParams.
2143        // Here we use the value UNDEFINED (as distinct from zero) to represent the undefined state
2144        // so that a layout manager default can be accessed post set up. We need this as, at the
2145        // point of installation, we do not know how many rows/cols there are and therefore
2146        // which elements are positioned next to the container's trailing edges. We need to
2147        // know this as margins around the container's boundary should have different
2148        // defaults to those between peers.
2149
2150        // This method could be parametrized and moved into MarginLayout.
2151        private void reInitSuper(Context context, AttributeSet attrs) {
2152            TypedArray a =
2153                    context.obtainStyledAttributes(attrs, R.styleable.ViewGroup_MarginLayout);
2154            try {
2155                int margin = a.getDimensionPixelSize(MARGIN, DEFAULT_MARGIN);
2156
2157                this.leftMargin = a.getDimensionPixelSize(LEFT_MARGIN, margin);
2158                this.topMargin = a.getDimensionPixelSize(TOP_MARGIN, margin);
2159                this.rightMargin = a.getDimensionPixelSize(RIGHT_MARGIN, margin);
2160                this.bottomMargin = a.getDimensionPixelSize(BOTTOM_MARGIN, margin);
2161            } finally {
2162                a.recycle();
2163            }
2164        }
2165
2166        private void init(Context context, AttributeSet attrs) {
2167            TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.GridLayout_Layout);
2168            try {
2169                int gravity = a.getInt(GRAVITY, Gravity.NO_GRAVITY);
2170
2171                int column = a.getInt(COLUMN, DEFAULT_COLUMN);
2172                int colSpan = a.getInt(COLUMN_SPAN, DEFAULT_SPAN_SIZE);
2173                float colWeight = a.getFloat(COLUMN_WEIGHT, Spec.DEFAULT_WEIGHT);
2174                this.columnSpec = spec(column, colSpan, getAlignment(gravity, true), colWeight);
2175
2176                int row = a.getInt(ROW, DEFAULT_ROW);
2177                int rowSpan = a.getInt(ROW_SPAN, DEFAULT_SPAN_SIZE);
2178                float rowWeight = a.getFloat(ROW_WEIGHT, Spec.DEFAULT_WEIGHT);
2179                this.rowSpec = spec(row, rowSpan, getAlignment(gravity, false), rowWeight);
2180            } finally {
2181                a.recycle();
2182            }
2183        }
2184
2185        /**
2186         * Describes how the child views are positioned. Default is {@code LEFT | BASELINE}.
2187         * See {@link Gravity}.
2188         *
2189         * @param gravity the new gravity value
2190         *
2191         * @attr ref android.R.styleable#GridLayout_Layout_layout_gravity
2192         */
2193        public void setGravity(int gravity) {
2194            rowSpec = rowSpec.copyWriteAlignment(getAlignment(gravity, false));
2195            columnSpec = columnSpec.copyWriteAlignment(getAlignment(gravity, true));
2196        }
2197
2198        @Override
2199        protected void setBaseAttributes(TypedArray attributes, int widthAttr, int heightAttr) {
2200            this.width = attributes.getLayoutDimension(widthAttr, DEFAULT_WIDTH);
2201            this.height = attributes.getLayoutDimension(heightAttr, DEFAULT_HEIGHT);
2202        }
2203
2204        final void setRowSpecSpan(Interval span) {
2205            rowSpec = rowSpec.copyWriteSpan(span);
2206        }
2207
2208        final void setColumnSpecSpan(Interval span) {
2209            columnSpec = columnSpec.copyWriteSpan(span);
2210        }
2211
2212        @Override
2213        public boolean equals(Object o) {
2214            if (this == o) return true;
2215            if (o == null || getClass() != o.getClass()) return false;
2216
2217            LayoutParams that = (LayoutParams) o;
2218
2219            if (!columnSpec.equals(that.columnSpec)) return false;
2220            if (!rowSpec.equals(that.rowSpec)) return false;
2221
2222            return true;
2223        }
2224
2225        @Override
2226        public int hashCode() {
2227            int result = rowSpec.hashCode();
2228            result = 31 * result + columnSpec.hashCode();
2229            return result;
2230        }
2231    }
2232
2233    /*
2234    In place of a HashMap from span to Int, use an array of key/value pairs - stored in Arcs.
2235    Add the mutables completesCycle flag to avoid creating another hash table for detecting cycles.
2236     */
2237    final static class Arc {
2238        public final Interval span;
2239        public final MutableInt value;
2240        public boolean valid = true;
2241
2242        public Arc(Interval span, MutableInt value) {
2243            this.span = span;
2244            this.value = value;
2245        }
2246
2247        @Override
2248        public String toString() {
2249            return span + " " + (!valid ? "+>" : "->") + " " + value;
2250        }
2251    }
2252
2253    // A mutable Integer - used to avoid heap allocation during the layout operation
2254
2255    final static class MutableInt {
2256        public int value;
2257
2258        public MutableInt() {
2259            reset();
2260        }
2261
2262        public MutableInt(int value) {
2263            this.value = value;
2264        }
2265
2266        public void reset() {
2267            value = Integer.MIN_VALUE;
2268        }
2269
2270        @Override
2271        public String toString() {
2272            return Integer.toString(value);
2273        }
2274    }
2275
2276    final static class Assoc<K, V> extends ArrayList<Pair<K, V>> {
2277        private final Class<K> keyType;
2278        private final Class<V> valueType;
2279
2280        private Assoc(Class<K> keyType, Class<V> valueType) {
2281            this.keyType = keyType;
2282            this.valueType = valueType;
2283        }
2284
2285        public static <K, V> Assoc<K, V> of(Class<K> keyType, Class<V> valueType) {
2286            return new Assoc<K, V>(keyType, valueType);
2287        }
2288
2289        public void put(K key, V value) {
2290            add(Pair.create(key, value));
2291        }
2292
2293        @SuppressWarnings(value = "unchecked")
2294        public PackedMap<K, V> pack() {
2295            int N = size();
2296            K[] keys = (K[]) Array.newInstance(keyType, N);
2297            V[] values = (V[]) Array.newInstance(valueType, N);
2298            for (int i = 0; i < N; i++) {
2299                keys[i] = get(i).first;
2300                values[i] = get(i).second;
2301            }
2302            return new PackedMap<K, V>(keys, values);
2303        }
2304    }
2305
2306    /*
2307    This data structure is used in place of a Map where we have an index that refers to the order
2308    in which each key/value pairs were added to the map. In this case we store keys and values
2309    in arrays of a length that is equal to the number of unique keys. We also maintain an
2310    array of indexes from insertion order to the compacted arrays of keys and values.
2311
2312    Note that behavior differs from that of a LinkedHashMap in that repeated entries
2313    *do* get added multiples times. So the length of index is equals to the number of
2314    items added.
2315
2316    This is useful in the GridLayout class where we can rely on the order of children not
2317    changing during layout - to use integer-based lookup for our internal structures
2318    rather than using (and storing) an implementation of Map<Key, ?>.
2319     */
2320    @SuppressWarnings(value = "unchecked")
2321    final static class PackedMap<K, V> {
2322        public final int[] index;
2323        public final K[] keys;
2324        public final V[] values;
2325
2326        private PackedMap(K[] keys, V[] values) {
2327            this.index = createIndex(keys);
2328
2329            this.keys = compact(keys, index);
2330            this.values = compact(values, index);
2331        }
2332
2333        public V getValue(int i) {
2334            return values[index[i]];
2335        }
2336
2337        private static <K> int[] createIndex(K[] keys) {
2338            int size = keys.length;
2339            int[] result = new int[size];
2340
2341            Map<K, Integer> keyToIndex = new HashMap<K, Integer>();
2342            for (int i = 0; i < size; i++) {
2343                K key = keys[i];
2344                Integer index = keyToIndex.get(key);
2345                if (index == null) {
2346                    index = keyToIndex.size();
2347                    keyToIndex.put(key, index);
2348                }
2349                result[i] = index;
2350            }
2351            return result;
2352        }
2353
2354        /*
2355        Create a compact array of keys or values using the supplied index.
2356         */
2357        private static <K> K[] compact(K[] a, int[] index) {
2358            int size = a.length;
2359            Class<?> componentType = a.getClass().getComponentType();
2360            K[] result = (K[]) Array.newInstance(componentType, max2(index, -1) + 1);
2361
2362            // this overwrite duplicates, retaining the last equivalent entry
2363            for (int i = 0; i < size; i++) {
2364                result[index[i]] = a[i];
2365            }
2366            return result;
2367        }
2368    }
2369
2370    /*
2371    For each group (with a given alignment) we need to store the amount of space required
2372    before the alignment point and the amount of space required after it. One side of this
2373    calculation is always 0 for START and END alignments but we don't make use of this.
2374    For CENTER and BASELINE alignments both sides are needed and in the BASELINE case no
2375    simple optimisations are possible.
2376
2377    The general algorithm therefore is to create a Map (actually a PackedMap) from
2378    group to Bounds and to loop through all Views in the group taking the maximum
2379    of the values for each View.
2380    */
2381    static class Bounds {
2382        public int before;
2383        public int after;
2384        public int flexibility; // we're flexible iff all included specs are flexible
2385
2386        private Bounds() {
2387            reset();
2388        }
2389
2390        protected void reset() {
2391            before = Integer.MIN_VALUE;
2392            after = Integer.MIN_VALUE;
2393            flexibility = CAN_STRETCH; // from the above, we're flexible when empty
2394        }
2395
2396        protected void include(int before, int after) {
2397            this.before = max(this.before, before);
2398            this.after = max(this.after, after);
2399        }
2400
2401        protected int size(boolean min) {
2402            if (!min) {
2403                if (canStretch(flexibility)) {
2404                    return MAX_SIZE;
2405                }
2406            }
2407            return before + after;
2408        }
2409
2410        protected int getOffset(GridLayout gl, View c, Alignment a, int size, boolean horizontal) {
2411            return before - a.getAlignmentValue(c, size, gl.getLayoutMode());
2412        }
2413
2414        protected final void include(GridLayout gl, View c, Spec spec, Axis axis, int size) {
2415            this.flexibility &= spec.getFlexibility();
2416            boolean horizontal = axis.horizontal;
2417            Alignment alignment = gl.getAlignment(spec.alignment, horizontal);
2418            // todo test this works correctly when the returned value is UNDEFINED
2419            int before = alignment.getAlignmentValue(c, size, gl.getLayoutMode());
2420            include(before, size - before);
2421        }
2422
2423        @Override
2424        public String toString() {
2425            return "Bounds{" +
2426                    "before=" + before +
2427                    ", after=" + after +
2428                    '}';
2429        }
2430    }
2431
2432    /**
2433     * An Interval represents a contiguous range of values that lie between
2434     * the interval's {@link #min} and {@link #max} values.
2435     * <p>
2436     * Intervals are immutable so may be passed as values and used as keys in hash tables.
2437     * It is not necessary to have multiple instances of Intervals which have the same
2438     * {@link #min} and {@link #max} values.
2439     * <p>
2440     * Intervals are often written as {@code [min, max]} and represent the set of values
2441     * {@code x} such that {@code min <= x < max}.
2442     */
2443    final static class Interval {
2444        /**
2445         * The minimum value.
2446         */
2447        public final int min;
2448
2449        /**
2450         * The maximum value.
2451         */
2452        public final int max;
2453
2454        /**
2455         * Construct a new Interval, {@code interval}, where:
2456         * <ul>
2457         *     <li> {@code interval.min = min} </li>
2458         *     <li> {@code interval.max = max} </li>
2459         * </ul>
2460         *
2461         * @param min the minimum value.
2462         * @param max the maximum value.
2463         */
2464        public Interval(int min, int max) {
2465            this.min = min;
2466            this.max = max;
2467        }
2468
2469        int size() {
2470            return max - min;
2471        }
2472
2473        Interval inverse() {
2474            return new Interval(max, min);
2475        }
2476
2477        /**
2478         * Returns {@code true} if the {@link #getClass class},
2479         * {@link #min} and {@link #max} properties of this Interval and the
2480         * supplied parameter are pairwise equal; {@code false} otherwise.
2481         *
2482         * @param that the object to compare this interval with
2483         *
2484         * @return {@code true} if the specified object is equal to this
2485         *         {@code Interval}, {@code false} otherwise.
2486         */
2487        @Override
2488        public boolean equals(Object that) {
2489            if (this == that) {
2490                return true;
2491            }
2492            if (that == null || getClass() != that.getClass()) {
2493                return false;
2494            }
2495
2496            Interval interval = (Interval) that;
2497
2498            if (max != interval.max) {
2499                return false;
2500            }
2501            //noinspection RedundantIfStatement
2502            if (min != interval.min) {
2503                return false;
2504            }
2505
2506            return true;
2507        }
2508
2509        @Override
2510        public int hashCode() {
2511            int result = min;
2512            result = 31 * result + max;
2513            return result;
2514        }
2515
2516        @Override
2517        public String toString() {
2518            return "[" + min + ", " + max + "]";
2519        }
2520    }
2521
2522    /**
2523     * A Spec defines the horizontal or vertical characteristics of a group of
2524     * cells. Each spec. defines the <em>grid indices</em> and <em>alignment</em>
2525     * along the appropriate axis.
2526     * <p>
2527     * The <em>grid indices</em> are the leading and trailing edges of this cell group.
2528     * See {@link GridLayout} for a description of the conventions used by GridLayout
2529     * for grid indices.
2530     * <p>
2531     * The <em>alignment</em> property specifies how cells should be aligned in this group.
2532     * For row groups, this specifies the vertical alignment.
2533     * For column groups, this specifies the horizontal alignment.
2534     * <p>
2535     * Use the following static methods to create specs:
2536     * <ul>
2537     *   <li>{@link #spec(int)}</li>
2538     *   <li>{@link #spec(int, int)}</li>
2539     *   <li>{@link #spec(int, Alignment)}</li>
2540     *   <li>{@link #spec(int, int, Alignment)}</li>
2541     *   <li>{@link #spec(int, float)}</li>
2542     *   <li>{@link #spec(int, int, float)}</li>
2543     *   <li>{@link #spec(int, Alignment, float)}</li>
2544     *   <li>{@link #spec(int, int, Alignment, float)}</li>
2545     * </ul>
2546     *
2547     */
2548    public static class Spec {
2549        static final Spec UNDEFINED = spec(GridLayout.UNDEFINED);
2550        static final float DEFAULT_WEIGHT = 0;
2551
2552        final boolean startDefined;
2553        final Interval span;
2554        final Alignment alignment;
2555        final float weight;
2556
2557        private Spec(boolean startDefined, Interval span, Alignment alignment, float weight) {
2558            this.startDefined = startDefined;
2559            this.span = span;
2560            this.alignment = alignment;
2561            this.weight = weight;
2562        }
2563
2564        private Spec(boolean startDefined, int start, int size, Alignment alignment, float weight) {
2565            this(startDefined, new Interval(start, start + size), alignment, weight);
2566        }
2567
2568        final Spec copyWriteSpan(Interval span) {
2569            return new Spec(startDefined, span, alignment, weight);
2570        }
2571
2572        final Spec copyWriteAlignment(Alignment alignment) {
2573            return new Spec(startDefined, span, alignment, weight);
2574        }
2575
2576        final int getFlexibility() {
2577            return (alignment == UNDEFINED_ALIGNMENT && weight == 0) ? INFLEXIBLE : CAN_STRETCH;
2578        }
2579
2580        /**
2581         * Returns {@code true} if the {@code class}, {@code alignment} and {@code span}
2582         * properties of this Spec and the supplied parameter are pairwise equal,
2583         * {@code false} otherwise.
2584         *
2585         * @param that the object to compare this spec with
2586         *
2587         * @return {@code true} if the specified object is equal to this
2588         *         {@code Spec}; {@code false} otherwise
2589         */
2590        @Override
2591        public boolean equals(Object that) {
2592            if (this == that) {
2593                return true;
2594            }
2595            if (that == null || getClass() != that.getClass()) {
2596                return false;
2597            }
2598
2599            Spec spec = (Spec) that;
2600
2601            if (!alignment.equals(spec.alignment)) {
2602                return false;
2603            }
2604            //noinspection RedundantIfStatement
2605            if (!span.equals(spec.span)) {
2606                return false;
2607            }
2608
2609            return true;
2610        }
2611
2612        @Override
2613        public int hashCode() {
2614            int result = span.hashCode();
2615            result = 31 * result + alignment.hashCode();
2616            return result;
2617        }
2618    }
2619
2620    /**
2621     * Return a Spec, {@code spec}, where:
2622     * <ul>
2623     *     <li> {@code spec.span = [start, start + size]} </li>
2624     *     <li> {@code spec.alignment = alignment} </li>
2625     *     <li> {@code spec.weight = weight} </li>
2626     * </ul>
2627     * <p>
2628     * To leave the start index undefined, use the value {@link #UNDEFINED}.
2629     *
2630     * @param start     the start
2631     * @param size      the size
2632     * @param alignment the alignment
2633     * @param weight    the weight
2634     */
2635    public static Spec spec(int start, int size, Alignment alignment, float weight) {
2636        return new Spec(start != UNDEFINED, start, size, alignment, weight);
2637    }
2638
2639    /**
2640     * Equivalent to: {@code spec(start, 1, alignment, weight)}.
2641     *
2642     * @param start     the start
2643     * @param alignment the alignment
2644     * @param weight    the weight
2645     */
2646    public static Spec spec(int start, Alignment alignment, float weight) {
2647        return spec(start, 1, alignment, weight);
2648    }
2649
2650    /**
2651     * Equivalent to: {@code spec(start, 1, default_alignment, weight)} -
2652     * where {@code default_alignment} is specified in
2653     * {@link android.widget.GridLayout.LayoutParams}.
2654     *
2655     * @param start  the start
2656     * @param size   the size
2657     * @param weight the weight
2658     */
2659    public static Spec spec(int start, int size, float weight) {
2660        return spec(start, size, UNDEFINED_ALIGNMENT, weight);
2661    }
2662
2663    /**
2664     * Equivalent to: {@code spec(start, 1, weight)}.
2665     *
2666     * @param start  the start
2667     * @param weight the weight
2668     */
2669    public static Spec spec(int start, float weight) {
2670        return spec(start, 1, weight);
2671    }
2672
2673    /**
2674     * Equivalent to: {@code spec(start, size, alignment, 0f)}.
2675     *
2676     * @param start     the start
2677     * @param size      the size
2678     * @param alignment the alignment
2679     */
2680    public static Spec spec(int start, int size, Alignment alignment) {
2681        return spec(start, size, alignment, Spec.DEFAULT_WEIGHT);
2682    }
2683
2684    /**
2685     * Return a Spec, {@code spec}, where:
2686     * <ul>
2687     *     <li> {@code spec.span = [start, start + 1]} </li>
2688     *     <li> {@code spec.alignment = alignment} </li>
2689     * </ul>
2690     * <p>
2691     * To leave the start index undefined, use the value {@link #UNDEFINED}.
2692     *
2693     * @param start     the start index
2694     * @param alignment the alignment
2695     *
2696     * @see #spec(int, int, Alignment)
2697     */
2698    public static Spec spec(int start, Alignment alignment) {
2699        return spec(start, 1, alignment);
2700    }
2701
2702    /**
2703     * Return a Spec, {@code spec}, where:
2704     * <ul>
2705     *     <li> {@code spec.span = [start, start + size]} </li>
2706     * </ul>
2707     * <p>
2708     * To leave the start index undefined, use the value {@link #UNDEFINED}.
2709     *
2710     * @param start     the start
2711     * @param size      the size
2712     *
2713     * @see #spec(int, Alignment)
2714     */
2715    public static Spec spec(int start, int size) {
2716        return spec(start, size, UNDEFINED_ALIGNMENT);
2717    }
2718
2719    /**
2720     * Return a Spec, {@code spec}, where:
2721     * <ul>
2722     *     <li> {@code spec.span = [start, start + 1]} </li>
2723     * </ul>
2724     * <p>
2725     * To leave the start index undefined, use the value {@link #UNDEFINED}.
2726     *
2727     * @param start     the start index
2728     *
2729     * @see #spec(int, int)
2730     */
2731    public static Spec spec(int start) {
2732        return spec(start, 1);
2733    }
2734
2735    /**
2736     * Alignments specify where a view should be placed within a cell group and
2737     * what size it should be.
2738     * <p>
2739     * The {@link LayoutParams} class contains a {@link LayoutParams#rowSpec rowSpec}
2740     * and a {@link LayoutParams#columnSpec columnSpec} each of which contains an
2741     * {@code alignment}. Overall placement of the view in the cell
2742     * group is specified by the two alignments which act along each axis independently.
2743     * <p>
2744     *  The GridLayout class defines the most common alignments used in general layout:
2745     * {@link #TOP}, {@link #LEFT}, {@link #BOTTOM}, {@link #RIGHT}, {@link #START},
2746     * {@link #END}, {@link #CENTER}, {@link #BASELINE} and {@link #FILL}.
2747     */
2748    /*
2749     * An Alignment implementation must define {@link #getAlignmentValue(View, int, int)},
2750     * to return the appropriate value for the type of alignment being defined.
2751     * The enclosing algorithms position the children
2752     * so that the locations defined by the alignment values
2753     * are the same for all of the views in a group.
2754     * <p>
2755     */
2756    public static abstract class Alignment {
2757        Alignment() {
2758        }
2759
2760        abstract int getGravityOffset(View view, int cellDelta);
2761
2762        /**
2763         * Returns an alignment value. In the case of vertical alignments the value
2764         * returned should indicate the distance from the top of the view to the
2765         * alignment location.
2766         * For horizontal alignments measurement is made from the left edge of the component.
2767         *
2768         * @param view              the view to which this alignment should be applied
2769         * @param viewSize          the measured size of the view
2770         * @param mode              the basis of alignment: CLIP or OPTICAL
2771         * @return the alignment value
2772         */
2773        abstract int getAlignmentValue(View view, int viewSize, int mode);
2774
2775        /**
2776         * Returns the size of the view specified by this alignment.
2777         * In the case of vertical alignments this method should return a height; for
2778         * horizontal alignments this method should return the width.
2779         * <p>
2780         * The default implementation returns {@code viewSize}.
2781         *
2782         * @param view              the view to which this alignment should be applied
2783         * @param viewSize          the measured size of the view
2784         * @param cellSize          the size of the cell into which this view will be placed
2785         * @return the aligned size
2786         */
2787        int getSizeInCell(View view, int viewSize, int cellSize) {
2788            return viewSize;
2789        }
2790
2791        Bounds getBounds() {
2792            return new Bounds();
2793        }
2794    }
2795
2796    static final Alignment UNDEFINED_ALIGNMENT = new Alignment() {
2797        @Override
2798        int getGravityOffset(View view, int cellDelta) {
2799            return UNDEFINED;
2800        }
2801
2802        @Override
2803        public int getAlignmentValue(View view, int viewSize, int mode) {
2804            return UNDEFINED;
2805        }
2806    };
2807
2808    /**
2809     * Indicates that a view should be aligned with the <em>start</em>
2810     * edges of the other views in its cell group.
2811     */
2812    private static final Alignment LEADING = new Alignment() {
2813        @Override
2814        int getGravityOffset(View view, int cellDelta) {
2815            return 0;
2816        }
2817
2818        @Override
2819        public int getAlignmentValue(View view, int viewSize, int mode) {
2820            return 0;
2821        }
2822    };
2823
2824    /**
2825     * Indicates that a view should be aligned with the <em>end</em>
2826     * edges of the other views in its cell group.
2827     */
2828    private static final Alignment TRAILING = new Alignment() {
2829        @Override
2830        int getGravityOffset(View view, int cellDelta) {
2831            return cellDelta;
2832        }
2833
2834        @Override
2835        public int getAlignmentValue(View view, int viewSize, int mode) {
2836            return viewSize;
2837        }
2838    };
2839
2840    /**
2841     * Indicates that a view should be aligned with the <em>top</em>
2842     * edges of the other views in its cell group.
2843     */
2844    public static final Alignment TOP = LEADING;
2845
2846    /**
2847     * Indicates that a view should be aligned with the <em>bottom</em>
2848     * edges of the other views in its cell group.
2849     */
2850    public static final Alignment BOTTOM = TRAILING;
2851
2852    /**
2853     * Indicates that a view should be aligned with the <em>start</em>
2854     * edges of the other views in its cell group.
2855     */
2856    public static final Alignment START = LEADING;
2857
2858    /**
2859     * Indicates that a view should be aligned with the <em>end</em>
2860     * edges of the other views in its cell group.
2861     */
2862    public static final Alignment END = TRAILING;
2863
2864    private static Alignment createSwitchingAlignment(final Alignment ltr, final Alignment rtl) {
2865        return new Alignment() {
2866            @Override
2867            int getGravityOffset(View view, int cellDelta) {
2868                return (!view.isLayoutRtl() ? ltr : rtl).getGravityOffset(view, cellDelta);
2869            }
2870
2871            @Override
2872            public int getAlignmentValue(View view, int viewSize, int mode) {
2873                return (!view.isLayoutRtl() ? ltr : rtl).getAlignmentValue(view, viewSize, mode);
2874            }
2875        };
2876    }
2877
2878    /**
2879     * Indicates that a view should be aligned with the <em>left</em>
2880     * edges of the other views in its cell group.
2881     */
2882    public static final Alignment LEFT = createSwitchingAlignment(START, END);
2883
2884    /**
2885     * Indicates that a view should be aligned with the <em>right</em>
2886     * edges of the other views in its cell group.
2887     */
2888    public static final Alignment RIGHT = createSwitchingAlignment(END, START);
2889
2890    /**
2891     * Indicates that a view should be <em>centered</em> with the other views in its cell group.
2892     * This constant may be used in both {@link LayoutParams#rowSpec rowSpecs} and {@link
2893     * LayoutParams#columnSpec columnSpecs}.
2894     */
2895    public static final Alignment CENTER = new Alignment() {
2896        @Override
2897        int getGravityOffset(View view, int cellDelta) {
2898            return cellDelta >> 1;
2899        }
2900
2901        @Override
2902        public int getAlignmentValue(View view, int viewSize, int mode) {
2903            return viewSize >> 1;
2904        }
2905    };
2906
2907    /**
2908     * Indicates that a view should be aligned with the <em>baselines</em>
2909     * of the other views in its cell group.
2910     * This constant may only be used as an alignment in {@link LayoutParams#rowSpec rowSpecs}.
2911     *
2912     * @see View#getBaseline()
2913     */
2914    public static final Alignment BASELINE = new Alignment() {
2915        @Override
2916        int getGravityOffset(View view, int cellDelta) {
2917            return 0; // baseline gravity is top
2918        }
2919
2920        @Override
2921        public int getAlignmentValue(View view, int viewSize, int mode) {
2922            if (view.getVisibility() == GONE) {
2923                return 0;
2924            }
2925            int baseline = view.getBaseline();
2926            return baseline == -1 ? UNDEFINED : baseline;
2927        }
2928
2929        @Override
2930        public Bounds getBounds() {
2931            return new Bounds() {
2932                /*
2933                In a baseline aligned row in which some components define a baseline
2934                and some don't, we need a third variable to properly account for all
2935                the sizes. This tracks the maximum size of all the components -
2936                including those that don't define a baseline.
2937                */
2938                private int size;
2939
2940                @Override
2941                protected void reset() {
2942                    super.reset();
2943                    size = Integer.MIN_VALUE;
2944                }
2945
2946                @Override
2947                protected void include(int before, int after) {
2948                    super.include(before, after);
2949                    size = max(size, before + after);
2950                }
2951
2952                @Override
2953                protected int size(boolean min) {
2954                    return max(super.size(min), size);
2955                }
2956
2957                @Override
2958                protected int getOffset(GridLayout gl, View c, Alignment a, int size, boolean hrz) {
2959                    return max(0, super.getOffset(gl, c, a, size, hrz));
2960                }
2961            };
2962        }
2963    };
2964
2965    /**
2966     * Indicates that a view should expanded to fit the boundaries of its cell group.
2967     * This constant may be used in both {@link LayoutParams#rowSpec rowSpecs} and
2968     * {@link LayoutParams#columnSpec columnSpecs}.
2969     */
2970    public static final Alignment FILL = new Alignment() {
2971        @Override
2972        int getGravityOffset(View view, int cellDelta) {
2973            return 0;
2974        }
2975
2976        @Override
2977        public int getAlignmentValue(View view, int viewSize, int mode) {
2978            return UNDEFINED;
2979        }
2980
2981        @Override
2982        public int getSizeInCell(View view, int viewSize, int cellSize) {
2983            return cellSize;
2984        }
2985    };
2986
2987    static boolean canStretch(int flexibility) {
2988        return (flexibility & CAN_STRETCH) != 0;
2989    }
2990
2991    private static final int INFLEXIBLE = 0;
2992    private static final int CAN_STRETCH = 2;
2993}
2994