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