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