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