/* * Copyright (C) 2011 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.widget; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Rect; import android.util.AttributeSet; import android.util.Log; import android.util.Pair; import android.view.Gravity; import android.view.View; import android.view.ViewGroup; import com.android.internal.R; import java.lang.reflect.Array; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import static java.lang.Math.max; import static java.lang.Math.min; /** * 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). * *
*
* 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 */ public class GridLayout extends ViewGroup { // Public constants /** * The horizontal orientation. */ public static final int HORIZONTAL = LinearLayout.HORIZONTAL; /** * The vertical orientation. */ public static final int VERTICAL = LinearLayout.VERTICAL; /** * 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 UNDEFINED = Integer.MIN_VALUE; /** * 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. * * @see #setAlignmentMode(int) */ public static final int ALIGN_BOUNDS = 0; /** * 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. * * @see #setAlignmentMode(int) */ public static final int ALIGN_MARGINS = 1; // Misc constants private static final String TAG = GridLayout.class.getName(); static boolean DEBUG = false; private static final int PRF = 1; // Defaults private static final int DEFAULT_ORIENTATION = HORIZONTAL; private static final int DEFAULT_COUNT = UNDEFINED; private static final boolean DEFAULT_USE_DEFAULT_MARGINS = false; private static final boolean DEFAULT_ORDER_PRESERVED = false; private static final int DEFAULT_ALIGNMENT_MODE = ALIGN_MARGINS; private static final int DEFAULT_CONTAINER_MARGIN = 0; private static final int MAX_SIZE = 100000; // TypedArray indices private static final int ORIENTATION = R.styleable.GridLayout_orientation; private static final int ROW_COUNT = R.styleable.GridLayout_rowCount; private static final int COLUMN_COUNT = R.styleable.GridLayout_columnCount; private static final int USE_DEFAULT_MARGINS = R.styleable.GridLayout_useDefaultMargins; private static final int ALIGNMENT_MODE = R.styleable.GridLayout_alignmentMode; private static final int ROW_ORDER_PRESERVED = R.styleable.GridLayout_rowOrderPreserved; private static final int COLUMN_ORDER_PRESERVED = R.styleable.GridLayout_columnOrderPreserved; // Instance variables private final Axis mHorizontalAxis = new Axis(true); private final Axis mVerticalAxis = new Axis(false); private boolean mLayoutParamsValid = false; private int mOrientation = DEFAULT_ORIENTATION; private boolean mUseDefaultMargins = DEFAULT_USE_DEFAULT_MARGINS; private int mAlignmentMode = DEFAULT_ALIGNMENT_MODE; private int mDefaultGravity = Gravity.NO_GRAVITY; private int mDefaultGap; // Constructors /** * {@inheritDoc} */ public GridLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); if (DEBUG) { setWillNotDraw(false); } 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)); mOrientation = a.getInt(ORIENTATION, DEFAULT_ORIENTATION); mUseDefaultMargins = a.getBoolean(USE_DEFAULT_MARGINS, DEFAULT_USE_DEFAULT_MARGINS); mAlignmentMode = 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(); } } /** * {@inheritDoc} */ public GridLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); } /** * {@inheritDoc} */ public GridLayout(Context context) { this(context, null); } // Implementation /** * Returns the current orientation. * * @return either {@link #HORIZONTAL} or {@link #VERTICAL} * * @see #setOrientation(int) * * @attr ref android.R.styleable#GridLayout_orientation */ public int getOrientation() { return mOrientation; } /** * The orientation property does not affect layout. Orientation is used * only to generate default row/column indices when they are not specified * by a component's layout parameters. *
* 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 */ public void setOrientation(int orientation) { if (mOrientation != orientation) { mOrientation = orientation; requestLayout(); } } /** * 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 */ public int getRowCount() { return mVerticalAxis.getCount(); } /** * The rowCount property does not affect layout. 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 */ public void setRowCount(int rowCount) { mVerticalAxis.setCount(rowCount); } /** * 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 */ public int getColumnCount() { return mHorizontalAxis.getCount(); } /** * The columnCount property does not affect layout. 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 */ public void setColumnCount(int columnCount) { mHorizontalAxis.setCount(columnCount); } /** * 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 */ public boolean getUseDefaultMargins() { return mUseDefaultMargins; } /** * 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 */ public void setUseDefaultMargins(boolean useDefaultMargins) { mUseDefaultMargins = useDefaultMargins; if (useDefaultMargins) { int padding = mDefaultGap; setPadding(padding, padding, padding, padding); } requestLayout(); } /** * 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 */ public int getAlignmentMode() { return mAlignmentMode; } /** * 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 */ public void setAlignmentMode(int alignmentMode) { mAlignmentMode = alignmentMode; requestLayout(); } /** * 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 */ public boolean isRowOrderPreserved() { return mVerticalAxis.isOrderPreserved(); } /** * When this property is {@code false}, the default state, GridLayout * is at liberty to choose an order that better suits the heights of its children.
* 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. *
* GridLayout implements this specification by creating ordering constraints between * the variables that represent the locations of the row boundaries. * * When this property is {@code true}, constraints are added for each pair of consecutive * indices: i.e. between row boundaries: {@code [0..1], [1..2], [2..3],...} etc. * * When the property is {@code false}, the ordering constraints are placed * only between boundaries that separate opposing edges of the layout's children. *
* The default value of this property is {@code false}. * @param rowOrderPreserved {@code true} to force GridLayout to respect the order * of row boundaries * * @see #isRowOrderPreserved() * * @attr ref android.R.styleable#GridLayout_rowOrderPreserved */ public void setRowOrderPreserved(boolean rowOrderPreserved) { mVerticalAxis.setOrderPreserved(rowOrderPreserved); invalidateStructure(); requestLayout(); } /** * 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 */ public boolean isColumnOrderPreserved() { return mHorizontalAxis.isOrderPreserved(); } /** * When this property is {@code false}, the default state, GridLayout * is at liberty to choose an order that better suits the widths of its children.
* 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. *
* GridLayout implements this specification by creating ordering constraints between * the variables that represent the locations of the column boundaries. * * When this property is {@code true}, constraints are added for each pair of consecutive * indices: i.e. between column boundaries: {@code [0..1], [1..2], [2..3],...} etc. * * When the property is {@code false}, the ordering constraints are placed * only between boundaries that separate opposing edges of the layout's children. *
* The default value of this property is {@code false}.
*
* @param columnOrderPreserved use {@code true} to force GridLayout to respect the order
* of column boundaries.
*
* @see #isColumnOrderPreserved()
*
* @attr ref android.R.styleable#GridLayout_columnOrderPreserved
*/
public void setColumnOrderPreserved(boolean columnOrderPreserved) {
mHorizontalAxis.setOrderPreserved(columnOrderPreserved);
invalidateStructure();
requestLayout();
}
private 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 static int sum(int[] a) {
int result = 0;
for (int i = 0, N = a.length; i < N; i++) {
result += a[i];
}
return result;
}
private static
* GridLayout supports both row and column spanning and arbitrary forms of alignment within
* each cell group. The fundamental parameters associated with each cell group are
* gathered into their vertical and horizontal components and stored
* in the {@link #rowSpec} and {@link #columnSpec} layout parameters.
* {@link android.widget.GridLayout.Spec Specs} are immutable structures
* and may be shared between the layout parameters of different children.
*
* The row and column specs contain the leading and trailing indices along each axis
* and together specify the four grid indices that delimit the cells of this cell group.
*
* The alignment properties of the row and column specs together specify
* both aspects of alignment within the cell group. It is also possible to specify a child's
* alignment within its cell group by using the {@link GridLayout.LayoutParams#setGravity(int)}
* method.
*
* See {@link GridLayout} for a description of the conventions used by GridLayout
* in reference to grid indices.
*
*
* Intervals are immutable so may be passed as values and used as keys in hash tables.
* It is not necessary to have multiple instances of Intervals which have the same
* {@link #min} and {@link #max} values.
*
* Intervals are often written as {@code [min, max]} and represent the set of values
* {@code x} such that {@code min <= x < max}.
*/
static class Interval {
private static final Interval GONE = new Interval(UNDEFINED, UNDEFINED);
/**
* The minimum value.
*/
public final int min;
/**
* The maximum value.
*/
public final int max;
/**
* Construct a new Interval, {@code interval}, where:
*
* See {@link GridLayout} for a description of the conventions used by GridLayout
* for grid indices.
*/
final Interval span;
/**
* Specifies how cells should be aligned in this group.
* For row groups, this specifies the vertical alignment.
* For column groups, this specifies the horizontal alignment.
*/
final Alignment alignment;
/**
* The flexibility field tells GridLayout how to derive minimum and maximum size
* values for a component. Specifications are made with respect to a child's
* 'measured size'. A child's measured size is, in turn, controlled by its
* height and width layout parameters which either specify a size or, in
* the case of {@link LayoutParams#WRAP_CONTENT WRAP_CONTENT}, defer to
* the computed size of the component.
*
* @see GridLayout#CAN_STRETCH
*/
final int flexibility;
private Spec(Interval span, Alignment alignment, int flexibility) {
this.span = span;
this.alignment = alignment;
this.flexibility = flexibility;
}
private Spec(Interval span, Alignment alignment) {
this(span, alignment, DEFAULT_FLEXIBILITY);
}
/* Copying constructor */
private Spec(Spec that) {
this(that.span, that.alignment, that.flexibility);
}
Spec(int start, int size, Alignment alignment, int flexibility) {
this(new Interval(start, start + size), alignment, flexibility);
}
private Spec copyWriteSpan(Interval span) {
return new Spec(span, alignment, flexibility);
}
private Spec copyWriteAlignment(Alignment alignment) {
return new Spec(span, alignment, flexibility);
}
private Spec copyWriteFlexibility(int flexibility) {
return new Spec(span, alignment, flexibility);
}
/**
* Returns {@code true} if the {@code class}, {@code alignment} and {@code span}
* properties of this Spec and the supplied parameter are pairwise equal,
* {@code false} otherwise.
*
* @param that the object to compare this spec with
*
* @return {@code true} if the specified object is equal to this
* {@code Spec}; {@code false} otherwise
*/
@Override
public boolean equals(Object that) {
if (this == that) {
return true;
}
if (that == null || getClass() != that.getClass()) {
return false;
}
Spec spec = (Spec) that;
if (!alignment.equals(spec.alignment)) {
return false;
}
if (!span.equals(spec.span)) {
return false;
}
return true;
}
@Override
public int hashCode() {
int result = span.hashCode();
result = 31 * result + alignment.hashCode();
return result;
}
}
/**
* Return a Spec, {@code spec}, where:
*
* The {@link LayoutParams} class contains a {@link LayoutParams#rowSpec rowSpec}
* and a {@link LayoutParams#columnSpec columnSpec} each of which contains an
* {@code alignment}. Overall placement of the view in the cell
* group is specified by the two alignments which act along each axis independently.
*
* The GridLayout class defines the most common alignments used in general layout:
* {@link #TOP}, {@link #LEFT}, {@link #BOTTOM}, {@link #RIGHT}, {@link #CENTER}, {@link
* #BASELINE} and {@link #FILL}.
*/
/*
* An Alignment implementation must define {@link #getAlignmentValue(View, int, int)},
* to return the appropriate value for the type of alignment being defined.
* The enclosing algorithms position the children
* so that the locations defined by the alignment values
* are the same for all of the views in a group.
*
*/
public static abstract class Alignment {
private static final Alignment GONE = new Alignment() {
public int getAlignmentValue(View view, int viewSize) {
assert false;
return 0;
}
};
Alignment() {
}
/**
* Returns an alignment value. In the case of vertical alignments the value
* returned should indicate the distance from the top of the view to the
* alignment location.
* For horizontal alignments measurement is made from the left edge of the component.
*
* @param view the view to which this alignment should be applied
* @param viewSize the measured size of the view
* @return the alignment value
*/
abstract int getAlignmentValue(View view, int viewSize);
/**
* Returns the size of the view specified by this alignment.
* In the case of vertical alignments this method should return a height; for
* horizontal alignments this method should return the width.
*
* The default implementation returns {@code viewSize}.
*
* @param view the view to which this alignment should be applied
* @param viewSize the measured size of the view
* @param cellSize the size of the cell into which this view will be placed
* @param measurementType This parameter is currently unused as GridLayout only supports
* one type of measurement: {@link View#measure(int, int)}.
*
* @return the aligned size
*/
int getSizeInCell(View view, int viewSize, int cellSize, int measurementType) {
return viewSize;
}
Bounds getBounds() {
return new Bounds();
}
}
private static final Alignment LEADING = new Alignment() {
public int getAlignmentValue(View view, int viewSize) {
return 0;
}
};
private static final Alignment TRAILING = new Alignment() {
public int getAlignmentValue(View view, int viewSize) {
return viewSize;
}
};
/**
* Indicates that a view should be aligned with the top
* edges of the other views in its cell group.
*/
public static final Alignment TOP = LEADING;
/**
* Indicates that a view should be aligned with the bottom
* edges of the other views in its cell group.
*/
public static final Alignment BOTTOM = TRAILING;
/**
* Indicates that a view should be aligned with the right
* edges of the other views in its cell group.
*/
public static final Alignment RIGHT = TRAILING;
/**
* Indicates that a view should be aligned with the left
* edges of the other views in its cell group.
*/
public static final Alignment LEFT = LEADING;
/**
* 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 CENTER = new Alignment() {
public int getAlignmentValue(View view, int viewSize) {
return viewSize >> 1;
}
};
/**
* 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}.
*
* @see View#getBaseline()
*/
public static final Alignment BASELINE = new Alignment() {
public int getAlignmentValue(View view, int viewSize) {
if (view == null) {
return UNDEFINED;
}
int baseline = view.getBaseline();
return (baseline == -1) ? UNDEFINED : baseline;
}
@Override
public Bounds getBounds() {
return new Bounds() {
/*
In a baseline aligned row in which some components define a baseline
and some don't, we need a third variable to properly account for all
the sizes. This tracks the maximum size of all the components -
including those that don't define a baseline.
*/
private int size;
@Override
protected void reset() {
super.reset();
size = Integer.MIN_VALUE;
}
@Override
protected void include(int before, int after) {
super.include(before, after);
size = max(size, before + after);
}
@Override
protected int size(boolean min) {
return max(super.size(min), size);
}
@Override
protected int getOffset(View c, Alignment alignment, int size) {
return max(0, super.getOffset(c, alignment, size));
}
};
}
};
/**
* 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}.
*/
public static final Alignment FILL = new Alignment() {
public int getAlignmentValue(View view, int viewSize) {
return UNDEFINED;
}
@Override
public int getSizeInCell(View view, int viewSize, int cellSize, int measurementType) {
return cellSize;
}
};
private static boolean canStretch(int flexibility) {
return (flexibility & CAN_STRETCH) != 0;
}
private static boolean isUndefined(int flexibility) {
return (flexibility & UNDEFINED) != 0;
}
/**
* Indicates that a view requests precisely the size specified by its layout parameters.
*
* @see Spec#flexibility
*/
private static final int NONE = 0;
/**
* Indicates that a view's size should lie between its minimum and the size specified by
* its layout parameters.
*
* @see Spec#flexibility
*/
private static final int CAN_SHRINK = 1;
/**
* Indicates that a view's size should be greater than or equal to the size specified by
* its layout parameters.
*
* @see Spec#flexibility
*/
public static final int CAN_STRETCH = 2;
/**
* A default value for flexibility.
*
* @see Spec#flexibility
*/
private static final int UNDEFINED_FLEXIBILITY = UNDEFINED | CAN_SHRINK | CAN_STRETCH;
}
Default values
*
*
*
*
* @attr ref android.R.styleable#GridLayout_Layout_layout_row
* @attr ref android.R.styleable#GridLayout_Layout_layout_rowSpan
* @attr ref android.R.styleable#GridLayout_Layout_layout_rowFlexibility
* @attr ref android.R.styleable#GridLayout_Layout_layout_column
* @attr ref android.R.styleable#GridLayout_Layout_layout_columnSpan
* @attr ref android.R.styleable#GridLayout_Layout_layout_columnFlexibility
* @attr ref android.R.styleable#GridLayout_Layout_layout_gravity
*/
public static class LayoutParams extends MarginLayoutParams {
// Default values
private static final int DEFAULT_WIDTH = WRAP_CONTENT;
private static final int DEFAULT_HEIGHT = WRAP_CONTENT;
private static final int DEFAULT_MARGIN = UNDEFINED;
private static final int DEFAULT_ROW = UNDEFINED;
private static final int DEFAULT_COLUMN = UNDEFINED;
private static final Interval DEFAULT_SPAN = new Interval(UNDEFINED, UNDEFINED + 1);
private static final int DEFAULT_SPAN_SIZE = DEFAULT_SPAN.size();
private static final Alignment DEFAULT_COLUMN_ALIGNMENT = LEFT;
private static final Alignment DEFAULT_ROW_ALIGNMENT = BASELINE;
// Misc
private static final Rect CONTAINER_BOUNDS = new Rect(0, 0, 2, 2);
private static final Alignment[] COLUMN_ALIGNMENTS = { LEFT, CENTER, RIGHT };
private static final Alignment[] ROW_ALIGNMENTS = { TOP, CENTER, BOTTOM };
// TypedArray indices
private static final int MARGIN = R.styleable.ViewGroup_MarginLayout_layout_margin;
private static final int LEFT_MARGIN = R.styleable.ViewGroup_MarginLayout_layout_marginLeft;
private static final int TOP_MARGIN = R.styleable.ViewGroup_MarginLayout_layout_marginTop;
private static final int RIGHT_MARGIN =
R.styleable.ViewGroup_MarginLayout_layout_marginRight;
private static final int BOTTOM_MARGIN =
R.styleable.ViewGroup_MarginLayout_layout_marginBottom;
private static final int COLUMN = R.styleable.GridLayout_Layout_layout_column;
private static final int COLUMN_SPAN = R.styleable.GridLayout_Layout_layout_columnSpan;
private static final int COLUMN_FLEXIBILITY =
R.styleable.GridLayout_Layout_layout_columnFlexibility;
private static final int ROW = R.styleable.GridLayout_Layout_layout_row;
private static final int ROW_SPAN = R.styleable.GridLayout_Layout_layout_rowSpan;
private static final int ROW_FLEXIBILITY =
R.styleable.GridLayout_Layout_layout_rowFlexibility;
private static final int GRAVITY = R.styleable.GridLayout_Layout_layout_gravity;
// Instance variables
/**
* The spec that specifies the vertical characteristics of the cell group
* described by these layout parameters.
*/
public Spec rowSpec;
/**
* The spec that specifies the horizontal characteristics of the cell group
* described by these layout parameters.
*/
public Spec columnSpec;
// Constructors
private LayoutParams(
int width, int height,
int left, int top, int right, int bottom,
Spec rowSpec, Spec columnSpec) {
super(width, height);
setMargins(left, top, right, bottom);
this.rowSpec = rowSpec;
this.columnSpec = columnSpec;
}
/**
* Constructs a new LayoutParams instance for this rowSpec
* and columnSpec
. All other fields are initialized with
* default values as defined in {@link LayoutParams}.
*
* @param rowSpec the rowSpec
* @param columnSpec the columnSpec
*/
public LayoutParams(Spec rowSpec, Spec columnSpec) {
this(DEFAULT_WIDTH, DEFAULT_HEIGHT,
DEFAULT_MARGIN, DEFAULT_MARGIN, DEFAULT_MARGIN, DEFAULT_MARGIN,
rowSpec, columnSpec);
}
/**
* Constructs a new LayoutParams with default values as defined in {@link LayoutParams}.
*/
public LayoutParams() {
this(new Spec(DEFAULT_SPAN, DEFAULT_ROW_ALIGNMENT, Spec.DEFAULT_FLEXIBILITY),
new Spec(DEFAULT_SPAN, DEFAULT_COLUMN_ALIGNMENT, Spec.DEFAULT_FLEXIBILITY));
}
// Copying constructors
/**
* {@inheritDoc}
*/
public LayoutParams(ViewGroup.LayoutParams params) {
super(params);
}
/**
* {@inheritDoc}
*/
public LayoutParams(MarginLayoutParams params) {
super(params);
}
/**
* {@inheritDoc}
*/
public LayoutParams(LayoutParams that) {
super(that);
this.columnSpec = new Spec(that.columnSpec);
this.rowSpec = new Spec(that.rowSpec);
}
// AttributeSet constructors
private LayoutParams(Context context, AttributeSet attrs, int defaultGravity) {
super(context, attrs);
reInitSuper(context, attrs);
init(context, attrs, defaultGravity);
}
/**
* {@inheritDoc}
*
* Values not defined in the attribute set take the default values
* defined in {@link LayoutParams}.
*/
public LayoutParams(Context context, AttributeSet attrs) {
this(context, attrs, Gravity.NO_GRAVITY);
}
// Implementation
private static boolean definesVertical(int gravity) {
return gravity > 0 && (gravity & Gravity.VERTICAL_GRAVITY_MASK) != 0;
}
private static boolean definesHorizontal(int gravity) {
return gravity > 0 && (gravity & Gravity.HORIZONTAL_GRAVITY_MASK) != 0;
}
private static
*
*
* @param min the minimum value.
* @param max the maximum value.
*/
public Interval(int min, int max) {
this.min = min;
this.max = max;
}
private int size() {
return max - min;
}
private Interval inverse() {
return new Interval(max, min);
}
/**
* Returns {@code true} if the {@link #getClass class},
* {@link #min} and {@link #max} properties of this Interval and the
* supplied parameter are pairwise equal; {@code false} otherwise.
*
* @param that the object to compare this interval with
*
* @return {@code true} if the specified object is equal to this
* {@code Interval}, {@code false} otherwise.
*/
@Override
public boolean equals(Object that) {
if (this == that) {
return true;
}
if (that == null || getClass() != that.getClass()) {
return false;
}
Interval interval = (Interval) that;
if (max != interval.max) {
return false;
}
if (min != interval.min) {
return false;
}
return true;
}
@Override
public int hashCode() {
int result = min;
result = 31 * result + max;
return result;
}
@Override
public String toString() {
return "[" + min + ", " + max + "]";
}
}
/**
* A spec defines either the horizontal or vertical characteristics of a group of
* cells.
*/
public static class Spec {
private static final int DEFAULT_FLEXIBILITY = UNDEFINED_FLEXIBILITY;
private static final Spec GONE = new Spec(Interval.GONE, Alignment.GONE);
/**
* The grid indices of the leading and trailing edges of this cell group for the
* appropriate axis.
*
*
*
* @param start the start
* @param size the size
* @param alignment the alignment
* @param flexibility the flexibility
*/
public static Spec spec(int start, int size, Alignment alignment, int flexibility) {
return new Spec(start, size, alignment, flexibility);
}
/**
* Return a Spec, {@code spec}, where:
*
*
*
* @param start the start
* @param alignment the alignment
* @param flexibility the flexibility
*/
public static Spec spec(int start, Alignment alignment, int flexibility) {
return spec(start, 1, alignment, flexibility);
}
/**
* Return a Spec, {@code spec}, where:
*
*
*
* @param start the start
* @param size the size
* @param alignment the alignment
*/
public static Spec spec(int start, int size, Alignment alignment) {
return spec(start, size, alignment, Spec.DEFAULT_FLEXIBILITY);
}
/**
* Return a Spec, {@code spec}, where:
*
*
*
* @param start the start index
* @param alignment the alignment
*/
public static Spec spec(int start, Alignment alignment) {
return spec(start, 1, alignment);
}
/**
* Alignments specify where a view should be placed within a cell group and
* what size it should be.
*