FileDocCategorySizeDatePackage
GridLayout.javaAPI DocAndroid 5.1 API112172Thu Mar 12 22:22:10 GMT 2015android.widget

GridLayout

public class GridLayout extends android.view.ViewGroup
A layout that places its children in a rectangular grid.

The grid is composed of a set of infinitely thin lines that separate the viewing area into cells. Throughout the API, grid lines are referenced by grid indices. A grid with {@code N} columns has {@code N + 1} grid indices that run from {@code 0} through {@code N} inclusive. Regardless of how GridLayout is configured, grid index {@code 0} is fixed to the leading edge of the container and grid index {@code N} is fixed to its trailing edge (after padding is taken into account).

Row and Column Specs

Children occupy one or more contiguous cells, as defined by their {@link GridLayout.LayoutParams#rowSpec rowSpec} and {@link GridLayout.LayoutParams#columnSpec columnSpec} layout parameters. Each spec defines the set of rows or columns that are to be occupied; and how children should be aligned within the resulting group of cells. Although cells do not normally overlap in a GridLayout, GridLayout does not prevent children being defined to occupy the same cell or group of cells. In this case however, there is no guarantee that children will not themselves overlap after the layout operation completes.

Default Cell Assignment

If a child does not specify the row and column indices of the cell it wishes to occupy, GridLayout assigns cell locations automatically using its: {@link GridLayout#setOrientation(int) orientation}, {@link GridLayout#setRowCount(int) rowCount} and {@link GridLayout#setColumnCount(int) columnCount} properties.

Space

Space between children may be specified either by using instances of the dedicated {@link Space} view or by setting the {@link ViewGroup.MarginLayoutParams#leftMargin leftMargin}, {@link ViewGroup.MarginLayoutParams#topMargin topMargin}, {@link ViewGroup.MarginLayoutParams#rightMargin rightMargin} and {@link ViewGroup.MarginLayoutParams#bottomMargin bottomMargin} layout parameters. When the {@link GridLayout#setUseDefaultMargins(boolean) useDefaultMargins} property is set, default margins around children are automatically allocated based on the prevailing UI style guide for the platform. Each of the margins so defined may be independently overridden by an assignment to the appropriate layout parameter. Default values will generally produce a reasonable spacing between components but values may change between different releases of the platform.

Excess Space Distribution

As of API 21, GridLayout's distribution of excess space accomodates the principle of weight. In the event that no weights are specified, the previous conventions are respected and columns and rows are taken as flexible if their views specify some form of alignment within their groups.

The flexibility of a view is therefore influenced by its alignment which is, in turn, typically defined by setting the {@link LayoutParams#setGravity(int) gravity} property of the child's layout parameters. If either a weight or alignment were defined along a given axis then the component is taken as flexible in that direction. If no weight or alignment was set, the component is instead assumed to be inflexible.

Multiple components in the same row or column group are considered to act in parallel. Such a group is flexible only if all of the components within it are flexible. Row and column groups that sit either side of a common boundary are instead considered to act in series. The composite group made of these two elements is flexible if one of its elements is flexible.

To make a column stretch, make sure all of the components inside it define a weight or a gravity. To prevent a column from stretching, ensure that one of the components in the column does not define a weight or a gravity.

When the principle of flexibility does not provide complete disambiguation, GridLayout's algorithms favour rows and columns that are closer to its right and bottom edges. To be more precise, GridLayout treats each of its layout parameters as a constraint in the a set of variables that define the grid-lines along a given axis. During layout, GridLayout solves the constraints so as to return the unique solution to those constraints for which all variables are less-than-or-equal-to the corresponding value in any other valid solution.

Interpretation of GONE

For layout purposes, GridLayout treats views whose visibility status is {@link View#GONE GONE}, as having zero width and height. This is subtly different from the policy of ignoring views that are marked as GONE outright. If, for example, a gone-marked view was alone in a column, that column would itself collapse to zero width if and only if no gravity was defined on the view. If gravity was defined, then the gone-marked view has no effect on the layout and the container should be laid out as if the view had never been added to it. These statements apply equally to rows as well as columns, and to groups of rows or columns.

See {@link GridLayout.LayoutParams} for a full description of the layout parameters used by GridLayout.

attr
ref android.R.styleable#GridLayout_orientation
attr
ref android.R.styleable#GridLayout_rowCount
attr
ref android.R.styleable#GridLayout_columnCount
attr
ref android.R.styleable#GridLayout_useDefaultMargins
attr
ref android.R.styleable#GridLayout_rowOrderPreserved
attr
ref android.R.styleable#GridLayout_columnOrderPreserved

Fields Summary
public static final int
HORIZONTAL
The horizontal orientation.
public static final int
VERTICAL
The vertical orientation.
public static final int
UNDEFINED
The constant used to indicate that a value is undefined. Fields can use this value to indicate that their values have not yet been set. Similarly, methods can return this value to indicate that there is no suitable value that the implementation can return. The value used for the constant (currently {@link Integer#MIN_VALUE}) is intended to avoid confusion between valid values whose sign may not be known.
public static final int
ALIGN_BOUNDS
This constant is an {@link #setAlignmentMode(int) alignmentMode}. When the {@code alignmentMode} is set to {@link #ALIGN_BOUNDS}, alignment is made between the edges of each component's raw view boundary: i.e. the area delimited by the component's: {@link android.view.View#getTop() top}, {@link android.view.View#getLeft() left}, {@link android.view.View#getBottom() bottom} and {@link android.view.View#getRight() right} properties.

For example, when {@code GridLayout} is in {@link #ALIGN_BOUNDS} mode, children that belong to a row group that uses {@link #TOP} alignment will all return the same value when their {@link android.view.View#getTop()} method is called.

public static final int
ALIGN_MARGINS
This constant is an {@link #setAlignmentMode(int) alignmentMode}. When the {@code alignmentMode} is set to {@link #ALIGN_MARGINS}, the bounds of each view are extended outwards, according to their margins, before the edges of the resulting rectangle are aligned.

For example, when {@code GridLayout} is in {@link #ALIGN_MARGINS} mode, the quantity {@code top - layoutParams.topMargin} is the same for all children that belong to a row group that uses {@link #TOP} alignment.

static final int
MAX_SIZE
static final int
DEFAULT_CONTAINER_MARGIN
static final int
UNINITIALIZED_HASH
static final android.util.Printer
LOG_PRINTER
static final android.util.Printer
NO_PRINTER
private static final int
DEFAULT_ORIENTATION
private static final int
DEFAULT_COUNT
private static final boolean
DEFAULT_USE_DEFAULT_MARGINS
private static final boolean
DEFAULT_ORDER_PRESERVED
private static final int
DEFAULT_ALIGNMENT_MODE
private static final int
ORIENTATION
private static final int
ROW_COUNT
private static final int
COLUMN_COUNT
private static final int
USE_DEFAULT_MARGINS
private static final int
ALIGNMENT_MODE
private static final int
ROW_ORDER_PRESERVED
private static final int
COLUMN_ORDER_PRESERVED
final Axis
mHorizontalAxis
final Axis
mVerticalAxis
int
mOrientation
boolean
mUseDefaultMargins
int
mAlignmentMode
int
mDefaultGap
int
mLastLayoutParamsHashCode
android.util.Printer
mPrinter
static final Alignment
UNDEFINED_ALIGNMENT
private static final Alignment
LEADING
Indicates that a view should be aligned with the start edges of the other views in its cell group.
private static final Alignment
TRAILING
Indicates that a view should be aligned with the end edges of the other views in its cell group.
public static final Alignment
TOP
Indicates that a view should be aligned with the top edges of the other views in its cell group.
public static final Alignment
BOTTOM
Indicates that a view should be aligned with the bottom edges of the other views in its cell group.
public static final Alignment
START
Indicates that a view should be aligned with the start edges of the other views in its cell group.
public static final Alignment
END
Indicates that a view should be aligned with the end edges of the other views in its cell group.
public static final Alignment
LEFT
Indicates that a view should be aligned with the left edges of the other views in its cell group.
public static final Alignment
RIGHT
Indicates that a view should be aligned with the right edges of the other views in its cell group.
public static final Alignment
CENTER
Indicates that a view should be centered with the other views in its cell group. This constant may be used in both {@link LayoutParams#rowSpec rowSpecs} and {@link LayoutParams#columnSpec columnSpecs}.
public static final Alignment
BASELINE
Indicates that a view should be aligned with the baselines of the other views in its cell group. This constant may only be used as an alignment in {@link LayoutParams#rowSpec rowSpecs}.
public static final Alignment
FILL
Indicates that a view should expanded to fit the boundaries of its cell group. This constant may be used in both {@link LayoutParams#rowSpec rowSpecs} and {@link LayoutParams#columnSpec columnSpecs}.
private static final int
INFLEXIBLE
private static final int
CAN_STRETCH
Constructors Summary
public GridLayout(android.content.Context context)


    // Constructors

       
        this(context, null);
    
public GridLayout(android.content.Context context, android.util.AttributeSet attrs)

        this(context, attrs, 0);
    
public GridLayout(android.content.Context context, android.util.AttributeSet attrs, int defStyleAttr)

        this(context, attrs, defStyleAttr, 0);
    
public GridLayout(android.content.Context context, android.util.AttributeSet attrs, int defStyleAttr, int defStyleRes)

        super(context, attrs, defStyleAttr, defStyleRes);
        mDefaultGap = context.getResources().getDimensionPixelOffset(R.dimen.default_gap);
        final TypedArray a = context.obtainStyledAttributes(
                attrs, R.styleable.GridLayout, defStyleAttr, defStyleRes);
        try {
            setRowCount(a.getInt(ROW_COUNT, DEFAULT_COUNT));
            setColumnCount(a.getInt(COLUMN_COUNT, DEFAULT_COUNT));
            setOrientation(a.getInt(ORIENTATION, DEFAULT_ORIENTATION));
            setUseDefaultMargins(a.getBoolean(USE_DEFAULT_MARGINS, DEFAULT_USE_DEFAULT_MARGINS));
            setAlignmentMode(a.getInt(ALIGNMENT_MODE, DEFAULT_ALIGNMENT_MODE));
            setRowOrderPreserved(a.getBoolean(ROW_ORDER_PRESERVED, DEFAULT_ORDER_PRESERVED));
            setColumnOrderPreserved(a.getBoolean(COLUMN_ORDER_PRESERVED, DEFAULT_ORDER_PRESERVED));
        } finally {
            a.recycle();
        }
    
Methods Summary
static intadjust(int measureSpec, int delta)

        return makeMeasureSpec(
                MeasureSpec.getSize(measureSpec + delta),  MeasureSpec.getMode(measureSpec));
    
static T[]append(T[] a, T[] b)

        T[] result = (T[]) Array.newInstance(a.getClass().getComponentType(), a.length + b.length);
        System.arraycopy(a, 0, result, 0, a.length);
        System.arraycopy(b, 0, result, a.length, b.length);
        return result;
    
static booleancanStretch(int flexibility)


        
        return (flexibility & CAN_STRETCH) != 0;
    
private voidcheckLayoutParams(android.widget.GridLayout$LayoutParams lp, boolean horizontal)

        String groupName = horizontal ? "column" : "row";
        Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
        Interval span = spec.span;
        if (span.min != UNDEFINED && span.min < 0) {
            handleInvalidParams(groupName + " indices must be positive");
        }
        Axis axis = horizontal ? mHorizontalAxis : mVerticalAxis;
        int count = axis.definedCount;
        if (count != UNDEFINED) {
            if (span.max > count) {
                handleInvalidParams(groupName +
                        " indices (start + span) mustn't exceed the " + groupName + " count");
            }
            if (span.size() > count) {
                handleInvalidParams(groupName + " span mustn't exceed the " + groupName + " count");
            }
        }
    
protected booleancheckLayoutParams(ViewGroup.LayoutParams p)

        if (!(p instanceof LayoutParams)) {
            return false;
        }
        LayoutParams lp = (LayoutParams) p;

        checkLayoutParams(lp, true);
        checkLayoutParams(lp, false);

        return true;
    
private static intclip(android.widget.GridLayout$Interval minorRange, boolean minorWasDefined, int count)

        int size = minorRange.size();
        if (count == 0) {
            return size;
        }
        int min = minorWasDefined ? min(minorRange.min, count) : 0;
        return min(size, count - min);
    
private intcomputeLayoutParamsHashCode()

        int result = 1;
        for (int i = 0, N = getChildCount(); i < N; i++) {
            View c = getChildAt(i);
            if (c.getVisibility() == View.GONE) continue;
            LayoutParams lp = (LayoutParams) c.getLayoutParams();
            result = 31 * result + lp.hashCode();
        }
        return result;
    
private voidconsistencyCheck()

        if (mLastLayoutParamsHashCode == UNINITIALIZED_HASH) {
            validateLayoutParams();
            mLastLayoutParamsHashCode = computeLayoutParamsHashCode();
        } else if (mLastLayoutParamsHashCode != computeLayoutParamsHashCode()) {
            mPrinter.println("The fields of some layout parameters were modified in between "
                    + "layout operations. Check the javadoc for GridLayout.LayoutParams#rowSpec.");
            invalidateStructure();
            consistencyCheck();
        }
    
private static android.widget.GridLayout$AlignmentcreateSwitchingAlignment(android.widget.GridLayout$Alignment ltr, android.widget.GridLayout$Alignment rtl)


             
        return new Alignment() {
            @Override
            int getGravityOffset(View view, int cellDelta) {
                return (!view.isLayoutRtl() ? ltr : rtl).getGravityOffset(view, cellDelta);
            }

            @Override
            public int getAlignmentValue(View view, int viewSize, int mode) {
                return (!view.isLayoutRtl() ? ltr : rtl).getAlignmentValue(view, viewSize, mode);
            }
        };
    
private voiddrawLine(android.graphics.Canvas graphics, int x1, int y1, int x2, int y2, android.graphics.Paint paint)

        if (isLayoutRtl()) {
            int width = getWidth();
            graphics.drawLine(width - x1, y1, width - x2, y2, paint);
        } else {
            graphics.drawLine(x1, y1, x2, y2, paint);
        }
    
private static booleanfits(int[] a, int value, int start, int end)

        if (end > a.length) {
            return false;
        }
        for (int i = start; i < end; i++) {
            if (a[i] > value) {
                return false;
            }
        }
        return true;
    
protected android.widget.GridLayout$LayoutParamsgenerateDefaultLayoutParams()

        return new LayoutParams();
    
public android.widget.GridLayout$LayoutParamsgenerateLayoutParams(android.util.AttributeSet attrs)

        return new LayoutParams(getContext(), attrs);
    
protected android.widget.GridLayout$LayoutParamsgenerateLayoutParams(ViewGroup.LayoutParams p)

        return new LayoutParams(p);
    
static android.widget.GridLayout$AlignmentgetAlignment(int gravity, boolean horizontal)

        int mask = horizontal ? HORIZONTAL_GRAVITY_MASK : VERTICAL_GRAVITY_MASK;
        int shift = horizontal ? AXIS_X_SHIFT : AXIS_Y_SHIFT;
        int flags = (gravity & mask) >> shift;
        switch (flags) {
            case (AXIS_SPECIFIED | AXIS_PULL_BEFORE):
                return horizontal ? LEFT : TOP;
            case (AXIS_SPECIFIED | AXIS_PULL_AFTER):
                return horizontal ? RIGHT : BOTTOM;
            case (AXIS_SPECIFIED | AXIS_PULL_BEFORE | AXIS_PULL_AFTER):
                return FILL;
            case AXIS_SPECIFIED:
                return CENTER;
            case (AXIS_SPECIFIED | AXIS_PULL_BEFORE | RELATIVE_LAYOUT_DIRECTION):
                return START;
            case (AXIS_SPECIFIED | AXIS_PULL_AFTER | RELATIVE_LAYOUT_DIRECTION):
                return END;
            default:
                return UNDEFINED_ALIGNMENT;
        }
    
final android.widget.GridLayout$AlignmentgetAlignment(android.widget.GridLayout$Alignment alignment, boolean horizontal)

        return (alignment != UNDEFINED_ALIGNMENT) ? alignment :
                (horizontal ? START : BASELINE);
    
public intgetAlignmentMode()
Returns the alignment mode.

return
the alignment mode; either {@link #ALIGN_BOUNDS} or {@link #ALIGN_MARGINS}
see
#ALIGN_BOUNDS
see
#ALIGN_MARGINS
see
#setAlignmentMode(int)
attr
ref android.R.styleable#GridLayout_alignmentMode

        return mAlignmentMode;
    
public intgetColumnCount()
Returns the current number of columns. This is either the last value that was set with {@link #setColumnCount(int)} or, if no such value was set, the maximum value of each the upper bounds defined in {@link LayoutParams#columnSpec}.

return
the current number of columns
see
#setColumnCount(int)
see
LayoutParams#columnSpec
attr
ref android.R.styleable#GridLayout_columnCount

        return mHorizontalAxis.getCount();
    
private intgetDefaultMargin(android.view.View c, boolean horizontal, boolean leading)

noinspection
UnusedParameters

        if (c.getClass() == Space.class) {
            return 0;
        }
        return mDefaultGap / 2;
    
private intgetDefaultMargin(android.view.View c, boolean isAtEdge, boolean horizontal, boolean leading)

        return /*isAtEdge ? DEFAULT_CONTAINER_MARGIN :*/ getDefaultMargin(c, horizontal, leading);
    
private intgetDefaultMargin(android.view.View c, android.widget.GridLayout$LayoutParams p, boolean horizontal, boolean leading)

        if (!mUseDefaultMargins) {
            return 0;
        }
        Spec spec = horizontal ? p.columnSpec : p.rowSpec;
        Axis axis = horizontal ? mHorizontalAxis : mVerticalAxis;
        Interval span = spec.span;
        boolean leading1 = (horizontal && isLayoutRtl()) ? !leading : leading;
        boolean isAtEdge = leading1 ? (span.min == 0) : (span.max == axis.getCount());

        return getDefaultMargin(c, isAtEdge, horizontal, leading);
    
final android.widget.GridLayout$LayoutParamsgetLayoutParams(android.view.View c)

        return (LayoutParams) c.getLayoutParams();
    
private intgetMargin(android.view.View view, boolean horizontal, boolean leading)

        if (mAlignmentMode == ALIGN_MARGINS) {
            return getMargin1(view, horizontal, leading);
        } else {
            Axis axis = horizontal ? mHorizontalAxis : mVerticalAxis;
            int[] margins = leading ? axis.getLeadingMargins() : axis.getTrailingMargins();
            LayoutParams lp = getLayoutParams(view);
            Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
            int index = leading ? spec.span.min : spec.span.max;
            return margins[index];
        }
    
intgetMargin1(android.view.View view, boolean horizontal, boolean leading)

        LayoutParams lp = getLayoutParams(view);
        int margin = horizontal ?
                (leading ? lp.leftMargin : lp.rightMargin) :
                (leading ? lp.topMargin : lp.bottomMargin);
        return margin == UNDEFINED ? getDefaultMargin(view, lp, horizontal, leading) : margin;
    
private intgetMeasurement(android.view.View c, boolean horizontal)

        return horizontal ? c.getMeasuredWidth() : c.getMeasuredHeight();
    
final intgetMeasurementIncludingMargin(android.view.View c, boolean horizontal)

        if (c.getVisibility() == View.GONE) {
            return 0;
        }
        return getMeasurement(c, horizontal) + getTotalMargin(c, horizontal);
    
public intgetOrientation()
Returns the current orientation.

return
either {@link #HORIZONTAL} or {@link #VERTICAL}
see
#setOrientation(int)
attr
ref android.R.styleable#GridLayout_orientation

        return mOrientation;
    
public android.util.PrintergetPrinter()
Return the printer that will log diagnostics from this layout.

see
#setPrinter(android.util.Printer)
return
the printer associated with this view
hide

        return mPrinter;
    
public intgetRowCount()
Returns the current number of rows. This is either the last value that was set with {@link #setRowCount(int)} or, if no such value was set, the maximum value of each the upper bounds defined in {@link LayoutParams#rowSpec}.

return
the current number of rows
see
#setRowCount(int)
see
LayoutParams#rowSpec
attr
ref android.R.styleable#GridLayout_rowCount

        return mVerticalAxis.getCount();
    
private intgetTotalMargin(android.view.View child, boolean horizontal)

        return getMargin(child, horizontal, true) + getMargin(child, horizontal, false);
    
public booleangetUseDefaultMargins()
Returns whether or not this GridLayout will allocate default margins when no corresponding layout parameters are defined.

return
{@code true} if default margins should be allocated
see
#setUseDefaultMargins(boolean)
attr
ref android.R.styleable#GridLayout_useDefaultMargins

        return mUseDefaultMargins;
    
private static voidhandleInvalidParams(java.lang.String msg)

        throw new IllegalArgumentException(msg + ". ");
    
private voidinvalidateStructure()

        mLastLayoutParamsHashCode = UNINITIALIZED_HASH;
        mHorizontalAxis.invalidateStructure();
        mVerticalAxis.invalidateStructure();
        // This can end up being done twice. Better twice than not at all.
        invalidateValues();
    
private voidinvalidateValues()

        // Need null check because requestLayout() is called in View's initializer,
        // before we are set up.
        if (mHorizontalAxis != null && mVerticalAxis != null) {
            mHorizontalAxis.invalidateValues();
            mVerticalAxis.invalidateValues();
        }
    
public booleanisColumnOrderPreserved()
Returns whether or not column boundaries are ordered by their grid indices.

return
{@code true} if column boundaries must appear in the order of their indices, {@code false} otherwise
see
#setColumnOrderPreserved(boolean)
attr
ref android.R.styleable#GridLayout_columnOrderPreserved

        return mHorizontalAxis.isOrderPreserved();
    
public booleanisRowOrderPreserved()
Returns whether or not row boundaries are ordered by their grid indices.

return
{@code true} if row boundaries must appear in the order of their indices, {@code false} otherwise
see
#setRowOrderPreserved(boolean)
attr
ref android.R.styleable#GridLayout_rowOrderPreserved

        return mVerticalAxis.isOrderPreserved();
    
static intmax2(int[] a, int valueIfEmpty)

        int result = valueIfEmpty;
        for (int i = 0, N = a.length; i < N; i++) {
            result = Math.max(result, a[i]);
        }
        return result;
    
private voidmeasureChildWithMargins2(android.view.View child, int parentWidthSpec, int parentHeightSpec, int childWidth, int childHeight)

        int childWidthSpec = getChildMeasureSpec(parentWidthSpec,
                getTotalMargin(child, true), childWidth);
        int childHeightSpec = getChildMeasureSpec(parentHeightSpec,
                getTotalMargin(child, false), childHeight);
        child.measure(childWidthSpec, childHeightSpec);
    
private voidmeasureChildrenWithMargins(int widthSpec, int heightSpec, boolean firstPass)

        for (int i = 0, N = getChildCount(); i < N; i++) {
            View c = getChildAt(i);
            if (c.getVisibility() == View.GONE) continue;
            LayoutParams lp = getLayoutParams(c);
            if (firstPass) {
                measureChildWithMargins2(c, widthSpec, heightSpec, lp.width, lp.height);
                mHorizontalAxis.recordOriginalMeasurement(i);
                mVerticalAxis.recordOriginalMeasurement(i);
            } else {
                boolean horizontal = (mOrientation == HORIZONTAL);
                Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
                if (spec.alignment == FILL) {
                    Interval span = spec.span;
                    Axis axis = horizontal ? mHorizontalAxis : mVerticalAxis;
                    int[] locations = axis.getLocations();
                    int cellSize = locations[span.max] - locations[span.min];
                    int viewSize = cellSize - getTotalMargin(c, horizontal);
                    if (horizontal) {
                        measureChildWithMargins2(c, widthSpec, heightSpec, viewSize, lp.height);
                    } else {
                        measureChildWithMargins2(c, widthSpec, heightSpec, lp.width, viewSize);
                    }
                }
            }
        }
    
protected voidonChildVisibilityChanged(android.view.View child, int oldVisibility, int newVisibility)
We need to call invalidateStructure() when a child's GONE flag changes state. This implementation is a catch-all, invalidating on any change in the visibility flags.

hide

        super.onChildVisibilityChanged(child, oldVisibility, newVisibility);
        if (oldVisibility == GONE || newVisibility == GONE) {
        invalidateStructure();
        }
    
protected voidonDebugDraw(android.graphics.Canvas canvas)

hide

        Paint paint = new Paint();
        paint.setStyle(Paint.Style.STROKE);
        paint.setColor(Color.argb(50, 255, 255, 255));

        Insets insets = getOpticalInsets();

        int top    =               getPaddingTop()    + insets.top;
        int left   =               getPaddingLeft()   + insets.left;
        int right  = getWidth()  - getPaddingRight()  - insets.right;
        int bottom = getHeight() - getPaddingBottom() - insets.bottom;

        int[] xs = mHorizontalAxis.locations;
        if (xs != null) {
            for (int i = 0, length = xs.length; i < length; i++) {
                int x = left + xs[i];
                drawLine(canvas, x, top, x, bottom, paint);
            }
        }

        int[] ys = mVerticalAxis.locations;
        if (ys != null) {
            for (int i = 0, length = ys.length; i < length; i++) {
                int y = top + ys[i];
                drawLine(canvas, left, y, right, y, paint);
            }
        }

        super.onDebugDraw(canvas);
    
protected voidonDebugDrawMargins(android.graphics.Canvas canvas, android.graphics.Paint paint)

hide

        // Apply defaults, so as to remove UNDEFINED values
        LayoutParams lp = new LayoutParams();
        for (int i = 0; i < getChildCount(); i++) {
            View c = getChildAt(i);
            lp.setMargins(
                    getMargin1(c, true, true),
                    getMargin1(c, false, true),
                    getMargin1(c, true, false),
                    getMargin1(c, false, false));
            lp.onDebugDraw(c, canvas, paint);
        }
    
public voidonInitializeAccessibilityEvent(android.view.accessibility.AccessibilityEvent event)

        super.onInitializeAccessibilityEvent(event);
        event.setClassName(GridLayout.class.getName());
    
public voidonInitializeAccessibilityNodeInfo(android.view.accessibility.AccessibilityNodeInfo info)

        super.onInitializeAccessibilityNodeInfo(info);
        info.setClassName(GridLayout.class.getName());
    
protected voidonLayout(boolean changed, int left, int top, int right, int bottom)
{@inheritDoc}

        consistencyCheck();

        int targetWidth = right - left;
        int targetHeight = bottom - top;

        int paddingLeft = getPaddingLeft();
        int paddingTop = getPaddingTop();
        int paddingRight = getPaddingRight();
        int paddingBottom = getPaddingBottom();

        mHorizontalAxis.layout(targetWidth - paddingLeft - paddingRight);
        mVerticalAxis.layout(targetHeight - paddingTop - paddingBottom);

        int[] hLocations = mHorizontalAxis.getLocations();
        int[] vLocations = mVerticalAxis.getLocations();

        for (int i = 0, N = getChildCount(); i < N; i++) {
            View c = getChildAt(i);
            if (c.getVisibility() == View.GONE) continue;
            LayoutParams lp = getLayoutParams(c);
            Spec columnSpec = lp.columnSpec;
            Spec rowSpec = lp.rowSpec;

            Interval colSpan = columnSpec.span;
            Interval rowSpan = rowSpec.span;

            int x1 = hLocations[colSpan.min];
            int y1 = vLocations[rowSpan.min];

            int x2 = hLocations[colSpan.max];
            int y2 = vLocations[rowSpan.max];

            int cellWidth = x2 - x1;
            int cellHeight = y2 - y1;

            int pWidth = getMeasurement(c, true);
            int pHeight = getMeasurement(c, false);

            Alignment hAlign = getAlignment(columnSpec.alignment, true);
            Alignment vAlign = getAlignment(rowSpec.alignment, false);

            Bounds boundsX = mHorizontalAxis.getGroupBounds().getValue(i);
            Bounds boundsY = mVerticalAxis.getGroupBounds().getValue(i);

            // Gravity offsets: the location of the alignment group relative to its cell group.
            int gravityOffsetX = hAlign.getGravityOffset(c, cellWidth - boundsX.size(true));
            int gravityOffsetY = vAlign.getGravityOffset(c, cellHeight - boundsY.size(true));

            int leftMargin = getMargin(c, true, true);
            int topMargin = getMargin(c, false, true);
            int rightMargin = getMargin(c, true, false);
            int bottomMargin = getMargin(c, false, false);

            int sumMarginsX = leftMargin + rightMargin;
            int sumMarginsY = topMargin + bottomMargin;

            // Alignment offsets: the location of the view relative to its alignment group.
            int alignmentOffsetX = boundsX.getOffset(this, c, hAlign, pWidth + sumMarginsX, true);
            int alignmentOffsetY = boundsY.getOffset(this, c, vAlign, pHeight + sumMarginsY, false);

            int width = hAlign.getSizeInCell(c, pWidth, cellWidth - sumMarginsX);
            int height = vAlign.getSizeInCell(c, pHeight, cellHeight - sumMarginsY);

            int dx = x1 + gravityOffsetX + alignmentOffsetX;

            int cx = !isLayoutRtl() ? paddingLeft + leftMargin + dx :
                    targetWidth - width - paddingRight - rightMargin - dx;
            int cy = paddingTop + y1 + gravityOffsetY + alignmentOffsetY + topMargin;

            if (width != c.getMeasuredWidth() || height != c.getMeasuredHeight()) {
                c.measure(makeMeasureSpec(width, EXACTLY), makeMeasureSpec(height, EXACTLY));
            }
            c.layout(cx, cy, cx + width, cy + height);
        }
    
protected voidonMeasure(int widthSpec, int heightSpec)

        consistencyCheck();

        /** If we have been called by {@link View#measure(int, int)}, one of width or height
         *  is  likely to have changed. We must invalidate if so. */
        invalidateValues();

        int hPadding = getPaddingLeft() + getPaddingRight();
        int vPadding = getPaddingTop()  + getPaddingBottom();

        int widthSpecSansPadding =  adjust( widthSpec, -hPadding);
        int heightSpecSansPadding = adjust(heightSpec, -vPadding);

        measureChildrenWithMargins(widthSpecSansPadding, heightSpecSansPadding, true);

        int widthSansPadding;
        int heightSansPadding;

        // Use the orientation property to decide which axis should be laid out first.
        if (mOrientation == HORIZONTAL) {
            widthSansPadding = mHorizontalAxis.getMeasure(widthSpecSansPadding);
            measureChildrenWithMargins(widthSpecSansPadding, heightSpecSansPadding, false);
            heightSansPadding = mVerticalAxis.getMeasure(heightSpecSansPadding);
        } else {
            heightSansPadding = mVerticalAxis.getMeasure(heightSpecSansPadding);
            measureChildrenWithMargins(widthSpecSansPadding, heightSpecSansPadding, false);
            widthSansPadding = mHorizontalAxis.getMeasure(widthSpecSansPadding);
        }

        int measuredWidth  = Math.max(widthSansPadding  + hPadding, getSuggestedMinimumWidth());
        int measuredHeight = Math.max(heightSansPadding + vPadding, getSuggestedMinimumHeight());

        setMeasuredDimension(
                resolveSizeAndState(measuredWidth,   widthSpec, 0),
                resolveSizeAndState(measuredHeight, heightSpec, 0));
    
protected voidonSetLayoutParams(android.view.View child, ViewGroup.LayoutParams layoutParams)

hide

        super.onSetLayoutParams(child, layoutParams);

        if (!checkLayoutParams(layoutParams)) {
            handleInvalidParams("supplied LayoutParams are of the wrong type");
        }

        invalidateStructure();
    
protected voidonViewAdded(android.view.View child)

hide

        super.onViewAdded(child);
        invalidateStructure();
    
protected voidonViewRemoved(android.view.View child)

hide

        super.onViewRemoved(child);
        invalidateStructure();
    
private static voidprocrusteanFill(int[] a, int start, int end, int value)

        int length = a.length;
        Arrays.fill(a, Math.min(start, length), Math.min(end, length), value);
    
public voidrequestLayout()

        super.requestLayout();
        invalidateValues();
    
public voidsetAlignmentMode(int alignmentMode)
Sets the alignment mode to be used for all of the alignments between the children of this container.

The default value of this property is {@link #ALIGN_MARGINS}.

param
alignmentMode either {@link #ALIGN_BOUNDS} or {@link #ALIGN_MARGINS}
see
#ALIGN_BOUNDS
see
#ALIGN_MARGINS
see
#getAlignmentMode()
attr
ref android.R.styleable#GridLayout_alignmentMode

        this.mAlignmentMode = alignmentMode;
        requestLayout();
    
private static voidsetCellGroup(android.widget.GridLayout$LayoutParams lp, int row, int rowSpan, int col, int colSpan)

        lp.setRowSpecSpan(new Interval(row, row + rowSpan));
        lp.setColumnSpecSpan(new Interval(col, col + colSpan));
    
public voidsetColumnCount(int columnCount)
ColumnCount is used only to generate default column/column indices when they are not specified by a component's layout parameters.

param
columnCount the number of columns.
see
#getColumnCount()
see
LayoutParams#columnSpec
attr
ref android.R.styleable#GridLayout_columnCount

        mHorizontalAxis.setCount(columnCount);
        invalidateStructure();
        requestLayout();
    
public voidsetColumnOrderPreserved(boolean columnOrderPreserved)
When this property is {@code true}, GridLayout is forced to place the column boundaries so that their associated grid indices are in ascending order in the view.

When this property is {@code false} GridLayout is at liberty to place the horizontal column boundaries in whatever order best fits the given constraints.

The default value of this property is {@code true}.

param
columnOrderPreserved use {@code true} to force GridLayout to respect the order of column boundaries.
see
#isColumnOrderPreserved()
attr
ref android.R.styleable#GridLayout_columnOrderPreserved

        mHorizontalAxis.setOrderPreserved(columnOrderPreserved);
        invalidateStructure();
        requestLayout();
    
public voidsetOrientation(int orientation)
GridLayout uses the orientation property for two purposes:
  • To control the 'direction' in which default row/column indices are generated when they are not specified in a component's layout parameters.
  • To control which axis should be processed first during the layout operation: when orientation is {@link #HORIZONTAL} the horizontal axis is laid out first.
The order in which axes are laid out is important if, for example, the height of one of GridLayout's children is dependent on its width - and its width is, in turn, dependent on the widths of other components.

If your layout contains a {@link TextView} (or derivative: {@code Button}, {@code EditText}, {@code CheckBox}, etc.) which is in multi-line mode (the default) it is normally best to leave GridLayout's orientation as {@code HORIZONTAL} - because {@code TextView} is capable of deriving its height for a given width, but not the other way around.

Other than the effects above, orientation does not affect the actual layout operation of GridLayout, so it's fine to leave GridLayout in {@code HORIZONTAL} mode even if the height of the intended layout greatly exceeds its width.

The default value of this property is {@link #HORIZONTAL}.

param
orientation either {@link #HORIZONTAL} or {@link #VERTICAL}
see
#getOrientation()
attr
ref android.R.styleable#GridLayout_orientation

        if (this.mOrientation != orientation) {
            this.mOrientation = orientation;
            invalidateStructure();
            requestLayout();
        }
    
public voidsetPrinter(android.util.Printer printer)
Set the printer that will log diagnostics from this layout. The default value is created by {@link android.util.LogPrinter}.

param
printer the printer associated with this layout
see
#getPrinter()
hide

        this.mPrinter = (printer == null) ? NO_PRINTER : printer;
    
public voidsetRowCount(int rowCount)
RowCount is used only to generate default row/column indices when they are not specified by a component's layout parameters.

param
rowCount the number of rows
see
#getRowCount()
see
LayoutParams#rowSpec
attr
ref android.R.styleable#GridLayout_rowCount

        mVerticalAxis.setCount(rowCount);
        invalidateStructure();
        requestLayout();
    
public voidsetRowOrderPreserved(boolean rowOrderPreserved)
When this property is {@code true}, GridLayout is forced to place the row boundaries so that their associated grid indices are in ascending order in the view.

When this property is {@code false} GridLayout is at liberty to place the vertical row boundaries in whatever order best fits the given constraints.

The default value of this property is {@code true}.

param
rowOrderPreserved {@code true} to force GridLayout to respect the order of row boundaries
see
#isRowOrderPreserved()
attr
ref android.R.styleable#GridLayout_rowOrderPreserved

        mVerticalAxis.setOrderPreserved(rowOrderPreserved);
        invalidateStructure();
        requestLayout();
    
public voidsetUseDefaultMargins(boolean useDefaultMargins)
When {@code true}, GridLayout allocates default margins around children based on the child's visual characteristics. Each of the margins so defined may be independently overridden by an assignment to the appropriate layout parameter.

When {@code false}, the default value of all margins is zero.

When setting to {@code true}, consider setting the value of the {@link #setAlignmentMode(int) alignmentMode} property to {@link #ALIGN_BOUNDS}.

The default value of this property is {@code false}.

param
useDefaultMargins use {@code true} to make GridLayout allocate default margins
see
#getUseDefaultMargins()
see
#setAlignmentMode(int)
see
MarginLayoutParams#leftMargin
see
MarginLayoutParams#topMargin
see
MarginLayoutParams#rightMargin
see
MarginLayoutParams#bottomMargin
attr
ref android.R.styleable#GridLayout_useDefaultMargins

        this.mUseDefaultMargins = useDefaultMargins;
        requestLayout();
    
public static android.widget.GridLayout$Specspec(int start, int size, android.widget.GridLayout$Alignment alignment, float weight)
Return a Spec, {@code spec}, where:
  • {@code spec.span = [start, start + size]}
  • {@code spec.alignment = alignment}
  • {@code spec.weight = weight}

To leave the start index undefined, use the value {@link #UNDEFINED}.

param
start the start
param
size the size
param
alignment the alignment
param
weight the weight

        return new Spec(start != UNDEFINED, start, size, alignment, weight);
    
public static android.widget.GridLayout$Specspec(int start, android.widget.GridLayout$Alignment alignment, float weight)
Equivalent to: {@code spec(start, 1, alignment, weight)}.

param
start the start
param
alignment the alignment
param
weight the weight

        return spec(start, 1, alignment, weight);
    
public static android.widget.GridLayout$Specspec(int start, int size, float weight)
Equivalent to: {@code spec(start, 1, default_alignment, weight)} - where {@code default_alignment} is specified in {@link android.widget.GridLayout.LayoutParams}.

param
start the start
param
size the size
param
weight the weight

        return spec(start, size, UNDEFINED_ALIGNMENT, weight);
    
public static android.widget.GridLayout$Specspec(int start, float weight)
Equivalent to: {@code spec(start, 1, weight)}.

param
start the start
param
weight the weight

        return spec(start, 1, weight);
    
public static android.widget.GridLayout$Specspec(int start, int size, android.widget.GridLayout$Alignment alignment)
Equivalent to: {@code spec(start, size, alignment, 0f)}.

param
start the start
param
size the size
param
alignment the alignment

        return spec(start, size, alignment, Spec.DEFAULT_WEIGHT);
    
public static android.widget.GridLayout$Specspec(int start, android.widget.GridLayout$Alignment alignment)
Return a Spec, {@code spec}, where:
  • {@code spec.span = [start, start + 1]}
  • {@code spec.alignment = alignment}

To leave the start index undefined, use the value {@link #UNDEFINED}.

param
start the start index
param
alignment the alignment
see
#spec(int, int, Alignment)

        return spec(start, 1, alignment);
    
public static android.widget.GridLayout$Specspec(int start, int size)
Return a Spec, {@code spec}, where:
  • {@code spec.span = [start, start + size]}

To leave the start index undefined, use the value {@link #UNDEFINED}.

param
start the start
param
size the size
see
#spec(int, Alignment)

        return spec(start, size, UNDEFINED_ALIGNMENT);
    
public static android.widget.GridLayout$Specspec(int start)
Return a Spec, {@code spec}, where:
  • {@code spec.span = [start, start + 1]}

To leave the start index undefined, use the value {@link #UNDEFINED}.

param
start the start index
see
#spec(int, int)

        return spec(start, 1);
    
private voidvalidateLayoutParams()

        final boolean horizontal = (mOrientation == HORIZONTAL);
        final Axis axis = horizontal ? mHorizontalAxis : mVerticalAxis;
        final int count = (axis.definedCount != UNDEFINED) ? axis.definedCount : 0;

        int major = 0;
        int minor = 0;
        int[] maxSizes = new int[count];

        for (int i = 0, N = getChildCount(); i < N; i++) {
            LayoutParams lp = (LayoutParams) getChildAt(i).getLayoutParams();

            final Spec majorSpec = horizontal ? lp.rowSpec : lp.columnSpec;
            final Interval majorRange = majorSpec.span;
            final boolean majorWasDefined = majorSpec.startDefined;
            final int majorSpan = majorRange.size();
            if (majorWasDefined) {
                major = majorRange.min;
            }

            final Spec minorSpec = horizontal ? lp.columnSpec : lp.rowSpec;
            final Interval minorRange = minorSpec.span;
            final boolean minorWasDefined = minorSpec.startDefined;
            final int minorSpan = clip(minorRange, minorWasDefined, count);
            if (minorWasDefined) {
                minor = minorRange.min;
            }

            if (count != 0) {
                // Find suitable row/col values when at least one is undefined.
                if (!majorWasDefined || !minorWasDefined) {
                    while (!fits(maxSizes, major, minor, minor + minorSpan)) {
                        if (minorWasDefined) {
                            major++;
                        } else {
                            if (minor + minorSpan <= count) {
                                minor++;
                            } else {
                                minor = 0;
                                major++;
                            }
                        }
                    }
                }
                procrusteanFill(maxSizes, minor, minor + minorSpan, major + majorSpan);
            }

            if (horizontal) {
                setCellGroup(lp, major, majorSpan, minor, minorSpan);
            } else {
                setCellGroup(lp, minor, minorSpan, major, majorSpan);
            }

            minor = minor + minorSpan;
        }