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