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