GridLayoutpublic 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 android.support.v7.widget.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
GridLayout's distribution of excess space accommodates the principle of weight.
In the event that no weights are specified, 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. |
Fields Summary |
---|
public static final int | HORIZONTALThe horizontal orientation. | public static final int | VERTICALThe vertical orientation. | public static final int | UNDEFINEDThe 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_BOUNDSThis 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_MARGINSThis 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 | LEADINGIndicates that a view should be aligned with the start
edges of the other views in its cell group. | private static final Alignment | TRAILINGIndicates that a view should be aligned with the end
edges of the other views in its cell group. | public static final Alignment | TOPIndicates that a view should be aligned with the top
edges of the other views in its cell group. | public static final Alignment | BOTTOMIndicates that a view should be aligned with the bottom
edges of the other views in its cell group. | public static final Alignment | STARTIndicates that a view should be aligned with the start
edges of the other views in its cell group. | public static final Alignment | ENDIndicates that a view should be aligned with the end
edges of the other views in its cell group. | public static final Alignment | LEFTIndicates that a view should be aligned with the left
edges of the other views in its cell group. | public static final Alignment | RIGHTIndicates that a view should be aligned with the right
edges of the other views in its cell group. | public static final Alignment | CENTERIndicates 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 | BASELINEIndicates 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 | FILLIndicates 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, android.util.AttributeSet attrs, int defStyle){@inheritDoc}
// Constructors
super(context, attrs, defStyle);
mDefaultGap = context.getResources().getDimensionPixelOffset(R.dimen.default_gap);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.GridLayout);
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();
}
| public GridLayout(android.content.Context context, android.util.AttributeSet attrs){@inheritDoc}
this(context, attrs, 0);
| public GridLayout(android.content.Context context){@inheritDoc}
//noinspection NullableProblems
this(context, null);
|
Methods Summary |
---|
static int | adjust(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 boolean | canStretch(int flexibility)
return (flexibility & CAN_STRETCH) != 0;
| private void | checkLayoutParams(android.support.v7.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 boolean | checkLayoutParams(ViewGroup.LayoutParams p)
if (!(p instanceof LayoutParams)) {
return false;
}
LayoutParams lp = (LayoutParams) p;
checkLayoutParams(lp, true);
checkLayoutParams(lp, false);
return true;
| private static int | clip(android.support.v7.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 int | computeLayoutParamsHashCode()
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 void | consistencyCheck()
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.support.v7.widget.GridLayout$Alignment | createSwitchingAlignment(android.support.v7.widget.GridLayout$Alignment ltr, android.support.v7.widget.GridLayout$Alignment rtl)
return new Alignment() {
@Override
int getGravityOffset(View view, int cellDelta) {
boolean isLayoutRtl = ViewCompat.getLayoutDirection(view) ==
ViewCompat.LAYOUT_DIRECTION_RTL;
return (!isLayoutRtl ? ltr : rtl).getGravityOffset(view, cellDelta);
}
@Override
public int getAlignmentValue(View view, int viewSize, int mode) {
boolean isLayoutRtl = ViewCompat.getLayoutDirection(view) ==
ViewCompat.LAYOUT_DIRECTION_RTL;
return (!isLayoutRtl ? ltr : rtl).getAlignmentValue(view, viewSize, mode);
}
};
| private void | drawLine(android.graphics.Canvas graphics, int x1, int y1, int x2, int y2, android.graphics.Paint paint)
if (isLayoutRtlCompat()) {
int width = getWidth();
graphics.drawLine(width - x1, y1, width - x2, y2, paint);
} else {
graphics.drawLine(x1, y1, x2, y2, paint);
}
| private static boolean | fits(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.support.v7.widget.GridLayout$LayoutParams | generateDefaultLayoutParams()
return new LayoutParams();
| public android.support.v7.widget.GridLayout$LayoutParams | generateLayoutParams(android.util.AttributeSet attrs)
return new LayoutParams(getContext(), attrs);
| protected android.support.v7.widget.GridLayout$LayoutParams | generateLayoutParams(ViewGroup.LayoutParams p)
return new LayoutParams(p);
| static android.support.v7.widget.GridLayout$Alignment | getAlignment(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 | GravityCompat.RELATIVE_LAYOUT_DIRECTION):
return START;
case (AXIS_SPECIFIED | AXIS_PULL_AFTER | GravityCompat.RELATIVE_LAYOUT_DIRECTION):
return END;
default:
return UNDEFINED_ALIGNMENT;
}
| final android.support.v7.widget.GridLayout$Alignment | getAlignment(android.support.v7.widget.GridLayout$Alignment alignment, boolean horizontal)
return (alignment != UNDEFINED_ALIGNMENT) ? alignment :
(horizontal ? START : BASELINE);
| public int | getAlignmentMode()Returns the alignment mode.
return mAlignmentMode;
| public int | getColumnCount()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 mHorizontalAxis.getCount();
| private int | getDefaultMargin(android.view.View c, boolean horizontal, boolean leading)
if (c.getClass() == android.support.v7.widget.Space.class) {
return 0;
}
return mDefaultGap / 2;
| private int | getDefaultMargin(android.view.View c, boolean isAtEdge, boolean horizontal, boolean leading)
return /*isAtEdge ? DEFAULT_CONTAINER_MARGIN :*/ getDefaultMargin(c, horizontal, leading);
| private int | getDefaultMargin(android.view.View c, android.support.v7.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 && isLayoutRtlCompat()) ? !leading : leading;
boolean isAtEdge = leading1 ? (span.min == 0) : (span.max == axis.getCount());
return getDefaultMargin(c, isAtEdge, horizontal, leading);
| final android.support.v7.widget.GridLayout$LayoutParams | getLayoutParams(android.view.View c)
return (LayoutParams) c.getLayoutParams();
| private int | getMargin(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];
}
| int | getMargin1(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 int | getMeasurement(android.view.View c, boolean horizontal)
return horizontal ? c.getMeasuredWidth() : c.getMeasuredHeight();
| final int | getMeasurementIncludingMargin(android.view.View c, boolean horizontal)
if (c.getVisibility() == View.GONE) {
return 0;
}
return getMeasurement(c, horizontal) + getTotalMargin(c, horizontal);
| public int | getOrientation()Returns the current orientation.
return mOrientation;
| public android.util.Printer | getPrinter()Return the printer that will log diagnostics from this layout.
return mPrinter;
| public int | getRowCount()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 mVerticalAxis.getCount();
| private int | getTotalMargin(android.view.View child, boolean horizontal)
return getMargin(child, horizontal, true) + getMargin(child, horizontal, false);
| public boolean | getUseDefaultMargins()Returns whether or not this GridLayout will allocate default margins when no
corresponding layout parameters are defined.
return mUseDefaultMargins;
| private static void | handleInvalidParams(java.lang.String msg)
throw new IllegalArgumentException(msg + ". ");
| private void | invalidateStructure()
mLastLayoutParamsHashCode = UNINITIALIZED_HASH;
if (mHorizontalAxis != null) mHorizontalAxis.invalidateStructure();
if (mVerticalAxis != null) mVerticalAxis.invalidateStructure();
// This can end up being done twice. Better twice than not at all.
invalidateValues();
| private void | invalidateValues()
// 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 boolean | isColumnOrderPreserved()Returns whether or not column boundaries are ordered by their grid indices.
return mHorizontalAxis.isOrderPreserved();
| private boolean | isLayoutRtlCompat()
return ViewCompat.getLayoutDirection(this) == ViewCompat.LAYOUT_DIRECTION_RTL;
| public boolean | isRowOrderPreserved()Returns whether or not row boundaries are ordered by their grid indices.
return mVerticalAxis.isOrderPreserved();
| static int | max2(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 void | measureChildWithMargins2(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 void | measureChildrenWithMargins(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 void | onLayout(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 = !isLayoutRtlCompat() ? 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 void | onMeasure(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(
ViewCompat.resolveSizeAndState(measuredWidth, widthSpec, 0),
ViewCompat.resolveSizeAndState(measuredHeight, heightSpec, 0));
| private static void | procrusteanFill(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 void | requestLayout()
super.requestLayout();
invalidateStructure();
| public void | setAlignmentMode(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}.
this.mAlignmentMode = alignmentMode;
requestLayout();
| private static void | setCellGroup(android.support.v7.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 void | setColumnCount(int columnCount)ColumnCount is used only to generate default column/column indices when
they are not specified by a component's layout parameters.
mHorizontalAxis.setCount(columnCount);
invalidateStructure();
requestLayout();
| public void | setColumnOrderPreserved(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}.
mHorizontalAxis.setOrderPreserved(columnOrderPreserved);
invalidateStructure();
requestLayout();
| public void | setOrientation(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 android.widget.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}.
if (this.mOrientation != orientation) {
this.mOrientation = orientation;
invalidateStructure();
requestLayout();
}
| public void | setPrinter(android.util.Printer printer)Set the printer that will log diagnostics from this layout.
The default value is created by {@link android.util.LogPrinter}.
this.mPrinter = (printer == null) ? NO_PRINTER : printer;
| public void | setRowCount(int rowCount)RowCount is used only to generate default row/column indices when
they are not specified by a component's layout parameters.
mVerticalAxis.setCount(rowCount);
invalidateStructure();
requestLayout();
| public void | setRowOrderPreserved(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}.
mVerticalAxis.setOrderPreserved(rowOrderPreserved);
invalidateStructure();
requestLayout();
| public void | setUseDefaultMargins(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}.
this.mUseDefaultMargins = useDefaultMargins;
requestLayout();
| public static android.support.v7.widget.GridLayout$Spec | spec(int start, int size, android.support.v7.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}.
return new Spec(start != UNDEFINED, start, size, alignment, weight);
| public static android.support.v7.widget.GridLayout$Spec | spec(int start, android.support.v7.widget.GridLayout$Alignment alignment, float weight)Equivalent to: {@code spec(start, 1, alignment, weight)}.
return spec(start, 1, alignment, weight);
| public static android.support.v7.widget.GridLayout$Spec | spec(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}.
return spec(start, size, UNDEFINED_ALIGNMENT, weight);
| public static android.support.v7.widget.GridLayout$Spec | spec(int start, float weight)Equivalent to: {@code spec(start, 1, weight)}.
return spec(start, 1, weight);
| public static android.support.v7.widget.GridLayout$Spec | spec(int start, int size, android.support.v7.widget.GridLayout$Alignment alignment)Equivalent to: {@code spec(start, size, alignment, 0f)}.
return spec(start, size, alignment, Spec.DEFAULT_WEIGHT);
| public static android.support.v7.widget.GridLayout$Spec | spec(int start, android.support.v7.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}.
return spec(start, 1, alignment);
| public static android.support.v7.widget.GridLayout$Spec | spec(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}.
return spec(start, size, UNDEFINED_ALIGNMENT);
| public static android.support.v7.widget.GridLayout$Spec | spec(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}.
return spec(start, 1);
| private void | validateLayoutParams()
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;
}
|
|