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