[go: nahoru, domu]

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