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