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