GridLayout.java revision 452eec33d667f9e705b57e60948b070536fbc1b4
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 hAlign = columnGroup.alignment;
847            Alignment vAlign = 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 type = PRF;
856            int c2ax = protect(hAlign.getAlignmentValue(null, cellWidth - colBounds.size(), type));
857            int c2ay = protect(vAlign.getAlignmentValue(null, cellHeight - rowBounds.size(), type));
858
859            if (mMarginsIncludedInAlignment) {
860                int leftMargin = getMargin(view, true, true);
861                int topMargin = getMargin(view, true, false);
862                int rightMargin = getMargin(view, false, true);
863                int bottomMargin = getMargin(view, false, false);
864
865                // Same calculation as getMeasurementIncludingMargin()
866                int mWidth = leftMargin + pWidth + rightMargin;
867                int mHeight = topMargin + pHeight + bottomMargin;
868
869                // Alignment offsets: the location of the view relative to its alignment group.
870                int a2vx = colBounds.before - hAlign.getAlignmentValue(view, mWidth, type);
871                int a2vy = rowBounds.before - vAlign.getAlignmentValue(view, mHeight, type);
872
873                dx = c2ax + a2vx + leftMargin;
874                dy = c2ay + a2vy + topMargin;
875
876                cellWidth -= leftMargin + rightMargin;
877                cellHeight -= topMargin + bottomMargin;
878            } else {
879                // Alignment offsets: the location of the view relative to its alignment group.
880                int a2vx = colBounds.before - hAlign.getAlignmentValue(view, pWidth, type);
881                int a2vy = rowBounds.before - vAlign.getAlignmentValue(view, pHeight, type);
882
883                dx = c2ax + a2vx;
884                dy = c2ay + a2vy;
885            }
886
887            int width = hAlign.getSizeInCell(view, pWidth, cellWidth, type);
888            int height = vAlign.getSizeInCell(view, pHeight, cellHeight, type);
889
890            int cx = paddingLeft + x1 + dx;
891            int cy = paddingTop + y1 + dy;
892            view.layout(cx, cy, cx + width, cy + height);
893        }
894    }
895
896    // Inner classes
897
898    /*
899     This internal class houses the algorithm for computing the locations of grid lines;
900     along either the horizontal or vertical axis. A GridLayout uses two instances of this class -
901     distinguished by the "horizontal" flag which is true for the horizontal axis and false
902     for the vertical one.
903     */
904    private class Axis {
905        private static final int MIN_VALUE = -1000000;
906
907        private static final int UNVISITED = 0;
908        private static final int PENDING = 1;
909        private static final int COMPLETE = 2;
910
911        public final boolean horizontal;
912
913        public int count = UNDEFINED;
914        public boolean countValid = false;
915        public boolean countWasExplicitySet = false;
916
917        PackedMap<Group, Bounds> groupBounds;
918        public boolean groupBoundsValid = false;
919
920        PackedMap<Interval, MutableInt> spanSizes;
921        public boolean spanSizesValid = false;
922
923        public int[] leadingMargins;
924        public boolean leadingMarginsValid = false;
925
926        public int[] trailingMargins;
927        public boolean trailingMarginsValid = false;
928
929        public Arc[] arcs;
930        public boolean arcsValid = false;
931
932        public int[] minima;
933        public boolean minimaValid = false;
934
935        public float[] weights;
936        public int[] locations;
937
938        private boolean mOrderPreserved = DEFAULT_ORDER_PRESERVED;
939
940        private Axis(boolean horizontal) {
941            this.horizontal = horizontal;
942        }
943
944        private int maxIndex() {
945            // note the number Integer.MIN_VALUE + 1 comes up in undefined cells
946            int count = -1;
947            for (int i = 0, size = getChildCount(); i < size; i++) {
948                LayoutParams params = getLayoutParams(getChildAt(i));
949                Group g = horizontal ? params.columnGroup : params.rowGroup;
950                count = max(count, g.span.min);
951                count = max(count, g.span.max);
952            }
953            return count == -1 ? UNDEFINED : count;
954        }
955
956        public int getCount() {
957            if (!countValid) {
958                count = max(0, maxIndex()); // if there are no cells, the count is zero
959                countValid = true;
960            }
961            return count;
962        }
963
964        public void setCount(int count) {
965            this.count = count;
966            this.countWasExplicitySet = count != UNDEFINED;
967        }
968
969        public boolean isOrderPreserved() {
970            return mOrderPreserved;
971        }
972
973        public void setOrderPreserved(boolean orderPreserved) {
974            mOrderPreserved = orderPreserved;
975            invalidateStructure();
976        }
977
978        private PackedMap<Group, Bounds> createGroupBounds() {
979            int N = getChildCount();
980            Group[] groups = new Group[N];
981            Bounds[] bounds = new Bounds[N];
982            for (int i = 0; i < N; i++) {
983                LayoutParams lp = getLayoutParams(getChildAt(i));
984                Group group = horizontal ? lp.columnGroup : lp.rowGroup;
985
986                groups[i] = group;
987                bounds[i] = new Bounds();
988            }
989
990            return new PackedMap<Group, Bounds>(groups, bounds);
991        }
992
993        private void computeGroupBounds() {
994            for (int i = 0; i < groupBounds.values.length; i++) {
995                groupBounds.values[i].reset();
996            }
997            for (int i = 0, N = getChildCount(); i < N; i++) {
998                View c = getChildAt(i);
999                LayoutParams lp = getLayoutParams(c);
1000                Group g = horizontal ? lp.columnGroup : lp.rowGroup;
1001
1002                Bounds bounds = groupBounds.getValue(i);
1003
1004                int size = getMeasurementIncludingMargin(c, horizontal, PRF);
1005                // todo test this works correctly when the returned value is UNDEFINED
1006                int before = g.alignment.getAlignmentValue(c, size, PRF);
1007                bounds.include(before, size - before);
1008            }
1009        }
1010
1011        private PackedMap<Group, Bounds> getGroupBounds() {
1012            if (groupBounds == null) {
1013                groupBounds = createGroupBounds();
1014            }
1015            if (!groupBoundsValid) {
1016                computeGroupBounds();
1017                groupBoundsValid = true;
1018            }
1019            return groupBounds;
1020        }
1021
1022        // Add values computed by alignment - taking the max of all alignments in each span
1023        private PackedMap<Interval, MutableInt> createSpanSizes() {
1024            PackedMap<Group, Bounds> groupBounds = getGroupBounds();
1025            int N = groupBounds.keys.length;
1026            Interval[] spans = new Interval[N];
1027            MutableInt[] values = new MutableInt[N];
1028            for (int i = 0; i < N; i++) {
1029                Interval key = groupBounds.keys[i].span;
1030
1031                spans[i] = key;
1032                values[i] = new MutableInt();
1033            }
1034            return new PackedMap<Interval, MutableInt>(spans, values);
1035        }
1036
1037        private void computeSpanSizes() {
1038            MutableInt[] spans = spanSizes.values;
1039            for (int i = 0; i < spans.length; i++) {
1040                spans[i].reset();
1041            }
1042
1043            Bounds[] bounds = getGroupBounds().values;  // use getter to trigger a re-evaluation
1044            for (int i = 0; i < bounds.length; i++) {
1045                int value = bounds[i].size();
1046
1047                MutableInt valueHolder = spanSizes.getValue(i);
1048                valueHolder.value = max(valueHolder.value, value);
1049            }
1050        }
1051
1052        private PackedMap<Interval, MutableInt> getSpanSizes() {
1053            if (spanSizes == null) {
1054                spanSizes = createSpanSizes();
1055            }
1056            if (!spanSizesValid) {
1057                computeSpanSizes();
1058                spanSizesValid = true;
1059            }
1060            return spanSizes;
1061        }
1062
1063        private void include(List<Arc> arcs, Interval key, MutableInt size) {
1064            // this bit below should really be computed outside here -
1065            // its just to stop default (col>0) constraints obliterating valid entries
1066            for (Arc arc : arcs) {
1067                Interval span = arc.span;
1068                if (span.equals(key)) {
1069                    return;
1070                }
1071            }
1072            arcs.add(new Arc(key, size));
1073        }
1074
1075        private void include2(List<Arc> arcs, Interval span, MutableInt min, MutableInt max,
1076                boolean both) {
1077            include(arcs, span, min);
1078            if (both) {
1079                // todo
1080//                include(arcs, span.inverse(), max.neg());
1081            }
1082        }
1083
1084        private void include2(List<Arc> arcs, Interval span, int min, int max, boolean both) {
1085            include2(arcs, span, new MutableInt(min), new MutableInt(max), both);
1086        }
1087
1088        // Group arcs by their first vertex, returning an array of arrays.
1089        // This is linear in the number of arcs.
1090        private Arc[][] groupArcsByFirstVertex(Arc[] arcs) {
1091            int N = getCount() + 1;// the number of vertices
1092            Arc[][] result = new Arc[N][];
1093            int[] sizes = new int[N];
1094            for (Arc arc : arcs) {
1095                sizes[arc.span.min]++;
1096            }
1097            for (int i = 0; i < sizes.length; i++) {
1098                result[i] = new Arc[sizes[i]];
1099            }
1100            // reuse the sizes array to hold the current last elements as we insert each arc
1101            Arrays.fill(sizes, 0);
1102            for (Arc arc : arcs) {
1103                int i = arc.span.min;
1104                result[i][sizes[i]++] = arc;
1105            }
1106
1107            return result;
1108        }
1109
1110        /*
1111        Topological sort.
1112         */
1113        private Arc[] topologicalSort(final Arc[] arcs, int start) {
1114        // todo ensure the <start> vertex is added in edge cases
1115            final List<Arc> result = new ArrayList<Arc>();
1116            new Object() {
1117                Arc[][] arcsByFirstVertex = groupArcsByFirstVertex(arcs);
1118                int[] visited = new int[getCount() + 1];
1119
1120                boolean completesCycle(int loc) {
1121                    int state = visited[loc];
1122                    if (state == UNVISITED) {
1123                        visited[loc] = PENDING;
1124                        for (Arc arc : arcsByFirstVertex[loc]) {
1125                            Interval span = arc.span;
1126                            // the recursive call
1127                            if (completesCycle(span.max)) {
1128                                // which arcs get set here is dependent on the order
1129                                // in which we explore nodes
1130                                arc.completesCycle = true;
1131                            }
1132                            result.add(arc);
1133                        }
1134                        visited[loc] = COMPLETE;
1135                    } else if (state == PENDING) {
1136                        return true;
1137                    } else if (state == COMPLETE) {
1138                    }
1139                    return false;
1140                }
1141            }.completesCycle(start);
1142            Collections.reverse(result);
1143            assert arcs.length == result.size();
1144            return result.toArray(new Arc[result.size()]);
1145        }
1146
1147        private boolean[] findUsed(Collection<Arc> arcs) {
1148            boolean[] result = new boolean[getCount()];
1149            for (Arc arc : arcs) {
1150                Interval span = arc.span;
1151                int min = min(span.min, span.max);
1152                int max = max(span.min, span.max);
1153                for (int i = min; i < max; i++) {
1154                    result[i] = true;
1155                }
1156            }
1157            return result;
1158        }
1159
1160        // todo unify with findUsed above. Both routines analyze which rows/columns are empty.
1161        private Collection<Interval> getSpacers() {
1162            List<Interval> result = new ArrayList<Interval>();
1163            int N = getCount() + 1;
1164            int[] leadingEdgeCount = new int[N];
1165            int[] trailingEdgeCount = new int[N];
1166            for (int i = 0, size = getChildCount(); i < size; i++) {
1167                LayoutParams lp = getLayoutParams(getChildAt(i));
1168                Group g = horizontal ? lp.columnGroup : lp.rowGroup;
1169                Interval span = g.span;
1170                leadingEdgeCount[span.min]++;
1171                trailingEdgeCount[span.max]++;
1172            }
1173
1174            int lastTrailingEdge = 0;
1175
1176            // treat the parent's edges like peer edges of the opposite type
1177            trailingEdgeCount[0] = 1;
1178            leadingEdgeCount[N - 1] = 1;
1179
1180            for (int i = 0; i < N; i++) {
1181                if (trailingEdgeCount[i] > 0) {
1182                    lastTrailingEdge = i;
1183                    continue; // if this is also a leading edge, don't add a space of length zero
1184                }
1185                if (leadingEdgeCount[i] > 0) {
1186                    result.add(new Interval(lastTrailingEdge, i));
1187                }
1188            }
1189            return result;
1190        }
1191
1192        private Arc[] createArcs() {
1193            List<Arc> spanToSize = new ArrayList<Arc>();
1194
1195            // Add all the preferred elements that were not defined by the user.
1196            PackedMap<Interval, MutableInt> spanSizes = getSpanSizes();
1197            for (int i = 0; i < spanSizes.keys.length; i++) {
1198                Interval key = spanSizes.keys[i];
1199                MutableInt value = spanSizes.values[i];
1200                // todo remove value duplicate
1201                include2(spanToSize, key, value, value, accommodateBothMinAndMax);
1202            }
1203
1204            // Find redundant rows/cols and glue them together with 0-length arcs to link the tree
1205            boolean[] used = findUsed(spanToSize);
1206            for (int i = 0; i < getCount(); i++) {
1207                if (!used[i]) {
1208                    Interval span = new Interval(i, i + 1);
1209                    include(spanToSize, span, new MutableInt(0));
1210                    include(spanToSize, span.inverse(), new MutableInt(0));
1211                }
1212            }
1213
1214            if (mOrderPreserved) {
1215                // Add preferred gaps
1216                for (int i = 0; i < getCount(); i++) {
1217                    if (used[i]) {
1218                        include2(spanToSize, new Interval(i, i + 1), 0, 0, false);
1219                    }
1220                }
1221            } else {
1222                for (Interval gap : getSpacers()) {
1223                    include2(spanToSize, gap, 0, 0, false);
1224                }
1225            }
1226            Arc[] arcs = spanToSize.toArray(new Arc[spanToSize.size()]);
1227            return topologicalSort(arcs, 0);
1228        }
1229
1230        public Arc[] getArcs() {
1231            if (arcs == null) {
1232                arcs = createArcs();
1233            }
1234            if (!arcsValid) {
1235                getSpanSizes();
1236                arcsValid = true;
1237            }
1238            return arcs;
1239        }
1240
1241        private boolean relax(int[] locations, Arc entry) {
1242            Interval span = entry.span;
1243            int u = span.min;
1244            int v = span.max;
1245            int value = entry.value.value;
1246            int candidate = locations[u] + value;
1247            if (candidate > locations[v]) {
1248                locations[v] = candidate;
1249                return true;
1250            }
1251            return false;
1252        }
1253
1254        /*
1255        Bellman-Ford variant - modified to reduce typical running time from O(N^2) to O(N)
1256
1257        GridLayout converts its requirements into a system of linear constraints of the
1258        form:
1259
1260        x[i] - x[j] < a[k]
1261
1262        Where the x[i] are variables and the a[k] are constants.
1263
1264        For example, if the variables were instead labeled x, y, z we might have:
1265
1266            x - y < 17
1267            y - z < 23
1268            z - x < 42
1269
1270        This is a special case of the Linear Programming problem that is, in turn,
1271        equivalent to the single-source shortest paths problem on a digraph, for
1272        which the O(n^2) Bellman-Ford algorithm the most commonly used general solution.
1273
1274        Other algorithms are faster in the case where no arcs have negative weights
1275        but allowing negative weights turns out to be the same as accommodating maximum
1276        size requirements as well as minimum ones.
1277
1278        Bellman-Ford works by iteratively 'relaxing' constraints over all nodes (an O(N)
1279        process) and performing this step N times. Proof of correctness hinges on the
1280        fact that there can be no negative weight chains of length > N - unless a
1281        'negative weight loop' exists. The algorithm catches this case in a final
1282        checking phase that reports failure.
1283
1284        By topologically sorting the nodes and checking this condition at each step
1285        typical layout problems complete after the first iteration and the algorithm
1286        completes in O(N) steps with very low constants.
1287        */
1288        private int[] solve(Arc[] arcs, int[] locations) {
1289            int N = getCount() + 1; // The number of vertices is the number of columns/rows + 1.
1290
1291            boolean changed = false;
1292            // We take one extra pass over traditional Bellman-Ford (and omit their final step)
1293            for (int i = 0; i < N; i++) {
1294                changed = false;
1295                for (int j = 0, length = arcs.length; j < length; j++) {
1296                    changed = changed | relax(locations, arcs[j]);
1297                }
1298                if (!changed) {
1299                    if (DEBUG) {
1300                        Log.d(TAG, "Iteration " +
1301                                " completed after " + (1 + i) + " steps out of " + N);
1302                    }
1303                    break;
1304                }
1305            }
1306            if (changed) {
1307                Log.d(TAG, "*** Algorithm failed to terminate ***");
1308            }
1309            return locations;
1310        }
1311
1312        private void computeMargins(boolean leading) {
1313            int[] margins = leading ? leadingMargins : trailingMargins;
1314            for (int i = 0, size = getChildCount(); i < size; i++) {
1315                View c = getChildAt(i);
1316                LayoutParams lp = getLayoutParams(c);
1317                Group g = horizontal ? lp.columnGroup : lp.rowGroup;
1318                Interval span = g.span;
1319                int index = leading ? span.min : span.max;
1320                margins[index] = max(margins[index], getMargin(c, leading, horizontal));
1321            }
1322        }
1323
1324        private int[] getLeadingMargins() {
1325            if (leadingMargins == null) {
1326                leadingMargins = new int[getCount() + 1];
1327            }
1328            if (!leadingMarginsValid) {
1329                computeMargins(true);
1330                leadingMarginsValid = true;
1331            }
1332            return leadingMargins;
1333        }
1334
1335        private int[] getTrailingMargins() {
1336            if (trailingMargins == null) {
1337                trailingMargins = new int[getCount() + 1];
1338            }
1339            if (!trailingMarginsValid) {
1340                computeMargins(false);
1341                trailingMarginsValid = true;
1342            }
1343            return trailingMargins;
1344        }
1345
1346        private void addMargins() {
1347            int[] leadingMargins = getLeadingMargins();
1348            int[] trailingMargins = getTrailingMargins();
1349
1350            int delta = 0;
1351            for (int i = 0, N = getCount(); i < N; i++) {
1352                int margins = leadingMargins[i] + trailingMargins[i + 1];
1353                delta += margins;
1354                minima[i + 1] += delta;
1355            }
1356        }
1357
1358        private int getLocationIncludingMargin(View view, boolean leading, int index) {
1359            int location = locations[index];
1360            int margin;
1361            if (!mMarginsIncludedInAlignment) {
1362                margin = (leading ? leadingMargins : trailingMargins)[index];
1363            } else {
1364                margin = 0;
1365            }
1366            return leading ? (location + margin) : (location - margin);
1367        }
1368
1369        private void computeMinima(int[] a) {
1370            Arrays.fill(a, MIN_VALUE);
1371            a[0] = 0;
1372            solve(getArcs(), a);
1373            if (!mMarginsIncludedInAlignment) {
1374                addMargins();
1375            }
1376        }
1377
1378        private int[] getMinima() {
1379            if (minima == null) {
1380                int N = getCount() + 1;
1381                minima = new int[N];
1382            }
1383            if (!minimaValid) {
1384                computeMinima(minima);
1385                minimaValid = true;
1386            }
1387            return minima;
1388        }
1389
1390        private void computeWeights() {
1391            for (int i = 0, N = getChildCount(); i < N; i++) {
1392                LayoutParams lp = getLayoutParams(getChildAt(i));
1393                Group g = horizontal ? lp.columnGroup : lp.rowGroup;
1394                Interval span = g.span;
1395                int penultimateIndex = span.max - 1;
1396                weights[penultimateIndex] += horizontal ? lp.columnWeight : lp.rowWeight;
1397            }
1398        }
1399
1400        private float[] getWeights() {
1401            if (weights == null) {
1402                int N = getCount();
1403                weights = new float[N];
1404            }
1405            computeWeights();
1406            return weights;
1407        }
1408
1409        private int[] getLocations() {
1410            if (locations == null) {
1411                int N = getCount() + 1;
1412                locations = new int[N];
1413            }
1414            return locations;
1415        }
1416
1417        // External entry points
1418
1419        private int size(int[] locations) {
1420            return max2(locations, 0) - locations[0];
1421        }
1422
1423        private int getMin() {
1424            return size(getMinima());
1425        }
1426
1427        private void layout(int targetSize) {
1428            int[] mins = getMinima();
1429
1430            int totalDelta = max(0, targetSize - size(mins)); // confine to expansion
1431
1432            float[] weights = getWeights();
1433            float totalWeight = sum(weights);
1434
1435            if (totalWeight == 0f && weights.length > 0) {
1436                weights[weights.length - 1] = 1;
1437                totalWeight = 1;
1438            }
1439
1440            int[] locations = getLocations();
1441            int cumulativeDelta = 0;
1442
1443            // note |weights| = |locations| - 1
1444            for (int i = 0; i < weights.length; i++) {
1445                float weight = weights[i];
1446                int delta = (int) (totalDelta * weight / totalWeight);
1447                cumulativeDelta += delta;
1448                locations[i + 1] = mins[i + 1] + cumulativeDelta;
1449
1450                totalDelta -= delta;
1451                totalWeight -= weight;
1452            }
1453        }
1454
1455        private void invalidateStructure() {
1456            countValid = false;
1457
1458            groupBounds = null;
1459            spanSizes = null;
1460            leadingMargins = null;
1461            trailingMargins = null;
1462            arcs = null;
1463            minima = null;
1464            weights = null;
1465            locations = null;
1466
1467            invalidateValues();
1468        }
1469
1470        private void invalidateValues() {
1471            groupBoundsValid = false;
1472            spanSizesValid = false;
1473            arcsValid = false;
1474            leadingMarginsValid = false;
1475            trailingMarginsValid = false;
1476            minimaValid = false;
1477        }
1478    }
1479
1480    /**
1481     * Layout information associated with each of the children of a GridLayout.
1482     * <p>
1483     * GridLayout supports both row and column spanning and arbitrary forms of alignment within
1484     * each cell group. The fundamental parameters associated with each cell group are
1485     * gathered into their vertical and horizontal components and stored
1486     * in the {@link #rowGroup} and {@link #columnGroup} layout parameters.
1487     * {@link Group Groups} are immutable structures and may be shared between the layout
1488     * parameters of different children.
1489     * <p>
1490     * The row and column groups contain the leading and trailing indices along each axis
1491     * and together specify the four grid indices that delimit the cells of this cell group.
1492     * <p>
1493     * The {@link Group#alignment alignment} fields of the row and column groups together specify
1494     * both aspects of alignment within the cell group. It is also possible to specify a child's
1495     * alignment within its cell group by using the {@link GridLayout.LayoutParams#setGravity(int)}
1496     * method.
1497     * <p>
1498     * See {@link GridLayout} for a description of the conventions used by GridLayout
1499     * in reference to grid indices.
1500     *
1501     * <h4>Default values</h4>
1502     *
1503     * <ul>
1504     *     <li>{@link #width} = {@link #WRAP_CONTENT}</li>
1505     *     <li>{@link #height} = {@link #WRAP_CONTENT}</li>
1506     *     <li>{@link #topMargin} = 0 when
1507     *          {@link GridLayout#setUseDefaultMargins(boolean) useDefaultMargins} is
1508     *          {@code false}; otherwise {@link #UNDEFINED}, to
1509     *          indicate that a default value should be computed on demand. </li>
1510     *     <li>{@link #leftMargin} = 0 when
1511     *          {@link GridLayout#setUseDefaultMargins(boolean) useDefaultMargins} is
1512     *          {@code false}; otherwise {@link #UNDEFINED}, to
1513     *          indicate that a default value should be computed on demand. </li>
1514     *     <li>{@link #bottomMargin} = 0 when
1515     *          {@link GridLayout#setUseDefaultMargins(boolean) useDefaultMargins} is
1516     *          {@code false}; otherwise {@link #UNDEFINED}, to
1517     *          indicate that a default value should be computed on demand. </li>
1518     *     <li>{@link #rightMargin} = 0 when
1519     *          {@link GridLayout#setUseDefaultMargins(boolean) useDefaultMargins} is
1520     *          {@code false}; otherwise {@link #UNDEFINED}, to
1521     *          indicate that a default value should be computed on demand. </li>
1522     *     <li>{@link #rowGroup}{@code .span} = {@code [0, 1]} </li>
1523     *     <li>{@link #rowGroup}{@code .alignment} = {@link #BASELINE} </li>
1524     *     <li>{@link #columnGroup}{@code .span} = {@code [0, 1]} </li>
1525     *     <li>{@link #columnGroup}{@code .alignment} = {@link #LEFT} </li>
1526     *     <li>{@link #rowWeight} = {@code 0f} </li>
1527     *     <li>{@link #columnWeight} = {@code 0f} </li>
1528     * </ul>
1529     *
1530     * @attr ref android.R.styleable#GridLayout_Layout_layout_row
1531     * @attr ref android.R.styleable#GridLayout_Layout_layout_rowSpan
1532     * @attr ref android.R.styleable#GridLayout_Layout_layout_rowWeight
1533     * @attr ref android.R.styleable#GridLayout_Layout_layout_column
1534     * @attr ref android.R.styleable#GridLayout_Layout_layout_columnSpan
1535     * @attr ref android.R.styleable#GridLayout_Layout_layout_columnWeight
1536     * @attr ref android.R.styleable#GridLayout_Layout_layout_gravity
1537     */
1538    public static class LayoutParams extends MarginLayoutParams {
1539
1540        // Default values
1541
1542        private static final int DEFAULT_WIDTH = WRAP_CONTENT;
1543        private static final int DEFAULT_HEIGHT = WRAP_CONTENT;
1544        private static final int DEFAULT_MARGIN = UNDEFINED;
1545        private static final int DEFAULT_ROW = UNDEFINED;
1546        private static final int DEFAULT_COLUMN = UNDEFINED;
1547        private static final Interval DEFAULT_SPAN = new Interval(UNDEFINED, UNDEFINED + 1);
1548        private static final int DEFAULT_SPAN_SIZE = DEFAULT_SPAN.size();
1549        private static final Alignment DEFAULT_COLUMN_ALIGNMENT = LEFT;
1550        private static final Alignment DEFAULT_ROW_ALIGNMENT = BASELINE;
1551        private static final Group DEFAULT_COLUMN_GROUP =
1552                new Group(DEFAULT_SPAN, DEFAULT_COLUMN_ALIGNMENT);
1553        private static final Group DEFAULT_ROW_GROUP =
1554                new Group(DEFAULT_SPAN, DEFAULT_ROW_ALIGNMENT);
1555        private static final int DEFAULT_WEIGHT_0 = 0;
1556        private static final int DEFAULT_WEIGHT_1 = 1;
1557
1558        // Misc
1559
1560        private static final Rect CONTAINER_BOUNDS = new Rect(0, 0, 2, 2);
1561        private static final Alignment[] COLUMN_ALIGNMENTS = { LEFT, CENTER, RIGHT };
1562        private static final Alignment[] ROW_ALIGNMENTS = { TOP, CENTER, BOTTOM };
1563
1564        // TypedArray indices
1565
1566        private static final int MARGIN = styleable.ViewGroup_MarginLayout_layout_margin;
1567        private static final int LEFT_MARGIN = styleable.ViewGroup_MarginLayout_layout_marginLeft;
1568        private static final int TOP_MARGIN = styleable.ViewGroup_MarginLayout_layout_marginTop;
1569        private static final int RIGHT_MARGIN = styleable.ViewGroup_MarginLayout_layout_marginRight;
1570        private static final int BOTTOM_MARGIN =
1571                styleable.ViewGroup_MarginLayout_layout_marginBottom;
1572
1573        private static final int COLUMN = styleable.GridLayout_Layout_layout_column;
1574        private static final int COLUMN_SPAN = styleable.GridLayout_Layout_layout_columnSpan;
1575        private static final int COLUMN_WEIGHT = styleable.GridLayout_Layout_layout_columnWeight;
1576        private static final int ROW = styleable.GridLayout_Layout_layout_row;
1577        private static final int ROW_SPAN = styleable.GridLayout_Layout_layout_rowSpan;
1578        private static final int ROW_WEIGHT = styleable.GridLayout_Layout_layout_rowWeight;
1579        private static final int GRAVITY = styleable.GridLayout_Layout_layout_gravity;
1580
1581        // Instance variables
1582
1583        /**
1584         * The group that specifies the vertical characteristics of the cell group
1585         * described by these layout parameters.
1586         */
1587        public Group rowGroup;
1588        /**
1589         * The group that specifies the horizontal characteristics of the cell group
1590         * described by these layout parameters.
1591         */
1592        public Group columnGroup;
1593        /**
1594         * The proportional space that should be taken by the associated row group
1595         * during excess space distribution.
1596         */
1597        public float rowWeight;
1598        /**
1599         * The proportional space that should be taken by the associated column group
1600         * during excess space distribution.
1601         */
1602        public float columnWeight;
1603
1604        // Constructors
1605
1606        private LayoutParams(
1607                int width, int height,
1608                int left, int top, int right, int bottom,
1609                Group rowGroup, Group columnGroup, float rowWeight, float columnWeight) {
1610            super(width, height);
1611            setMargins(left, top, right, bottom);
1612            this.rowGroup = rowGroup;
1613            this.columnGroup = columnGroup;
1614            this.rowWeight = rowWeight;
1615            this.columnWeight = columnWeight;
1616        }
1617
1618        /**
1619         * Constructs a new LayoutParams instance for this <code>rowGroup</code>
1620         * and <code>columnGroup</code>. All other fields are initialized with
1621         * default values as defined in {@link LayoutParams}.
1622         *
1623         * @param rowGroup    the rowGroup
1624         * @param columnGroup the columnGroup
1625         */
1626        public LayoutParams(Group rowGroup, Group columnGroup) {
1627            this(DEFAULT_WIDTH, DEFAULT_HEIGHT,
1628                    DEFAULT_MARGIN, DEFAULT_MARGIN, DEFAULT_MARGIN, DEFAULT_MARGIN,
1629                    rowGroup, columnGroup, DEFAULT_WEIGHT_0, DEFAULT_WEIGHT_0);
1630        }
1631
1632        /**
1633         * Constructs a new LayoutParams with default values as defined in {@link LayoutParams}.
1634         */
1635        public LayoutParams() {
1636            this(DEFAULT_ROW_GROUP, DEFAULT_COLUMN_GROUP);
1637        }
1638
1639        // Copying constructors
1640
1641        /**
1642         * {@inheritDoc}
1643         */
1644        public LayoutParams(ViewGroup.LayoutParams params) {
1645            super(params);
1646        }
1647
1648        /**
1649         * {@inheritDoc}
1650         */
1651        public LayoutParams(MarginLayoutParams params) {
1652            super(params);
1653        }
1654
1655        /**
1656         * {@inheritDoc}
1657         */
1658        public LayoutParams(LayoutParams that) {
1659            super(that);
1660            this.columnGroup = that.columnGroup;
1661            this.rowGroup = that.rowGroup;
1662            this.columnWeight = that.columnWeight;
1663            this.rowWeight = that.rowWeight;
1664        }
1665
1666        // AttributeSet constructors
1667
1668        private LayoutParams(Context context, AttributeSet attrs, int defaultGravity) {
1669            super(context, attrs);
1670            reInitSuper(context, attrs);
1671            init(context, attrs, defaultGravity);
1672        }
1673
1674        /**
1675         * {@inheritDoc}
1676         *
1677         * Values not defined in the attribute set take the default values
1678         * defined in {@link LayoutParams}.
1679         */
1680        public LayoutParams(Context context, AttributeSet attrs) {
1681            this(context, attrs, Gravity.NO_GRAVITY);
1682        }
1683
1684        // Implementation
1685
1686        private static boolean definesVertical(int gravity) {
1687            return gravity > 0 && (gravity & Gravity.VERTICAL_GRAVITY_MASK) != 0;
1688        }
1689
1690        private static boolean definesHorizontal(int gravity) {
1691            return gravity > 0 && (gravity & Gravity.HORIZONTAL_GRAVITY_MASK) != 0;
1692        }
1693
1694        private static <T> T getAlignment(T[] alignments, T fill, int min, int max,
1695                boolean isUndefined, T defaultValue) {
1696            if (isUndefined) {
1697                return defaultValue;
1698            }
1699            return min != max ? fill : alignments[min];
1700        }
1701
1702        // Reinitialise the margins using a different default policy than MarginLayoutParams.
1703        // Here we use the value UNDEFINED (as distinct from zero) to represent the undefined state
1704        // so that a layout manager default can be accessed post set up. We need this as, at the
1705        // point of installation, we do not know how many rows/cols there are and therefore
1706        // which elements are positioned next to the container's trailing edges. We need to
1707        // know this as margins around the container's boundary should have different
1708        // defaults to those between peers.
1709
1710        // This method could be parametrized and moved into MarginLayout.
1711        private void reInitSuper(Context context, AttributeSet attrs) {
1712            TypedArray a = context.obtainStyledAttributes(attrs, styleable.ViewGroup_MarginLayout);
1713            try {
1714                int margin = a.getDimensionPixelSize(MARGIN, DEFAULT_MARGIN);
1715
1716                this.leftMargin = a.getDimensionPixelSize(LEFT_MARGIN, margin);
1717                this.topMargin = a.getDimensionPixelSize(TOP_MARGIN, margin);
1718                this.rightMargin = a.getDimensionPixelSize(RIGHT_MARGIN, margin);
1719                this.bottomMargin = a.getDimensionPixelSize(BOTTOM_MARGIN, margin);
1720            } finally {
1721                a.recycle();
1722            }
1723        }
1724
1725        // Gravity. For conversion from the static the integers defined in the Gravity class,
1726        // use Gravity.apply() to apply gravity to a view of zero size and see where it ends up.
1727        private static Alignment getColumnAlignment(int gravity, int width) {
1728            Rect r = new Rect(0, 0, 0, 0);
1729            Gravity.apply(gravity, 0, 0, CONTAINER_BOUNDS, r);
1730
1731            boolean fill = (width == MATCH_PARENT);
1732            Alignment defaultAlignment = fill ? FILL : DEFAULT_COLUMN_ALIGNMENT;
1733            return getAlignment(COLUMN_ALIGNMENTS, FILL, r.left, r.right,
1734                    !definesHorizontal(gravity), defaultAlignment);
1735        }
1736
1737        private static Alignment getRowAlignment(int gravity, int height) {
1738            Rect r = new Rect(0, 0, 0, 0);
1739            Gravity.apply(gravity, 0, 0, CONTAINER_BOUNDS, r);
1740
1741            boolean fill = (height == MATCH_PARENT);
1742            Alignment defaultAlignment = fill ? FILL : DEFAULT_ROW_ALIGNMENT;
1743            return getAlignment(ROW_ALIGNMENTS, FILL, r.top, r.bottom,
1744                    !definesVertical(gravity), defaultAlignment);
1745        }
1746
1747        private int getDefaultWeight(int size) {
1748            return (size == MATCH_PARENT) ? DEFAULT_WEIGHT_1 : DEFAULT_WEIGHT_0;
1749        }
1750
1751        private void init(Context context, AttributeSet attrs, int defaultGravity) {
1752            TypedArray a = context.obtainStyledAttributes(attrs, styleable.GridLayout_Layout);
1753            try {
1754                int gravity = a.getInteger(GRAVITY, defaultGravity);
1755
1756                int column = a.getInteger(COLUMN, DEFAULT_COLUMN);
1757                int columnSpan = a.getInteger(COLUMN_SPAN, DEFAULT_SPAN_SIZE);
1758                Interval hSpan = new Interval(column, column + columnSpan);
1759                this.columnGroup = new Group(hSpan, getColumnAlignment(gravity, width));
1760                this.columnWeight = a.getFloat(COLUMN_WEIGHT, getDefaultWeight(width));
1761
1762                int row = a.getInteger(ROW, DEFAULT_ROW);
1763                int rowSpan = a.getInteger(ROW_SPAN, DEFAULT_SPAN_SIZE);
1764                Interval vSpan = new Interval(row, row + rowSpan);
1765                this.rowGroup = new Group(vSpan, getRowAlignment(gravity, height));
1766                this.rowWeight = a.getFloat(ROW_WEIGHT, getDefaultWeight(height));
1767            } finally {
1768                a.recycle();
1769            }
1770        }
1771
1772        /**
1773         * Describes how the child views are positioned. Default is {@code LEFT | BASELINE}.
1774         * See {@link android.view.Gravity}.
1775         *
1776         * @param gravity the new gravity value
1777         *
1778         * @attr ref android.R.styleable#GridLayout_Layout_layout_gravity
1779         */
1780        public void setGravity(int gravity) {
1781            columnGroup = columnGroup.copyWriteAlignment(getColumnAlignment(gravity, width));
1782            rowGroup = rowGroup.copyWriteAlignment(getRowAlignment(gravity, height));
1783        }
1784
1785        @Override
1786        protected void setBaseAttributes(TypedArray attributes, int widthAttr, int heightAttr) {
1787            this.width = attributes.getLayoutDimension(widthAttr, DEFAULT_WIDTH);
1788            this.height = attributes.getLayoutDimension(heightAttr, DEFAULT_HEIGHT);
1789        }
1790
1791        private void setRowGroupSpan(Interval span) {
1792            rowGroup = rowGroup.copyWriteSpan(span);
1793        }
1794
1795        private void setColumnGroupSpan(Interval span) {
1796            columnGroup = columnGroup.copyWriteSpan(span);
1797        }
1798    }
1799
1800    /*
1801    In place of a HashMap from span to Int, use an array of key/value pairs - stored in Arcs.
1802    Add the mutables completesCycle flag to avoid creating another hash table for detecting cycles.
1803     */
1804    private static class Arc {
1805        public final Interval span;
1806        public final MutableInt value;
1807        public boolean completesCycle;
1808
1809        public Arc(Interval span, MutableInt value) {
1810            this.span = span;
1811            this.value = value;
1812        }
1813
1814        @Override
1815        public String toString() {
1816            return span + " " + (completesCycle ? "+>" : "->") + " " + value;
1817        }
1818    }
1819
1820    // A mutable Integer - used to avoid heap allocation during the layout operation
1821
1822    private static class MutableInt {
1823        public int value;
1824
1825        private MutableInt() {
1826            reset();
1827        }
1828
1829        private MutableInt(int value) {
1830            this.value = value;
1831        }
1832
1833        private void reset() {
1834            value = Integer.MIN_VALUE;
1835        }
1836    }
1837
1838    /*
1839    This data structure is used in place of a Map where we have an index that refers to the order
1840    in which each key/value pairs were added to the map. In this case we store keys and values
1841    in arrays of a length that is equal to the number of unique keys. We also maintain an
1842    array of indexes from insertion order to the compacted arrays of keys and values.
1843
1844    Note that behavior differs from that of a LinkedHashMap in that repeated entries
1845    *do* get added multiples times. So the length of index is equals to the number of
1846    items added.
1847
1848    This is useful in the GridLayout class where we can rely on the order of children not
1849    changing during layout - to use integer-based lookup for our internal structures
1850    rather than using (and storing) an implementation of Map<Key, ?>.
1851     */
1852    @SuppressWarnings(value = "unchecked")
1853    private static class PackedMap<K, V> {
1854        public final int[] index;
1855        public final K[] keys;
1856        public final V[] values;
1857
1858        private PackedMap(K[] keys, V[] values) {
1859            this.index = createIndex(keys);
1860
1861            this.keys = compact(keys, index);
1862            this.values = compact(values, index);
1863        }
1864
1865        private K getKey(int i) {
1866            return keys[index[i]];
1867        }
1868
1869        private V getValue(int i) {
1870            return values[index[i]];
1871        }
1872
1873        private static <K> int[] createIndex(K[] keys) {
1874            int size = keys.length;
1875            int[] result = new int[size];
1876
1877            Map<K, Integer> keyToIndex = new HashMap<K, Integer>();
1878            for (int i = 0; i < size; i++) {
1879                K key = keys[i];
1880                Integer index = keyToIndex.get(key);
1881                if (index == null) {
1882                    index = keyToIndex.size();
1883                    keyToIndex.put(key, index);
1884                }
1885                result[i] = index;
1886            }
1887            return result;
1888        }
1889
1890        /*
1891        Create a compact array of keys or values using the supplied index.
1892         */
1893        private static <K> K[] compact(K[] a, int[] index) {
1894            int size = a.length;
1895            Class<?> componentType = a.getClass().getComponentType();
1896            K[] result = (K[]) Array.newInstance(componentType, max2(index, -1) + 1);
1897
1898            // this overwrite duplicates, retaining the last equivalent entry
1899            for (int i = 0; i < size; i++) {
1900                result[index[i]] = a[i];
1901            }
1902            return result;
1903        }
1904    }
1905
1906    /*
1907    For each Group (with a given alignment) we need to store the amount of space required
1908    before the alignment point and the amount of space required after it. One side of this
1909    calculation is always 0 for LEADING and TRAILING alignments but we don't make use of this.
1910    For CENTER and BASELINE alignments both sides are needed and in the BASELINE case no
1911    simple optimisations are possible.
1912
1913    The general algorithm therefore is to create a Map (actually a PackedMap) from
1914    Group to Bounds and to loop through all Views in the group taking the maximum
1915    of the values for each View.
1916    */
1917    private static class Bounds {
1918        public int before;
1919        public int after;
1920
1921        private Bounds() {
1922            reset();
1923        }
1924
1925        private void reset() {
1926            before = Integer.MIN_VALUE;
1927            after = Integer.MIN_VALUE;
1928        }
1929
1930        private void include(int before, int after) {
1931            this.before = max(this.before, before);
1932            this.after = max(this.after, after);
1933        }
1934
1935        private int size() {
1936            return before + after;
1937        }
1938
1939        @Override
1940        public String toString() {
1941            return "Bounds{" +
1942                    "before=" + before +
1943                    ", after=" + after +
1944                    '}';
1945        }
1946    }
1947
1948    /**
1949     * An Interval represents a contiguous range of values that lie between
1950     * the interval's {@link #min} and {@link #max} values.
1951     * <p>
1952     * Intervals are immutable so may be passed as values and used as keys in hash tables.
1953     * It is not necessary to have multiple instances of Intervals which have the same
1954     * {@link #min} and {@link #max} values.
1955     * <p>
1956     * Intervals are often written as {@code [min, max]} and represent the set of values
1957     * {@code x} such that {@code min <= x < max}.
1958     */
1959    /* package */ static class Interval {
1960        /**
1961         * The minimum value.
1962         */
1963        public final int min;
1964
1965        /**
1966         * The maximum value.
1967         */
1968        public final int max;
1969
1970        /**
1971         * Construct a new Interval, {@code interval}, where:
1972         * <ul>
1973         *     <li> {@code interval.min = min} </li>
1974         *     <li> {@code interval.max = max} </li>
1975         * </ul>
1976         *
1977         * @param min the minimum value.
1978         * @param max the maximum value.
1979         */
1980        public Interval(int min, int max) {
1981            this.min = min;
1982            this.max = max;
1983        }
1984
1985        private int size() {
1986            return max - min;
1987        }
1988
1989        private Interval inverse() {
1990            return new Interval(max, min);
1991        }
1992
1993        /**
1994         * Returns {@code true} if the {@link #getClass class},
1995         * {@link #min} and {@link #max} properties of this Interval and the
1996         * supplied parameter are pairwise equal; {@code false} otherwise.
1997         *
1998         * @param that the object to compare this interval with
1999         *
2000         * @return {@code true} if the specified object is equal to this
2001         *         {@code Interval}, {@code false} otherwise.
2002         */
2003        @Override
2004        public boolean equals(Object that) {
2005            if (this == that) {
2006                return true;
2007            }
2008            if (that == null || getClass() != that.getClass()) {
2009                return false;
2010            }
2011
2012            Interval interval = (Interval) that;
2013
2014            if (max != interval.max) {
2015                return false;
2016            }
2017            if (min != interval.min) {
2018                return false;
2019            }
2020
2021            return true;
2022        }
2023
2024        @Override
2025        public int hashCode() {
2026            int result = min;
2027            result = 31 * result + max;
2028            return result;
2029        }
2030
2031        @Override
2032        public String toString() {
2033            return "[" + min + ", " + max + "]";
2034        }
2035    }
2036
2037    /**
2038     * A group specifies either the horizontal or vertical characteristics of a group of
2039     * cells.
2040     * <p>
2041     * Groups are immutable and so may be shared between views with the same
2042     * {@code span} and {@code alignment}.
2043     */
2044    public static class Group {
2045        /**
2046         * The grid indices of the leading and trailing edges of this cell group for the
2047         * appropriate axis.
2048         * <p>
2049         * See {@link GridLayout} for a description of the conventions used by GridLayout
2050         * for grid indices.
2051         */
2052        /* package */ final Interval span;
2053        /**
2054         * Specifies how cells should be aligned in this group.
2055         * For row groups, this specifies the vertical alignment.
2056         * For column groups, this specifies the horizontal alignment.
2057         */
2058        public final Alignment alignment;
2059
2060        /**
2061         * Construct a new Group, {@code group}, where:
2062         * <ul>
2063         *     <li> {@code group.span = span} </li>
2064         *     <li> {@code group.alignment = alignment} </li>
2065         * </ul>
2066         *
2067         * @param span      the span
2068         * @param alignment the alignment
2069         */
2070        /* package */ Group(Interval span, Alignment alignment) {
2071            this.span = span;
2072            this.alignment = alignment;
2073        }
2074
2075        /**
2076         * Construct a new Group, {@code group}, where:
2077         * <ul>
2078         *     <li> {@code group.span = [start, start + size]} </li>
2079         *     <li> {@code group.alignment = alignment} </li>
2080         * </ul>
2081         *
2082         * @param start     the start
2083         * @param size      the size
2084         * @param alignment the alignment
2085         */
2086        public Group(int start, int size, Alignment alignment) {
2087            this(new Interval(start, start + size), alignment);
2088        }
2089
2090        /**
2091         * Construct a new Group, {@code group}, where:
2092         * <ul>
2093         *     <li> {@code group.span = [start, start + 1]} </li>
2094         *     <li> {@code group.alignment = alignment} </li>
2095         * </ul>
2096         *
2097         * @param start     the start index
2098         * @param alignment the alignment
2099         */
2100        public Group(int start, Alignment alignment) {
2101            this(start, 1, alignment);
2102        }
2103
2104        private Group copyWriteSpan(Interval span) {
2105            return new Group(span, alignment);
2106        }
2107
2108        private Group copyWriteAlignment(Alignment alignment) {
2109            return new Group(span, alignment);
2110        }
2111
2112        /**
2113         * Returns {@code true} if the {@link #getClass class}, {@link #alignment} and {@code span}
2114         * properties of this Group and the supplied parameter are pairwise equal,
2115         * {@code false} otherwise.
2116         *
2117         * @param that the object to compare this group with
2118         *
2119         * @return {@code true} if the specified object is equal to this
2120         *         {@code Group}; {@code false} otherwise
2121         */
2122        @Override
2123        public boolean equals(Object that) {
2124            if (this == that) {
2125                return true;
2126            }
2127            if (that == null || getClass() != that.getClass()) {
2128                return false;
2129            }
2130
2131            Group group = (Group) that;
2132
2133            if (!alignment.equals(group.alignment)) {
2134                return false;
2135            }
2136            if (!span.equals(group.span)) {
2137                return false;
2138            }
2139
2140            return true;
2141        }
2142
2143        @Override
2144        public int hashCode() {
2145            int result = span.hashCode();
2146            result = 31 * result + alignment.hashCode();
2147            return result;
2148        }
2149    }
2150
2151    /**
2152     * Alignments specify where a view should be placed within a cell group and
2153     * what size it should be.
2154     * <p>
2155     * The {@link LayoutParams} class contains a {@link LayoutParams#rowGroup rowGroup}
2156     * and a {@link LayoutParams#columnGroup columnGroup} each of which contains an
2157     * {@link Group#alignment alignment}. Overall placement of the view in the cell
2158     * group is specified by the two alignments which act along each axis independently.
2159     * <p>
2160     * An Alignment implementation must define {@link #getAlignmentValue(View, int, int)},
2161     * to return the appropriate value for the type of alignment being defined.
2162     * The enclosing algorithms position the children
2163     * so that the locations defined by the alignmnet values
2164     * are the same for all of the views in a group.
2165     * <p>
2166     *  The GridLayout class defines the most common alignments used in general layout:
2167     * {@link #TOP}, {@link #LEFT}, {@link #BOTTOM}, {@link #RIGHT}, {@link #CENTER}, {@link
2168     * #BASELINE} and {@link #FILL}.
2169     */
2170    public static abstract class Alignment {
2171        /**
2172         * Returns an alignment value. In the case of vertical alignments the value
2173         * returned should indicate the distance from the top of the view to the
2174         * alignment location.
2175         * For horizontal alignments measurement is made from the left edge of the component.
2176         *
2177         * @param view              the view to which this alignment should be applied
2178         * @param viewSize          the measured size of the view
2179         * @param measurementType   the type of measurement that should be made
2180         *
2181         * @return                  the alignment value
2182         */
2183        public abstract int getAlignmentValue(View view, int viewSize, int measurementType);
2184
2185        /**
2186         * Returns the size of the view specified by this alignment.
2187         * In the case of vertical alignments this method should return a height; for
2188         * horizontal alignments this method should return the width.
2189         * <p>
2190         * The default implementation returns {@code viewSize}.
2191         *
2192         * @param view              the view to which this alignment should be applied
2193         * @param viewSize          the measured size of the view
2194         * @param cellSize          the size of the cell into which this view will be placed
2195         * @param measurementType   the type of measurement that should be made
2196         *
2197         * @return                  the aligned size
2198         */
2199        public int getSizeInCell(View view, int viewSize, int cellSize, int measurementType) {
2200            return viewSize;
2201        }
2202    }
2203
2204    private static final Alignment LEADING = new Alignment() {
2205        public int getAlignmentValue(View view, int viewSize, int measurementType) {
2206            return 0;
2207        }
2208
2209    };
2210
2211    private static final Alignment TRAILING = new Alignment() {
2212        public int getAlignmentValue(View view, int viewSize, int measurementType) {
2213            return viewSize;
2214        }
2215    };
2216
2217    /**
2218     * Indicates that a view should be aligned with the <em>top</em>
2219     * edges of the other views in its cell group.
2220     */
2221    public static final Alignment TOP = LEADING;
2222
2223    /**
2224     * Indicates that a view should be aligned with the <em>bottom</em>
2225     * edges of the other views in its cell group.
2226     */
2227    public static final Alignment BOTTOM = TRAILING;
2228
2229    /**
2230     * Indicates that a view should be aligned with the <em>right</em>
2231     * edges of the other views in its cell group.
2232     */
2233    public static final Alignment RIGHT = TRAILING;
2234
2235    /**
2236     * Indicates that a view should be aligned with the <em>left</em>
2237     * edges of the other views in its cell group.
2238     */
2239    public static final Alignment LEFT = LEADING;
2240
2241    /**
2242     * Indicates that a view should be <em>centered</em> with the other views in its cell group.
2243     * This constant may be used in both {@link LayoutParams#rowGroup rowGroups} and {@link
2244     * LayoutParams#columnGroup columnGroups}.
2245     */
2246    public static final Alignment CENTER = new Alignment() {
2247        public int getAlignmentValue(View view, int viewSize, int measurementType) {
2248            return viewSize >> 1;
2249        }
2250    };
2251
2252    /**
2253     * Indicates that a view should be aligned with the <em>baselines</em>
2254     * of the other views in its cell group.
2255     * This constant may only be used as an alignment in {@link LayoutParams#rowGroup rowGroups}.
2256     *
2257     * @see View#getBaseline()
2258     */
2259    public static final Alignment BASELINE = new Alignment() {
2260        public int getAlignmentValue(View view, int viewSize, int measurementType) {
2261            if (view == null) {
2262                return UNDEFINED;
2263            }
2264            int baseline = view.getBaseline();
2265            if (baseline == -1) {
2266                return UNDEFINED;
2267            } else {
2268                return baseline;
2269            }
2270        }
2271
2272    };
2273
2274    /**
2275     * Indicates that a view should expanded to fit the boundaries of its cell group.
2276     * This constant may be used in both {@link LayoutParams#rowGroup rowGroups} and
2277     * {@link LayoutParams#columnGroup columnGroups}.
2278     */
2279    public static final Alignment FILL = new Alignment() {
2280        public int getAlignmentValue(View view, int viewSize, int measurementType) {
2281            return UNDEFINED;
2282        }
2283
2284        @Override
2285        public int getSizeInCell(View view, int viewSize, int cellSize, int measurementType) {
2286            return cellSize;
2287        }
2288    };
2289}
2290