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