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