diff options
21 files changed, 3003 insertions, 0 deletions
diff --git a/api/current.txt b/api/current.txt index 2f5b9b4..fdef20f 100644 --- a/api/current.txt +++ b/api/current.txt @@ -301,7 +301,9 @@ package android { field public static final int colorBackgroundCacheHint = 16843435; // 0x10102ab field public static final int colorForeground = 16842800; // 0x1010030 field public static final int colorForegroundInverse = 16843270; // 0x1010206 + field public static final int columnCount = 16843633; // 0x1010371 field public static final int columnDelay = 16843215; // 0x10101cf + field public static final int columnOrderPreserved = 16843634; // 0x1010372 field public static final int columnWidth = 16843031; // 0x1010117 field public static final int completionHint = 16843122; // 0x1010172 field public static final int completionHintView = 16843123; // 0x1010173 @@ -575,6 +577,8 @@ package android { field public static final int layout_centerInParent = 16843151; // 0x101018f field public static final int layout_centerVertical = 16843153; // 0x1010191 field public static final int layout_column = 16843084; // 0x101014c + field public static final int layout_columnSpan = 16843639; // 0x1010377 + field public static final int layout_columnWeight = 16843640; // 0x1010378 field public static final int layout_gravity = 16842931; // 0x10100b3 field public static final int layout_height = 16842997; // 0x10100f5 field public static final int layout_margin = 16842998; // 0x10100f6 @@ -582,6 +586,9 @@ package android { field public static final int layout_marginLeft = 16842999; // 0x10100f7 field public static final int layout_marginRight = 16843001; // 0x10100f9 field public static final int layout_marginTop = 16843000; // 0x10100f8 + field public static final int layout_row = 16843636; // 0x1010374 + field public static final int layout_rowSpan = 16843637; // 0x1010375 + field public static final int layout_rowWeight = 16843638; // 0x1010376 field public static final int layout_scale = 16843155; // 0x1010193 field public static final int layout_span = 16843085; // 0x101014d field public static final int layout_toLeftOf = 16843138; // 0x1010182 @@ -751,9 +758,11 @@ package android { field public static final int rotation = 16843558; // 0x1010326 field public static final int rotationX = 16843559; // 0x1010327 field public static final int rotationY = 16843560; // 0x1010328 + field public static final int rowCount = 16843631; // 0x101036f field public static final int rowDelay = 16843216; // 0x10101d0 field public static final int rowEdgeFlags = 16843329; // 0x1010241 field public static final int rowHeight = 16843058; // 0x1010132 + field public static final int rowOrderPreserved = 16843632; // 0x1010370 field public static final int saveEnabled = 16842983; // 0x10100e7 field public static final int scaleGravity = 16843262; // 0x10101fe field public static final int scaleHeight = 16843261; // 0x10101fd @@ -984,6 +993,7 @@ package android { field public static final int unfocusedMonthDateColor = 16843588; // 0x1010344 field public static final int unselectedAlpha = 16843278; // 0x101020e field public static final int updatePeriodMillis = 16843344; // 0x1010250 + field public static final int useDefaultMargins = 16843635; // 0x1010373 field public static final int useIntrinsicSizeAsMinimum = 16843536; // 0x1010310 field public static final int useLevel = 16843167; // 0x101019f field public static final int userVisible = 16843409; // 0x1010291 @@ -24167,6 +24177,67 @@ package android.widget { ctor public Gallery.LayoutParams(android.view.ViewGroup.LayoutParams); } + public class GridLayout extends android.view.ViewGroup { + ctor public GridLayout(android.content.Context); + ctor public GridLayout(android.content.Context, android.util.AttributeSet, int); + ctor public GridLayout(android.content.Context, android.util.AttributeSet); + method public int getColumnCount(); + method public int getOrientation(); + method public int getRowCount(); + method public boolean getUseDefaultMargins(); + method public boolean isColumnOrderPreserved(); + method public boolean isRowOrderPreserved(); + method protected void onLayout(boolean, int, int, int, int); + method public void setColumnCount(int); + method public void setColumnOrderPreserved(boolean); + method public void setOrientation(int); + method public void setRowCount(int); + method public void setRowOrderPreserved(boolean); + method public void setUseDefaultMargins(boolean); + field public static final android.widget.GridLayout.Alignment BASELINE; + field public static final android.widget.GridLayout.Alignment BOTTOM; + field public static final android.widget.GridLayout.Alignment CENTER; + field public static final android.widget.GridLayout.Alignment FILL; + field public static final int HORIZONTAL = 0; // 0x0 + field public static final android.widget.GridLayout.Alignment LEFT; + field public static final android.widget.GridLayout.Alignment RIGHT; + field public static final android.widget.GridLayout.Alignment TOP; + field public static final int VERTICAL = 1; // 0x1 + } + + public static abstract interface GridLayout.Alignment { + method public abstract int getAlignmentValue(android.view.View, int); + method public abstract int getSizeInCell(android.view.View, int, int); + } + + public static class GridLayout.Group { + ctor public GridLayout.Group(android.widget.GridLayout.Interval, android.widget.GridLayout.Alignment); + ctor public GridLayout.Group(int, int, android.widget.GridLayout.Alignment); + ctor public GridLayout.Group(int, android.widget.GridLayout.Alignment); + field public final android.widget.GridLayout.Alignment alignment; + field public final android.widget.GridLayout.Interval span; + } + + public static class GridLayout.Interval { + ctor public GridLayout.Interval(int, int); + field public final int max; + field public final int min; + } + + public static class GridLayout.LayoutParams extends android.view.ViewGroup.MarginLayoutParams { + ctor public GridLayout.LayoutParams(android.widget.GridLayout.Group, android.widget.GridLayout.Group); + ctor public GridLayout.LayoutParams(); + ctor public GridLayout.LayoutParams(android.view.ViewGroup.LayoutParams); + ctor public GridLayout.LayoutParams(android.view.ViewGroup.MarginLayoutParams); + ctor public GridLayout.LayoutParams(android.widget.GridLayout.LayoutParams); + ctor public GridLayout.LayoutParams(android.content.Context, android.util.AttributeSet); + method public void setGravity(int); + field public android.widget.GridLayout.Group columnGroup; + field public float columnWeight; + field public android.widget.GridLayout.Group rowGroup; + field public float rowWeight; + } + public class GridView extends android.widget.AbsListView { ctor public GridView(android.content.Context); ctor public GridView(android.content.Context, android.util.AttributeSet); @@ -25049,6 +25120,12 @@ package android.widget { method public abstract void onScrollStarted(); } + public final class Space extends android.view.View { + ctor public Space(android.content.Context, android.util.AttributeSet, int); + ctor public Space(android.content.Context, android.util.AttributeSet); + ctor public Space(android.content.Context); + } + public class Spinner extends android.widget.AbsSpinner implements android.content.DialogInterface.OnClickListener { ctor public Spinner(android.content.Context); ctor public Spinner(android.content.Context, int); diff --git a/core/java/android/widget/GridLayout.java b/core/java/android/widget/GridLayout.java new file mode 100644 index 0000000..4889101 --- /dev/null +++ b/core/java/android/widget/GridLayout.java @@ -0,0 +1,2036 @@ +/* + * 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.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import com.android.internal.R.styleable; + +import java.lang.reflect.Array; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +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 <em>grid</em>. + * <p> + * The grid is composed of a set of infinitely thin lines that separate the + * viewing area into <em>cells</em>. Throughout the API, grid lines are referenced + * by grid <em>indices</em>. A grid that has <code>N</code> columns + * has <code>N + 1</code> grid indices that run from <code>0</code> + * through <code>N</code> inclusive. Regardless of how GridLayout is + * configured, grid index <code>0</code> is fixed to the leading edge of the + * container and grid index <code>N</code> is fixed to its trailing edge + * (after padding is taken into account). + * + * <h4>Row and Column Groups</h4> + * + * Children occupy one or more contiguous cells, as defined + * by their {@link GridLayout.LayoutParams#rowGroup rowGroup} and + * {@link GridLayout.LayoutParams#columnGroup columnGroup} layout parameters. + * Each group specifies 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. + * + * <h4>Default Cell Assignment</h4> + * + * If no child specifies 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. + * + * <h4>Space</h4> + * + * 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 child's visual characteristics. Each of the + * margins so defined may be independently overridden by an assignment + * to the appropriate layout parameter. + * + * <h4>Excess Space Distribution</h4> + * + * Like {@link LinearLayout}, a child's ability to stretch is controlled + * using <em>weights</em>, which are specified using the + * {@link GridLayout.LayoutParams#rowWeight rowWeight} and + * {@link GridLayout.LayoutParams#columnWeight columnWeight} layout parameters. + * <p> + * <p> + * 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; + + // Misc constants + + private static final String TAG = GridLayout.class.getName(); + private static final boolean DEBUG = false; + private static final int UNDEFINED = Integer.MIN_VALUE; + private static final Paint GRID_PAINT = new Paint(); + private static final double GOLDEN_RATIO = (1 + Math.sqrt(5)) / 2; + private static final int MIN = 0; + private static final int PRF = 1; + private static final int MAX = 2; + + // 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; + + // TypedArray indices + + private static final int ORIENTATION = styleable.GridLayout_orientation; + private static final int ROW_COUNT = styleable.GridLayout_rowCount; + private static final int COLUMN_COUNT = styleable.GridLayout_columnCount; + private static final int USE_DEFAULT_MARGINS = styleable.GridLayout_useDefaultMargins; + private static final int ROW_ORDER_PRESERVED = styleable.GridLayout_rowOrderPreserved; + private static final int COLUMN_ORDER_PRESERVED = 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 mDefaultGravity = Gravity.NO_GRAVITY; + boolean maximizing = false; + boolean accommodateBothMinAndMax = false; + + // Constructors + + /** + * {@inheritDoc} + */ + public GridLayout(Context context) { + super(context); + if (DEBUG) { + setWillNotDraw(false); + } + } + + /** + * {@inheritDoc} + */ + public GridLayout(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + processAttributes(context, attrs); + } + + /** + * {@inheritDoc} + */ + public GridLayout(Context context, AttributeSet attrs) { + super(context, attrs); + processAttributes(context, attrs); + } + + private void processAttributes(Context context, AttributeSet attrs) { + TypedArray a = context.obtainStyledAttributes(attrs, styleable.GridLayout); + try { + setRowCount(a.getInteger(ROW_COUNT, DEFAULT_COUNT)); + setColumnCount(a.getInteger(COLUMN_COUNT, DEFAULT_COUNT)); + mOrientation = a.getInteger(ORIENTATION, DEFAULT_ORIENTATION); + mUseDefaultMargins = a.getBoolean(USE_DEFAULT_MARGINS, DEFAULT_USE_DEFAULT_MARGINS); + setRowOrderPreserved(a.getBoolean(ROW_ORDER_PRESERVED, DEFAULT_ORDER_PRESERVED)); + setColumnOrderPreserved(a.getBoolean(COLUMN_ORDER_PRESERVED, DEFAULT_ORDER_PRESERVED)); + } finally { + a.recycle(); + } + } + + // Implementation + + /** + * Returns the current orientation. + * + * @return either {@link #HORIZONTAL} or {@link #VERTICAL}. The default + * is {@link #HORIZONTAL}. + * + * @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. + * + * @param orientation the 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#rowGroup}. + * + * @return the current number of rows + * + * @see #setRowCount(int) + * @see LayoutParams#rowGroup + * + * @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#rowGroup + * + * @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#columnGroup}. + * + * @return the current number of columns + * + * @see #setColumnCount(int) + * @see LayoutParams#columnGroup + * + * @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#columnGroup + * + * @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 true if default margins should be allocated. + * + * @see #setUseDefaultMargins(boolean) + * + * @attr ref android.R.styleable#GridLayout_useDefaultMargins + */ + public boolean getUseDefaultMargins() { + return mUseDefaultMargins; + } + + /** + * When 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. + * <p> + * When false, the default value of all margins is zero. + * + * @param useDefaultMargins use true to make GridLayout allocate default margins + * + * @see #getUseDefaultMargins() + * + * @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; + } + + /** + * Returns whether or not row boundaries are ordered by their grid indices. + * + * @return true if row boundaries must appear in the order of their indices, false otherwise. + * The default is false. + * + * @see #setRowOrderPreserved(boolean) + * + * @attr ref android.R.styleable#GridLayout_rowOrderPreserved + */ + public boolean isRowOrderPreserved() { + return mVerticalAxis.isOrderPreserved(); + } + + /** + * When this property is <code>false</code>, the default state, GridLayout + * is at liberty to choose an order that better suits the heights of its children. + <p> + * When this property is <code>true</code>, GridLayout is forced to place row boundaries + * (the {@link Interval#min min} and {@link Interval#max max} values of + * a {@link LayoutParams#rowGroup rowGroup}'s {@link Group#span span}) + * so that they appear in ascending order in the view. + * <p> + * 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</code>, constraints are added for each pair of consecutive + * indices: i.e. between row boundaries: <code>[0..1], [1..2], [3..4],...</code> etc. + * + * When the property is <code>false</code>, the ordering constraints are placed + * only between boundaries that separate opposing edges of the layout's children. + * + * @param rowOrderPreserved use 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); + } + + /** + * Returns whether or not column boundaries are ordered by their grid indices. + * + * @return true if column boundaries must appear in the order of their indices, false otherwise. + * The default is false. + * + * @see #setColumnOrderPreserved(boolean) + * + * @attr ref android.R.styleable#GridLayout_columnOrderPreserved + */ + public boolean isColumnOrderPreserved() { + return mHorizontalAxis.isOrderPreserved(); + } + + /** + * When this property is <code>false</code>, the default state, GridLayout + * is at liberty to choose an order that better suits the widths of its children. + <p> + * When this property is <code>true</code>, GridLayout is forced to place column boundaries + * (the {@link Interval#min min} and {@link Interval#max max} values of + * a {@link LayoutParams#columnGroup columnGroup}'s {@link Group#span span}) + * so that they appear in ascending order in the view. + * <p> + * 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</code>, constraints are added for each pair of consecutive + * indices: i.e. between column boundaries: <code>[0..1], [1..2], [3..4],...</code> etc. + * + * When the property is <code>false</code>, the ordering constraints are placed + * only between boundaries that separate opposing edges of the layout's children. + * + * @param columnOrderPreserved use 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); + } + + private static int compare(int i, int j) { + return i < j ? -1 : i > j ? 1 : 0; + } + + private static int sum(int[] a) { + int result = 0; + for (int i = 0, length = a.length; i < length; i++) { + result += a[i]; + } + return result; + } + + private int getDefaultMargin(View c, boolean leading, boolean horizontal) { + // In the absence of any other information, calculate a default gap such + // that, in a grid of identical components, the heights and the vertical + // gaps are in the proportion of the golden ratio. + // To effect this with equal margins at each edge, set each of the + // four margin values to half this amount. + c.measure(0, 0); + return (int) (c.getMeasuredHeight() / GOLDEN_RATIO / 2); + } + + private int getDefaultMargin(View c, boolean isAtEdge, boolean leading, boolean horizontal) { + // todo remove 20 - use padding here? + return isAtEdge ? 20 : getDefaultMargin(c, leading, horizontal); + } + + private int getDefaultMarginValue(View c, LayoutParams p, boolean leading, boolean horizontal) { + if (!mUseDefaultMargins) { + return 0; + } + Group group = horizontal ? p.columnGroup : p.rowGroup; + Axis axis = horizontal ? mHorizontalAxis : mVerticalAxis; + Interval span = group.span; + boolean isAtEdge = leading ? span.min == 0 : span.max == axis.getCount(); + + return getDefaultMargin(c, isAtEdge, leading, horizontal); + } + + private int getMargin(View view, boolean leading, boolean horizontal) { + LayoutParams lp = getLayoutParams(view); + int margin = horizontal ? + leading ? lp.leftMargin : lp.rightMargin : + leading ? lp.topMargin : lp.bottomMargin; + return margin == UNDEFINED ? getDefaultMarginValue(view, lp, leading, horizontal) : margin; + } + + private static boolean isUndefined(Interval span) { + return span.min == UNDEFINED || span.max == UNDEFINED; + } + + private void validateLayoutParams() { + // install default indices for cells if *none* are defined + if (mHorizontalAxis.maxIndex1() == UNDEFINED || mVerticalAxis.maxIndex1() == UNDEFINED) { + boolean horizontal = mOrientation == HORIZONTAL; + int count = horizontal ? mHorizontalAxis.count : mVerticalAxis.count; + if (count == UNDEFINED) { + count = Integer.MAX_VALUE; + } + int x = 0; + int y = 0; + int maxSize = 0; + for (int i = 0, size = getChildCount(); i < size; i++) { + LayoutParams lp = getLayoutParams1(getChildAt(i)); + + Interval hSpan = lp.columnGroup.span; + int cellWidth = hSpan.size(); + + Interval vSpan = lp.rowGroup.span; + int cellHeight = vSpan.size(); + + if (horizontal) { + if (x + cellWidth > count) { + x = 0; + y += maxSize; + maxSize = 0; + } + } else { + if (y + cellHeight > count) { + y = 0; + x += maxSize; + maxSize = 0; + } + } + lp.setHorizontalGroupSpan(new Interval(x, x + cellWidth)); + lp.setVerticalGroupSpan(new Interval(y, y + cellHeight)); + + if (horizontal) { + x = x + cellWidth; + } else { + y = y + cellHeight; + } + maxSize = max(maxSize, horizontal ? cellHeight : cellWidth); + } + } else { + /* + At least one row and one column index have been defined. + Assume missing row/cols are in error and set them to zero so that + they will display top/left and the developer can add the right indices. + Without this UNDEFINED would cause ArrayIndexOutOfBoundsException. + */ + for (int i = 0, size = getChildCount(); i < size; i++) { + LayoutParams lp = getLayoutParams1(getChildAt(i)); + if (isUndefined(lp.columnGroup.span)) { + lp.setHorizontalGroupSpan(LayoutParams.DEFAULT_SPAN); + } + if (isUndefined(lp.rowGroup.span)) { + lp.setVerticalGroupSpan(LayoutParams.DEFAULT_SPAN); + } + } + } + } + + private void invalidateStructure() { + mLayoutParamsValid = false; + mHorizontalAxis.invalidateStructure(); + mVerticalAxis.invalidateStructure(); + + // This can end up being done twice. But better that than not at all. + invalidateValues(); + } + + private void invalidateValues() { + mHorizontalAxis.invalidateValues(); + mVerticalAxis.invalidateValues(); + } + + private LayoutParams getLayoutParams1(View c) { + return (LayoutParams) c.getLayoutParams(); + } + + private LayoutParams getLayoutParams(View c) { + if (!mLayoutParamsValid) { + validateLayoutParams(); + mLayoutParamsValid = true; + } + return getLayoutParams1(c); + } + + @Override + protected LayoutParams generateDefaultLayoutParams() { + return new LayoutParams(); + } + + @Override + public LayoutParams generateLayoutParams(AttributeSet attrs) { + return new LayoutParams(getContext(), attrs, mDefaultGravity); + } + + @Override + protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { + return new LayoutParams(p); + } + + // Draw grid + + private void drawLine(Canvas graphics, int x1, int y1, int x2, int y2, Paint paint) { + int dx = getPaddingLeft(); + int dy = getPaddingTop(); + graphics.drawLine(dx + x1, dy + y1, dx + x2, dy + y2, paint); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + + if (DEBUG) { + int height = getHeight() - getPaddingTop() - getPaddingBottom(); + int width = getWidth() - getPaddingLeft() - getPaddingRight(); + + int[] xs = mHorizontalAxis.locations; + for (int i = 0, length = xs.length; i < length; i++) { + int x = xs[i]; + drawLine(canvas, x, 0, x, height - 1, GRID_PAINT); + } + int[] ys = mVerticalAxis.locations; + for (int i = 0, length = ys.length; i < length; i++) { + int y = ys[i]; + drawLine(canvas, 0, y, width - 1, y, GRID_PAINT); + } + } + } + + static { + GRID_PAINT.setColor(Color.argb(50, 255, 255, 255)); + } + + // Add/remove + + @Override + public void addView(View child, int index, ViewGroup.LayoutParams params) { + super.addView(child, index, params); + invalidateStructure(); + } + + @Override + public void removeView(View view) { + super.removeView(view); + invalidateStructure(); + } + + @Override + public void removeViewInLayout(View view) { + super.removeViewInLayout(view); + invalidateStructure(); + } + + @Override + public void removeViewsInLayout(int start, int count) { + super.removeViewsInLayout(start, count); + invalidateStructure(); + } + + @Override + public void removeViewAt(int index) { + super.removeViewAt(index); + invalidateStructure(); + } + + // Measurement + + @Override + protected void onMeasure(int widthSpec, int heightSpec) { + invalidateValues(); + // int width = MeasureSpec.getSize(widthSpec); + // int widthMode = MeasureSpec.getMode(widthSpec); + // int height = MeasureSpec.getSize(heightSpec); + // int heightMode = MeasureSpec.getMode(heightSpec); + + // todo - handle widthSpec and heightSpec properly + + int computedWidth = getPaddingLeft() + mHorizontalAxis.getPref() + getPaddingRight(); + int computedHeight = getPaddingTop() + mVerticalAxis.getPref() + getPaddingBottom(); + + setMeasuredDimension( + resolveSizeAndState(computedWidth, widthSpec, 0), + resolveSizeAndState(computedHeight, heightSpec, 0)); + } + + private int protect(int alignment) { + return alignment == UNDEFINED ? 0 : alignment; + } + + private int getLocationIncludingMargin(Axis state, int index, boolean leading) { + int margin = leading ? state.leadingMargins[index] : -state.trailingMargins[index]; + return state.locations[index] + margin; + } + + private int getMeasurement(View c, boolean horizontal, int measurementType) { + LayoutParams lp = (LayoutParams) c.getLayoutParams(); + // First check to see if the user has specified the size. + // If so, return the specified size. + int size = horizontal ? lp.width : lp.height; + if (size >= 0) { + return size; + } + + // measureChild(c, 0, 0); + c.measure(0, 0);// todo work out correct order of events for measurement calls + int result = horizontal ? c.getMeasuredWidth() : c.getMeasuredHeight(); + + float weight = horizontal ? lp.columnWeight : lp.rowWeight; + Axis axis = horizontal ? mHorizontalAxis : mVerticalAxis; + if (weight != 0) { + return result + axis.prefSizeOfWeightedComponent; + } + return result; + } + + // Layout container + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + invalidateValues(); + + int targetWidth = r - l; + int targetHeight = b - t; + + int paddingLeft = getPaddingLeft(); + int paddingTop = getPaddingTop(); + int paddingRight = getPaddingRight(); + int paddingBottom = getPaddingBottom(); + + mHorizontalAxis.layout(targetWidth - paddingLeft - paddingRight); + mVerticalAxis.layout(targetHeight - paddingTop - paddingBottom); + + for (int i = 0, size = getChildCount(); i < size; i++) { + View view = getChildAt(i); + LayoutParams constraints = getLayoutParams(view); + Interval hRange = constraints.columnGroup.span; + Interval vRange = constraints.rowGroup.span; + + int x1 = getLocationIncludingMargin(mHorizontalAxis, hRange.min, true); + int y1 = getLocationIncludingMargin(mVerticalAxis, vRange.min, true); + + int x2 = getLocationIncludingMargin(mHorizontalAxis, hRange.max, false); + int y2 = getLocationIncludingMargin(mVerticalAxis, vRange.max, false); + + int cellWidth = x2 - x1; + int cellHeight = y2 - y1; + + Bounds minMaxX = mHorizontalAxis.getGroupBounds().getValue(i); + Bounds minMaxY = mVerticalAxis.getGroupBounds().getValue(i); + + int pWidth = getMeasurement(view, true, PRF); + int pHeight = getMeasurement(view, false, PRF); + + Alignment hAlignment = constraints.columnGroup.alignment; + Alignment vAlignment = constraints.rowGroup.alignment; + + int ddx = protect(hAlignment.getAlignmentValue(null, cellWidth - minMaxX.size())); + int ddy = protect(vAlignment.getAlignmentValue(null, cellHeight - minMaxY.size())); + + int dx = ddx + -minMaxX.below - hAlignment.getAlignmentValue(view, pWidth); + int dy = ddy + -minMaxY.below - vAlignment.getAlignmentValue(view, pHeight); + + int width = hAlignment.getSizeInCell(view, pWidth, cellWidth); + int height = vAlignment.getSizeInCell(view, pHeight, cellHeight); + + int cx = paddingLeft + x1 + dx; + int cy = paddingTop + y1 + dy; + view.layout(cx, cy, cx + width, cy + height); + } + } + + // Inner classes + + private class Axis { + private static final int MIN_VALUE = -1000000; + private static final int MAX_VALUE = 1000000; + + private static final int UNVISITED = 0; + private static final int PENDING = 1; + private static final int COMPLETE = 2; + + public final boolean horizontal; + + public int count = UNDEFINED; + public boolean countValid = false; + public boolean countWasExplicitySet = false; + + PackedMap<Group, Bounds> groupBounds; + public boolean groupBoundsValid = false; + + PackedMap<Interval, Int> spanSizes; + public boolean spanSizesValid = false; + + public int[] locations; + + public int[] leadingMargins; + public int[] trailingMargins; + + public Arc[] arcs; + public boolean arcsValid = false; + + private boolean mOrderPreserved = DEFAULT_ORDER_PRESERVED; + + public int prefSizeOfWeightedComponent; + + private Axis(boolean horizontal) { + this.horizontal = horizontal; + } + + private int maxIndex(boolean internal) { + // note the number Integer.MIN_VALUE + 1 comes up in undefined cells + int count = -1; + for (int i = 0, size = getChildCount(); i < size; i++) { + LayoutParams params = internal ? + getLayoutParams1(getChildAt(i)) : + getLayoutParams(getChildAt(i)); + Group g = horizontal ? params.columnGroup : params.rowGroup; + count = max(count, g.span.min); + count = max(count, g.span.max); + } + return count == -1 ? UNDEFINED : count; + } + + private int maxIndex1() { + return maxIndex(true); + } + + public int getCount() { + if (!countWasExplicitySet && !countValid) { + count = max(0, maxIndex(false)); // if there are no cells, the count is zero + countValid = true; + } + return count; + } + + public void setCount(int count) { + this.count = count; + this.countWasExplicitySet = count != UNDEFINED; + } + + public boolean isOrderPreserved() { + return mOrderPreserved; + } + + public void setOrderPreserved(boolean orderPreserved) { + mOrderPreserved = orderPreserved; + invalidateStructure(); + } + + private PackedMap<Group, Bounds> createGroupBounds() { + int N = getChildCount(); + Group[] groups = new Group[N]; + Bounds[] bounds = new Bounds[N]; + for (int i = 0; i < N; i++) { + LayoutParams lp = getLayoutParams(getChildAt(i)); + Group group = horizontal ? lp.columnGroup : lp.rowGroup; + + groups[i] = group; + bounds[i] = new Bounds(); + } + + return new PackedMap<Group, Bounds>(groups, bounds); + } + + private void computeGroupBounds() { + for (int i = 0; i < groupBounds.values.length; i++) { + groupBounds.values[i].reset(); + } + for (int i = 0, size = getChildCount(); i < size; i++) { + View c = getChildAt(i); + LayoutParams lp = getLayoutParams(c); + Group g = horizontal ? lp.columnGroup : lp.rowGroup; + + Bounds bounds = groupBounds.getValue(i); + int dim = getMeasurement(c, horizontal, PRF); + // todo test this works correctly when the returned value is UNDEFINED + int below = g.alignment.getAlignmentValue(c, dim); + int above = dim - below; + bounds.include(-below, above); + } + } + + private PackedMap<Group, Bounds> getGroupBounds() { + if (groupBounds == null) { + groupBounds = createGroupBounds(); + } + if (!groupBoundsValid) { + computeGroupBounds(); + groupBoundsValid = true; + } + return groupBounds; + } + + // Add values computed by alignment - taking the max of all alignments in each span + private PackedMap<Interval, Int> createSpanSizes() { + PackedMap<Group, Bounds> groupBounds = getGroupBounds(); + int N = groupBounds.keys.length; + Interval[] spans = new Interval[N]; + Int[] values = new Int[N]; + for (int i = 0; i < N; i++) { + Interval key = groupBounds.keys[i].span; + + spans[i] = key; + values[i] = new Int(); + } + return new PackedMap<Interval, Int>(spans, values); + } + + private void computeSpanSizes() { + Int[] spans = spanSizes.values; + for (int i = 0; i < spans.length; i++) { + spans[i].reset(); + } + + Bounds[] bounds = getGroupBounds().values; // us get to trigger a re-evaluation + for (int i = 0; i < bounds.length; i++) { + int value = bounds[i].size(); + + Int valueHolder = spanSizes.getValue(i); + valueHolder.value = max(valueHolder.value, value); + } + } + + private PackedMap<Interval, Int> getSpanSizes() { + if (spanSizes == null) { + spanSizes = createSpanSizes(); + } + if (!spanSizesValid) { + computeSpanSizes(); + spanSizesValid = true; + } + return spanSizes; + } + + private void include(List<Arc> arcs, Interval key, Int size, boolean maximizing) { + key = maximizing ? key.inverse() : key; + size = maximizing ? size.neg() : size; + // this bit below should really be computed outside here - + // its just to stop default (col>0) constraints obliterating valid entries + for (Arc arc : arcs) { + Interval span = arc.span; + if (span.equals(key)) { + return; + } + } + arcs.add(new Arc(key, size)); + } + + private void include2(List<Arc> arcs, Interval span, Int min, Int max, + boolean both, boolean maximizing) { + include(arcs, span, min, maximizing); + if (both) { + include(arcs, span.inverse(), max.neg(), maximizing); + } + } + + private void include2(List<Arc> arcs, Interval span, int min, int max, + boolean both, boolean maximizing) { + include2(arcs, span, new Int(min), new Int(max), both, maximizing); + } + + // Group arcs by their first index, returning an array of arrays. + // This is linear in the number of arcs. + private Arc[][] index(Arc[] arcs) { + int N = getCount() + 1;// the number of vertices + Arc[][] result = new Arc[N][]; + int[] sizes = new int[N]; + for (Arc arc : arcs) { + sizes[arc.span.min]++; + } + for (int i = 0; i < sizes.length; i++) { + result[i] = new Arc[sizes[i]]; + } + // reuse the sizes array to hold the current last elements as we insert each arc + Arrays.fill(sizes, 0); + for (Arc arc : arcs) { + int i = arc.span.min; + result[i][sizes[i]++] = arc; + } + + return result; + } + + // todo do we always add first element? + private Arc[] sort(final Arc[] arcs, int start) { + final List<Arc> result = new ArrayList<Arc>(); + new Object() { + Arc[][] index = index(arcs); + int[] visited = new int[getCount() + 1]; + + boolean completesCycle(int loc) { + int state = visited[loc]; + if (state == UNVISITED) { + visited[loc] = PENDING; + for (Arc arc : index[loc]) { + Interval span = arc.span; + // the recursive call + if (completesCycle(span.max)) { + // which arcs get set here is dependent on the order + // in which we explore nodes + arc.completesCycle = true; + } + result.add(arc); + } + visited[loc] = COMPLETE; + } else if (state == PENDING) { + return true; + } else if (state == COMPLETE) { + } + return false; + } + }.completesCycle(start); + Collections.reverse(result); + assert arcs.length == result.size(); + return result.toArray(new Arc[result.size()]); + } + + private boolean[] findUsed(Collection<Arc> arcs) { + boolean[] result = new boolean[getCount()]; + for (Arc arc : arcs) { + Interval span = arc.span; + int min = min(span.min, span.max); + int max = max(span.min, span.max); + for (int i = min; i < max; i++) { + result[i] = true; + } + } + return result; + } + + // todo unify with findUsed above + private Collection<Interval> getSpacers() { + List<Interval> result = new ArrayList<Interval>(); + int N = getCount() + 1; + int[] leadingEdgeCount = new int[N]; + int[] trailingEdgeCount = new int[N]; + for (int i = 0, size = getChildCount(); i < size; i++) { + LayoutParams lp = getLayoutParams(getChildAt(i)); + Group g = horizontal ? lp.columnGroup : lp.rowGroup; + Interval span = g.span; + leadingEdgeCount[span.min]++; + trailingEdgeCount[span.max]++; + } + + int lastTrailingEdge = 0; + + // treat the parent's edges like peer edges of the opposite type + trailingEdgeCount[0] = 1; + leadingEdgeCount[N - 1] = 1; + + for (int i = 0; i < N; i++) { + if (trailingEdgeCount[i] > 0) { + lastTrailingEdge = i; + continue; // if this is also a leading edge, don't add a space of length zero + } + if (leadingEdgeCount[i] > 0) { + result.add(new Interval(lastTrailingEdge, i)); + } + } + return result; + } + + private Arc[] createArcs(boolean maximizing) { + List<Arc> spanToSize = new ArrayList<Arc>(); + + // Add all the preferred elements that were not defined by the user. + PackedMap<Interval, Int> spanSizes = getSpanSizes(); + for (int i = 0; i < spanSizes.keys.length; i++) { + Interval key = spanSizes.keys[i]; + Int value = spanSizes.values[i]; + // todo remove value duplicate + include2(spanToSize, key, value, value, accommodateBothMinAndMax, maximizing); + } + + // Find redundant rows/cols and glue them together with 0-length arcs to link the tree + boolean[] used = findUsed(spanToSize); + for (int i = 0; i < getCount(); i++) { + if (!used[i]) { + Interval span = new Interval(i, i + 1); + include(spanToSize, span, new Int(0), maximizing); + include(spanToSize, span.inverse(), new Int(0), maximizing); + } + } + + if (mOrderPreserved) { + // Add preferred gaps + for (int i = 0; i < getCount(); i++) { + if (used[i]) { + include2(spanToSize, new Interval(i, i + 1), 0, 0, false, maximizing); + } + } + } else { + for (Interval gap : getSpacers()) { + include2(spanToSize, gap, 0, 0, false, maximizing); + } + } + Arc[] arcs = spanToSize.toArray(new Arc[spanToSize.size()]); + return sort(arcs, maximizing ? getCount() : 0); + } + + public Arc[] getArcs(boolean maximizing) { + if (arcs == null) { + arcs = createArcs(maximizing); + } + if (!arcsValid) { + getSpanSizes(); + arcsValid = true; + } + return arcs; + } + + private boolean relax(int[] locations, Arc entry, boolean maximizing) { + Interval span = entry.span; + int u = span.min; + int v = span.max; + int value = entry.value.value; + int candidate = locations[u] + value; + if (maximizing ? candidate < locations[v] : candidate > locations[v]) { + locations[v] = candidate; + return true; + } + return false; + } + + // Bellman-Ford variant + private int[] solve(Arc[] arcs, int[] locations, boolean maximizing) { + int N = getCount() + 1; // The number of vertices is the number of columns/rows + 1. + + boolean changed = false; + // We take one extra pass over traditional Bellman-Ford (and omit their final step) + for (int i = 0; i < N; i++) { + changed = false; + for (int j = 0, length = arcs.length; j < length; j++) { + changed = changed | relax(locations, arcs[j], maximizing); + } + if (!changed) { + if (DEBUG) { + Log.d(TAG, "Iteration " + (maximizing ? "(max)" : "(min)") + + " completed after " + (1 + i) + " steps out of " + N); + } + break; + } + } + if (changed) { + Log.d(TAG, "*** Algorithm failed to terminate ***"); + } + return locations; + } + + private int[] init(int defaultValue, int min, int max) { + int N = getCount() + 1; // The number of vertices is the number of columns/rows + 1. + int[] locations = new int[N]; + Arrays.fill(locations, defaultValue); + locations[0] = min; + locations[N - 1] = max; + return locations; + } + + private int[] computeMargins(boolean leading) { + int[] result = new int[getCount() + 1]; + for (int i = 0, size = getChildCount(); i < size; i++) { + View c = getChildAt(i); + LayoutParams lp = getLayoutParams(c); + Group g = horizontal ? lp.columnGroup : lp.rowGroup; + Interval span = g.span; + int index = leading ? span.min : span.max; + result[index] = max(result[index], getMargin(c, leading, horizontal)); + } + return result; + } + + // has side effects + private void computeLocations(int[] locations, boolean maximizing) { + leadingMargins = computeMargins(true); + trailingMargins = computeMargins(false); + + solve(getArcs(maximizing), locations, maximizing); + + // Add margins + int delta = 0; + for (int i = 0; i < getCount(); i++) { + int margins = leadingMargins[i] + trailingMargins[i + 1]; + delta += margins; + locations[i + 1] += delta; + } + } + + private int size(int[] locations) { + return locations[locations.length - 1] - locations[0]; + } + + private int[] getLimit(boolean lowerBound, boolean maximizing) { + int defaultValue = maximizing ? MAX_VALUE : MIN_VALUE; + if (lowerBound) { + // as long as it avoids overflow, the upper bound can be anything (including zero) + int[] result = init(defaultValue, defaultValue, 1000); + computeLocations(result, maximizing); + int delta = result[0]; + for (int i = 0; i < result.length; i++) { + result[i] -= delta; + } + return result; + } else { + int[] result = init(defaultValue, 0, defaultValue); + computeLocations(result, maximizing); + return result; + } + } + + // External entry points + + private int getMin() { + int[] mins = getLimit(maximizing, maximizing); + return size(mins); + } + + private int getPref() { + return accommodateBothMinAndMax ? getMax() : getMin(); + } + + private int getMax() { + int[] maxs = getLimit(!maximizing, maximizing); + return size(maxs); + } + + private int totalMarginSize() { + return sum(leadingMargins) + sum(trailingMargins); + } + + private void layout(int targetSize) { + int N = getCount() + 1; + int min = getMin(); + int max = getMax(); + + int clippedTargetSize = max(min(max, targetSize), min); // confine size to valid range + + if (DEBUG) { + Log.d(TAG, "Computing sizes for target " + clippedTargetSize + " for " + + (horizontal ? "col" : "row") + "s from: " + arcs); + } + int delta = clippedTargetSize - min; + prefSizeOfWeightedComponent = delta; + invalidateValues(); + int defaultValue = maximizing ? MAX_VALUE : MIN_VALUE; + locations = init(defaultValue, 0, clippedTargetSize - totalMarginSize()); + computeLocations(locations, maximizing); + prefSizeOfWeightedComponent = 0; + + if (DEBUG) { + Log.d(TAG, "locations = " + Arrays.toString(locations)); + int[] computedSizes = new int[N - 1]; + for (int i = 0; i < N - 1; i++) { + computedSizes[i] = locations[i + 1] - locations[i]; + } + Log.d(TAG, "sizes = " + Arrays.toString(computedSizes)); + } + } + + private void invalidateStructure() { + countValid = false; + groupBounds = null; + spanSizes = null; + + invalidateValues(); + } + + private void invalidateValues() { + groupBoundsValid = false; + spanSizesValid = false; + arcsValid = false; + } + } + + /** + * Layout information associated with each of the children of a GridLayout. + * <p> + * 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 #rowGroup} and {@link #columnGroup} layout parameters. + * {@link Group Groups} are immutable structures and may be shared between the layout + * parameters of different children. + * <p> + * The {@link Group#span span} fields of the row and column groups together specify + * the four grid indices that delimit the cells of this cell group. + * <p> + * The {@link Group#alignment alignment} fields of the row and column groups 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. + * <p> + * See {@link GridLayout} for a description of the conventions used by GridLayout + * in reference to grid indices. + * + * <h4>Default values</h4> + * + * <ul> + * <li>{@link #width} = {@link #WRAP_CONTENT}</li> + * <li>{@link #height} = {@link #WRAP_CONTENT}</li> + * <li>{@link #topMargin} = 0 when + * {@link GridLayout#setUseDefaultMargins(boolean) useDefaultMargins} is + * <code>false</code>; otherwise {@link Integer#MIN_VALUE}, to + * indicate that a default value should be computed on demand. </li> + * <li>{@link #leftMargin} = 0 when + * {@link GridLayout#setUseDefaultMargins(boolean) useDefaultMargins} is + * <code>false</code>; otherwise {@link Integer#MIN_VALUE}, to + * indicate that a default value should be computed on demand. </li> + * <li>{@link #bottomMargin} = 0 when + * {@link GridLayout#setUseDefaultMargins(boolean) useDefaultMargins} is + * <code>false</code>; otherwise {@link Integer#MIN_VALUE}, to + * indicate that a default value should be computed on demand. </li> + * <li>{@link #rightMargin} = 0 when + * {@link GridLayout#setUseDefaultMargins(boolean) useDefaultMargins} is + * <code>false</code>; otherwise {@link Integer#MIN_VALUE}, to + * indicate that a default value should be computed on demand. </li> + * <li>{@link #rowGroup}<code>.span</code> = <code>[0, 1]</code> </li> + * <li>{@link #rowGroup}<code>.alignment</code> = {@link #BASELINE} </li> + * <li>{@link #columnGroup}<code>.span</code> = <code>[0, 1]</code> </li> + * <li>{@link #columnGroup}<code>.alignment</code> = {@link #LEFT} </li> + * <li>{@link #rowWeight} = <code>0f</code> </li> + * <li>{@link #columnWeight} = <code>0f</code> </li> + * </ul> + * + * @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_rowWeight + * @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_columnWeight + * @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(0, 1); + private static final int DEFAULT_SPAN_SIZE = DEFAULT_SPAN.size(); + private static final Alignment DEFAULT_HORIZONTAL_ALIGNMENT = LEFT; + private static final Alignment DEFAULT_VERTCIAL_ALGIGNMENT = BASELINE; + private static final Group DEFAULT_HORIZONTAL_GROUP = + new Group(DEFAULT_SPAN, DEFAULT_HORIZONTAL_ALIGNMENT); + private static final Group DEFAULT_VERTICAL_GROUP = + new Group(DEFAULT_SPAN, DEFAULT_VERTCIAL_ALGIGNMENT); + private static final int DEFAULT_WEIGHT = 0; + + // Misc + + private static final Rect CONTAINER_BOUNDS = new Rect(0, 0, 2, 2); + private static final Alignment[] HORIZONTAL_ALIGNMENTS = { LEFT, CENTER, RIGHT }; + private static final Alignment[] VERTICAL_ALIGNMENTS = { TOP, CENTER, BOTTOM }; + + // TypedArray indices + + private static final int MARGIN = styleable.ViewGroup_MarginLayout_layout_margin; + private static final int LEFT_MARGIN = styleable.ViewGroup_MarginLayout_layout_marginLeft; + private static final int TOP_MARGIN = styleable.ViewGroup_MarginLayout_layout_marginTop; + private static final int RIGHT_MARGIN = styleable.ViewGroup_MarginLayout_layout_marginRight; + private static final int BOTTOM_MARGIN = + styleable.ViewGroup_MarginLayout_layout_marginBottom; + + private static final int COLUMN = styleable.GridLayout_Layout_layout_column; + private static final int COLUMN_SPAN = styleable.GridLayout_Layout_layout_columnSpan; + private static final int COLUMN_WEIGHT = styleable.GridLayout_Layout_layout_columnWeight; + private static final int ROW = styleable.GridLayout_Layout_layout_row; + private static final int ROW_SPAN = styleable.GridLayout_Layout_layout_rowSpan; + private static final int ROW_WEIGHT = styleable.GridLayout_Layout_layout_rowWeight; + private static final int GRAVITY = styleable.GridLayout_Layout_layout_gravity; + + // Instance variables + + /** + * The group that specifies the vertical characteristics of the cell group + * described by these layout parameters. + */ + public Group rowGroup; + /** + * The group that specifies the horizontal characteristics of the cell group + * described by these layout parameters. + */ + public Group columnGroup; + /** + * The proportional space that should be taken by the associated row group + * during excess space distribution. + */ + public float rowWeight; + /** + * The proportional space that should be taken by the associated column group + * during excess space distribution. + */ + public float columnWeight; + + // Constructors + + private LayoutParams( + int width, int height, + int left, int top, int right, int bottom, + Group rowGroup, Group columnGroup, float rowWeight, float columnWeight) { + super(width, height); + setMargins(left, top, right, bottom); + this.rowGroup = rowGroup; + this.columnGroup = columnGroup; + this.rowWeight = rowWeight; + this.columnWeight = columnWeight; + } + + /** + * Constructs a new LayoutParams instance for this <code>rowGroup</code> + * and <code>columnGroup</code>. All other fields are initialized with + * default values as defined in {@link LayoutParams}. + * + * @param rowGroup the rowGroup + * @param columnGroup the columnGroup + */ + public LayoutParams(Group rowGroup, Group columnGroup) { + this(DEFAULT_WIDTH, DEFAULT_HEIGHT, + DEFAULT_MARGIN, DEFAULT_MARGIN, DEFAULT_MARGIN, DEFAULT_MARGIN, + rowGroup, columnGroup, DEFAULT_WEIGHT, DEFAULT_WEIGHT); + } + + /** + * Constructs a new LayoutParams with default values as defined in {@link LayoutParams}. + */ + public LayoutParams() { + this(DEFAULT_HORIZONTAL_GROUP, DEFAULT_VERTICAL_GROUP); + } + + // 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.columnGroup = that.columnGroup; + this.rowGroup = that.rowGroup; + this.columnWeight = that.columnWeight; + this.rowWeight = that.rowWeight; + } + + // 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 <T> T getAlignment(T[] alignments, T fill, int min, int max, + boolean isUndefined, T defaultValue) { + if (isUndefined) { + return defaultValue; + } + return min != max ? fill : alignments[min]; + } + + // Reinitialise the margins using a different default policy than MarginLayoutParams. + // Here we use the value UNDEFINED (as distinct from zero) to represent the undefined state + // so that a layout manager default can be accessed post set up. We need this as, at the + // point of installation, we do not know how many rows/cols there are and therefore + // which elements are positioned next to the container's trailing edges. We need to + // know this as margins around the container's boundary should have different + // defaults to those between peers. + + // This method could be parametrized and moved into MarginLayout. + private void reInitSuper(Context context, AttributeSet attrs) { + TypedArray a = context.obtainStyledAttributes(attrs, styleable.ViewGroup_MarginLayout); + try { + int margin = a.getDimensionPixelSize(MARGIN, DEFAULT_MARGIN); + + this.leftMargin = a.getDimensionPixelSize(LEFT_MARGIN, margin); + this.topMargin = a.getDimensionPixelSize(TOP_MARGIN, margin); + this.rightMargin = a.getDimensionPixelSize(RIGHT_MARGIN, margin); + this.bottomMargin = a.getDimensionPixelSize(BOTTOM_MARGIN, margin); + } finally { + a.recycle(); + } + } + + // Gravity. For conversion from the static the integers defined in the Gravity class, + // use Gravity.apply() to apply gravity to a view of zero size and see where it ends up. + private static Alignment getHorizontalAlignment(int gravity, int width) { + Rect r = new Rect(0, 0, 0, 0); + Gravity.apply(gravity, 0, 0, CONTAINER_BOUNDS, r); + + boolean fill = width == MATCH_PARENT; + Alignment defaultAlignment = fill ? FILL : DEFAULT_HORIZONTAL_ALIGNMENT; + return getAlignment(HORIZONTAL_ALIGNMENTS, FILL, r.left, r.right, + !definesHorizontal(gravity), defaultAlignment); + } + + private static Alignment getVerticalAlignment(int gravity, int height) { + Rect r = new Rect(0, 0, 0, 0); + Gravity.apply(gravity, 0, 0, CONTAINER_BOUNDS, r); + + boolean fill = height == MATCH_PARENT; + Alignment defaultAlignment = fill ? FILL : DEFAULT_VERTCIAL_ALGIGNMENT; + return getAlignment(VERTICAL_ALIGNMENTS, FILL, r.top, r.bottom, + !definesVertical(gravity), defaultAlignment); + } + + private void init(Context context, AttributeSet attrs, int defaultGravity) { + TypedArray a = context.obtainStyledAttributes(attrs, styleable.GridLayout_Layout); + try { + int gravity = a.getInteger(GRAVITY, defaultGravity); + + int column = a.getInteger(COLUMN, DEFAULT_COLUMN); + int width = a.getInteger(COLUMN_SPAN, DEFAULT_SPAN_SIZE); + Interval colSpan = new Interval(column, column + width); + this.columnGroup = new Group(colSpan, getHorizontalAlignment(gravity, width)); + this.columnWeight = a.getFloat(COLUMN_WEIGHT, DEFAULT_WEIGHT); + + int row = a.getInteger(ROW, DEFAULT_ROW); + int height = a.getInteger(ROW_SPAN, DEFAULT_SPAN_SIZE); + Interval rowSpan = new Interval(row, row + height); + this.rowGroup = new Group(rowSpan, getVerticalAlignment(gravity, height)); + this.rowWeight = a.getFloat(ROW_WEIGHT, DEFAULT_WEIGHT); + } finally { + a.recycle(); + } + } + + /** + * Describes how the child views are positioned. Default is <code>LEFT | BASELINE</code>. + * + * @param gravity the new gravity. See {@link android.view.Gravity}. + * + * @attr ref android.R.styleable#GridLayout_Layout_layout_gravity + */ + public void setGravity(int gravity) { + columnGroup = columnGroup.copyWriteAlignment(getHorizontalAlignment(gravity, width)); + rowGroup = rowGroup.copyWriteAlignment(getVerticalAlignment(gravity, height)); + } + + @Override + protected void setBaseAttributes(TypedArray attributes, int widthAttr, int heightAttr) { + this.width = attributes.getLayoutDimension(widthAttr, DEFAULT_WIDTH); + this.height = attributes.getLayoutDimension(heightAttr, DEFAULT_HEIGHT); + } + + private void setVerticalGroupSpan(Interval span) { + rowGroup = rowGroup.copyWriteSpan(span); + } + + private void setHorizontalGroupSpan(Interval span) { + columnGroup = columnGroup.copyWriteSpan(span); + } + } + + private static class Arc { + public final Interval span; + public final Int value; + public boolean completesCycle; + + public Arc(Interval span, Int value) { + this.span = span; + this.value = value; + } + + @Override + public String toString() { + return span + " " + (completesCycle ? "+>" : "->") + " " + value; + } + } + + // A mutable Integer - used to avoid heap allocation during the layout operation + + private static class Int { + public int value; + + private Int() { + reset(); + } + + private Int(int value) { + this.value = value; + } + + private void reset() { + value = Integer.MIN_VALUE; + } + + private Int neg() { + // this should never be called + throw new UnsupportedOperationException(); + } + } + + @SuppressWarnings(value = "unchecked") + private static class PackedMap<K, V> { + public final int[] index; + public final K[] keys; + public final V[] values; + + private PackedMap(K[] keys, V[] values) { + this.index = createIndex(keys); + + this.keys = index(keys, index); + this.values = index(values, index); + } + + private K getKey(int i) { + return keys[index[i]]; + } + + private V getValue(int i) { + return values[index[i]]; + } + + private static <K> int[] createIndex(K[] keys) { + int size = keys.length; + int[] result = new int[size]; + + Map<K, Integer> keyToIndex = new HashMap<K, Integer>(); + for (int i = 0; i < size; i++) { + K key = keys[i]; + Integer index = keyToIndex.get(key); + if (index == null) { + index = keyToIndex.size(); + keyToIndex.put(key, index); + } + result[i] = index; + } + return result; + } + + private static int max(int[] a, int valueIfEmpty) { + int result = valueIfEmpty; + for (int i = 0, length = a.length; i < length; i++) { + result = Math.max(result, a[i]); + } + return result; + } + + private static <K> K[] index(K[] keys, int[] index) { + int size = keys.length; + Class<?> componentType = keys.getClass().getComponentType(); + K[] result = (K[]) Array.newInstance(componentType, max(index, -1) + 1); + + // this overwrite duplicates, retaining the last equivalent entry + for (int i = 0; i < size; i++) { + result[index[i]] = keys[i]; + } + return result; + } + } + + private static class Bounds { + public int below; + public int above; + + private Bounds(int below, int above) { + this.below = below; + this.above = above; + } + + private Bounds() { + reset(); + } + + private void reset() { + below = Integer.MAX_VALUE; + above = Integer.MIN_VALUE; + } + + private void include(int below, int above) { + this.below = min(this.below, below); + this.above = max(this.above, above); + } + + private int size() { + return above - below; + } + + @Override + public String toString() { + return "Bounds{" + + "below=" + below + + ", above=" + above + + '}'; + } + } + + /** + * An Interval represents a contiguous range of values that lie between + * the interval's {@link #min} and {@link #max} values. + * <p> + * 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. + * <p> + * Intervals are often written as <code>[min, max]</code> and represent the set of values + * <em>x</em> such that <em>min <= x < max</em>. + */ + public static class Interval { + /** + * The minimum value. + */ + public final int min; + /** + * The maximum value. + */ + public final int max; + + /** + * Construct a new Interval, <code>interval</code>, where: + * <ul> + * <li> <code>interval.min = min</code> </li> + * <li> <code>interval.max = max</code> </li> + * </ul> + * + * @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 true if the {@link #getClass class}, {@link #min} and {@link #max} properties + * of this Interval and the supplied parameter are pairwise equal; 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 group specifies either the horizontal or vertical characteristics of a group of + * cells. + * <p> + * Groups are immutable and so may be shared between views with the same + * <code>span</code> and <code>alignment</code>. + */ + public static class Group { + /** + * The {@link Interval#min min} and {@link Interval#max max} values of + * a span specify the grid indices of the leading and trailing edges of + * the cell group. + * <p> + * See {@link GridLayout} for a description of the conventions used by GridLayout + * for grid indices. + */ + public 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. + */ + public final Alignment alignment; + + /** + * Construct a new Group, <code>group</code>, where: + * <ul> + * <li> <code>group.span = span</code> </li> + * <li> <code>group.alignment = alignment</code> </li> + * </ul> + * + * @param span the span. + * @param alignment the alignment. + */ + public Group(Interval span, Alignment alignment) { + this.span = span; + this.alignment = alignment; + } + + /** + * Construct a new Group, <code>group</code>, where: + * <ul> + * <li> <code>group.span = [min, max]</code> </li> + * <li> <code>group.alignment = alignment</code> </li> + * </ul> + * + * @param min the minimum. + * @param max the maximum. + * @param alignment the alignment. + */ + public Group(int min, int max, Alignment alignment) { + this(new Interval(min, max), alignment); + } + + /** + * Construct a new Group, <code>group</code>, where: + * <ul> + * <li> <code>group.span = [min, min + 1]</code> </li> + * <li> <code>group.alignment = alignment</code> </li> + * </ul> + * + * @param min the minimum. + * @param alignment the alignment. + */ + public Group(int min, Alignment alignment) { + this(min, min + 1, alignment); + } + + private Group copyWriteSpan(Interval span) { + return new Group(span, alignment); + } + + private Group copyWriteAlignment(Alignment alignment) { + return new Group(span, alignment); + } + + /** + * Returns true if the {@link #getClass class}, {@link #alignment} and {@link #span} + * properties of this Group and the supplied parameter are pairwise equal; false otherwise. + * + * @param that the object to compare this group with. + * + * @return {@code true} if the specified object is equal to this + * {@code Group}; {@code false} otherwise. + */ + @Override + public boolean equals(Object that) { + if (this == that) { + return true; + } + if (that == null || getClass() != that.getClass()) { + return false; + } + + Group group = (Group) that; + + if (!alignment.equals(group.alignment)) { + return false; + } + if (!span.equals(group.span)) { + return false; + } + + return true; + } + + @Override + public int hashCode() { + int result = span.hashCode(); + result = 31 * result + alignment.hashCode(); + return result; + } + } + + // Alignments + + + /** + * Alignments specify where a view should be placed within a cell group and + * what size it should be. + * <p> + * The {@link LayoutParams} class contains a {@link LayoutParams#rowGroup rowGroup} + * and a {@link LayoutParams#columnGroup columnGroup} each of which contains an + * {@link Group#alignment alignment}. Overall placement of the view in the cell + * group is specified by the two alignments which act along each axis independently. + * <p> + * An Alignment implementation must define the {@link #getAlignmentValue(View, int)} + * to return the appropriate value for the type of alignment being defined. + * The enclosing algorithms position the children + * so that the values returned from the alignment + * are the same for all of the views in a group. + * <p> + * 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}. + */ + public static interface 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. + */ + public 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. + * + * @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. + * @return the aligned size. + */ + public int getSizeInCell(View view, int viewSize, int cellSize); + } + + private static abstract class AbstractAlignment implements Alignment { + public int getSizeInCell(View view, int viewSize, int cellSize) { + return viewSize; + } + } + + private static final Alignment LEADING = new AbstractAlignment() { + public int getAlignmentValue(View view, int viewSize) { + return 0; + } + + }; + + private static final Alignment TRAILING = new AbstractAlignment() { + public int getAlignmentValue(View view, int viewSize) { + return viewSize; + } + }; + + /** + * Indicates that a view should be aligned with the <em>top</em> + * edges of the other views in its cell group. + */ + public static final Alignment TOP = LEADING; + + /** + * Indicates that a view should be aligned with the <em>bottom</em> + * edges of the other views in its cell group. + */ + public static final Alignment BOTTOM = TRAILING; + + /** + * Indicates that a view should be aligned with the <em>right</em> + * edges of the other views in its cell group. + */ + public static final Alignment RIGHT = TRAILING; + + /** + * Indicates that a view should be aligned with the <em>left</em> + * edges of the other views in its cell group. + */ + public static final Alignment LEFT = LEADING; + + /** + * Indicates that a view should be <em>centered</em> with the other views in its cell group. + * This constant may be used in both {@link LayoutParams#rowGroup rowGroups} and {@link + * LayoutParams#columnGroup columnGroups}. + */ + public static final Alignment CENTER = new AbstractAlignment() { + public int getAlignmentValue(View view, int viewSize) { + return viewSize >> 1; + } + }; + + /** + * Indicates that a view should be aligned with the <em>baselines</em> + * of the other views in its cell group. + * This constant may only be used as an alignment in {@link LayoutParams#rowGroup rowGroups}. + * + * @see View#getBaseline() + */ + public static final Alignment BASELINE = new AbstractAlignment() { + public int getAlignmentValue(View view, int viewSize) { + if (view == null) { + return UNDEFINED; + } + // todo do we need to call measure first? + int baseline = view.getBaseline(); + return baseline == -1 ? UNDEFINED : baseline; + } + + }; + + /** + * Indicates that a view should expanded to fit the boundaries of its cell group. + * This constant may be used in both {@link LayoutParams#rowGroup rowGroups} and + * {@link LayoutParams#columnGroup columnGroups}. + */ + public static final Alignment FILL = new Alignment() { + public int getAlignmentValue(View view, int viewSize) { + return UNDEFINED; + } + + public int getSizeInCell(View view, int viewSize, int cellSize) { + return cellSize; + } + }; +}
\ No newline at end of file diff --git a/core/java/android/widget/Space.java b/core/java/android/widget/Space.java new file mode 100644 index 0000000..d98b937 --- /dev/null +++ b/core/java/android/widget/Space.java @@ -0,0 +1,75 @@ +/* + * 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.graphics.Canvas; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; + +/** + * Space is a lightweight View subclass that may be used to create gaps between components + * in general purpose layouts. + */ +public final class Space extends View { + /** + * {@inheritDoc} + */ + public Space(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + /** + * {@inheritDoc} + */ + public Space(Context context, AttributeSet attrs) { + super(context, attrs); + } + + /** + * {@inheritDoc} + */ + public Space(Context context) { + super(context); + } + + /** + * Draw nothing. + * + * @param canvas an unused parameter. + */ + @Override + public void draw(Canvas canvas) { + } + + /** + * {@inheritDoc} + */ + @Override + public ViewGroup.LayoutParams getLayoutParams() { + return super.getLayoutParams(); + } + + /** + * {@inheritDoc} + */ + @Override + public void setLayoutParams(ViewGroup.LayoutParams params) { + super.setLayoutParams(params); + } +} diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index c1e81c3..f854e93 100755 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -2429,6 +2429,36 @@ <!-- Size of padding on either end of a divider. --> <attr name="dividerPadding" format="dimension" /> </declare-styleable> + <declare-styleable name="GridLayout"> + <!-- The orientation property is not used during layout. It is only used to + allocate row and column prameters when they are not specified by its children's + layout paramters. GridLayout works like LinearLayout in this case; + putting all the components either in a single row or in a single column - + depending on the value of this flag. In the horozintal case, a columnCount + property may be additionally supplied to force new rows to be created when a + row is full. The rowCount attribute may be used similarly in the vertical case. + The default is horizontal. --> + <attr name="orientation" /> + <!-- The maxmimum number of rows to create when automatically positioning children. --> + <attr name="rowCount" format="integer" /> + <!-- The maxmimum number of columns to create when automatically positioning children. --> + <attr name="columnCount" format="integer" /> + <!-- When set to true, tells GridLayout to use default margins when none are specified + in a view's layout parameters. + The default value is false. + See {@link android.widget.GridLayout#setUseDefaultMargins(boolean)}.--> + <attr name="useDefaultMargins" format="boolean" /> + <!-- When set to true, forces row boundaries to appear in the same order + as row indices. + The default is false. + See {@link android.widget.GridLayout#setRowOrderPreserved(boolean)}.--> + <attr name="rowOrderPreserved" format="boolean" /> + <!-- When set to true, forces column boundaries to appear in the same order + as column indices. + The default is false. + See {@link android.widget.GridLayout#setColumnOrderPreserved(boolean)}.--> + <attr name="columnOrderPreserved" format="boolean" /> + </declare-styleable> <declare-styleable name="ListView"> <!-- Reference to an array resource that will populate the ListView. For static content, this is simpler than populating the ListView programmatically. --> @@ -3128,6 +3158,38 @@ <attr name="layout_weight" format="float" /> <attr name="layout_gravity" /> </declare-styleable> + <declare-styleable name="GridLayout_Layout"> + <!-- The row boundary delimiting the top of the group of cells + occupied by this view. --> + <attr name="layout_row" format="integer" /> + <!-- The row span: the difference between the bottom and top + boundaries delimiting the group of cells occupied by this view. + The default is one. + See {@link android.widget.GridLayout.Group#span}. --> + <attr name="layout_rowSpan" format="integer" min="1" /> + <!-- A number indicating the relative proportion of availible space that + should be taken by this group of cells. + The default is zero. + See {@link android.widget.GridLayout.LayoutParams#columnWeight}. --> + <attr name="layout_rowWeight" format="float" /> + <!-- The column boundary delimiting the left of the group of cells + occupied by this view. --> + <attr name="layout_column" /> + <!-- The column span: the difference between the right and left + boundaries delimiting the group of cells occupied by this view. + The default is one. + See {@link android.widget.GridLayout.Group#span}. --> + <attr name="layout_columnSpan" format="integer" min="1" /> + <!-- A number indicating the relative proportion of availible space that + should be taken by this group of cells. + The default is zero. + See {@link android.widget.GridLayout.LayoutParams#columnWeight}.--> + <attr name="layout_columnWeight" format="float" /> + <!-- Gravity specifies how a component should be placed in its group of cells. + The default is LEFT | BASELINE. + See {@link android.widget.GridLayout.LayoutParams#setGravity(int)}. --> + <attr name="layout_gravity" /> + </declare-styleable> <declare-styleable name="FrameLayout_Layout"> <attr name="layout_gravity" /> </declare-styleable> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 1957b2a..0bd939e 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -1672,4 +1672,17 @@ <public type="attr" name="fullBackupAgent" /> <public type="attr" name="suggestionsEnabled" /> + <public type="attr" name="rowCount" /> + <public type="attr" name="rowOrderPreserved" /> + <public type="attr" name="columnCount" /> + <public type="attr" name="columnOrderPreserved" /> + <public type="attr" name="useDefaultMargins" /> + + <public type="attr" name="layout_row" /> + <public type="attr" name="layout_rowSpan" /> + + <public type="attr" name="layout_rowWeight" /> + <public type="attr" name="layout_columnSpan" /> + <public type="attr" name="layout_columnWeight" /> + </resources> diff --git a/tests/GridLayoutTest/Android.mk b/tests/GridLayoutTest/Android.mk new file mode 100644 index 0000000..a02918b --- /dev/null +++ b/tests/GridLayoutTest/Android.mk @@ -0,0 +1,26 @@ +# +# Copyright (C) 2010 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. +# + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := $(call all-subdir-java-files) + +LOCAL_PACKAGE_NAME := GridLayoutTest + +LOCAL_MODULE_TAGS := tests + +include $(BUILD_PACKAGE) diff --git a/tests/GridLayoutTest/AndroidManifest.xml b/tests/GridLayoutTest/AndroidManifest.xml new file mode 100644 index 0000000..53ca4ce --- /dev/null +++ b/tests/GridLayoutTest/AndroidManifest.xml @@ -0,0 +1,82 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2010 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. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.test.layout"> + + <uses-permission android:name="android.permission.INTERNET"/> + <uses-sdk android:minSdkVersion="11"/> + + <application> + <activity android:name="Activity0" android:label="Activity0"> + <intent-filter> + <action android:name="android.intent.action.MAIN"/> + <category android:name="android.intent.category.LAUNCHER"/> + </intent-filter> + </activity> + + <activity android:name="Activity1" android:label="Activity1"> + <intent-filter> + <action android:name="android.intent.action.MAIN"/> + <category android:name="android.intent.category.LAUNCHER"/> + </intent-filter> + </activity> + + <activity android:name="Activity2" android:label="Activity2"> + <intent-filter> + <action android:name="android.intent.action.MAIN"/> + <category android:name="android.intent.category.LAUNCHER"/> + </intent-filter> + </activity> + + <activity android:name="Activity3" android:label="Activity3"> + <intent-filter> + <action android:name="android.intent.action.MAIN"/> + <category android:name="android.intent.category.LAUNCHER"/> + </intent-filter> + </activity> + + <activity android:name="Activity4" android:label="Activity4"> + <intent-filter> + <action android:name="android.intent.action.MAIN"/> + <category android:name="android.intent.category.LAUNCHER"/> + </intent-filter> + </activity> + + <activity android:name="Activity5" android:label="Activity5"> + <intent-filter> + <action android:name="android.intent.action.MAIN"/> + <category android:name="android.intent.category.LAUNCHER"/> + </intent-filter> + </activity> + + <activity android:name="Activity6" android:label="Activity6"> + <intent-filter> + <action android:name="android.intent.action.MAIN"/> + <category android:name="android.intent.category.LAUNCHER"/> + </intent-filter> + </activity> + + <activity android:name="Activity7" android:label="Activity7"> + <intent-filter> + <action android:name="android.intent.action.MAIN"/> + <category android:name="android.intent.category.LAUNCHER"/> + </intent-filter> + </activity> + </application> + + +</manifest> diff --git a/tests/GridLayoutTest/res/layout/grid0.xml b/tests/GridLayoutTest/res/layout/grid0.xml new file mode 100644 index 0000000..1c291f0 --- /dev/null +++ b/tests/GridLayoutTest/res/layout/grid0.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<GridLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="fill_parent" + android:layout_height="fill_parent"> +</GridLayout>
\ No newline at end of file diff --git a/tests/GridLayoutTest/res/layout/grid3.xml b/tests/GridLayoutTest/res/layout/grid3.xml new file mode 100644 index 0000000..ace7b4c --- /dev/null +++ b/tests/GridLayoutTest/res/layout/grid3.xml @@ -0,0 +1,77 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<GridLayout + xmlns:android="http://schemas.android.com/apk/res/android" + + android:layout_width="match_parent" + android:layout_height="match_parent" + > + + <Button + android:text="fill" + android:width="200dip" + android:height="100dip" + android:layout_marginLeft="50dip" + android:layout_row="0" + android:layout_column="0" + android:layout_gravity="fill_horizontal" + /> + + <EditText + android:layout_row="0" + android:layout_column="1" + /> + + <Button + android:text="left" + android:layout_row="1" + android:layout_column="0" + /> + + <EditText + android:layout_row="1" + android:layout_column="1" + /> + + <Button + android:text="right" + android:layout_row="2" + android:layout_column="0" + android:layout_gravity="right" + /> + + <EditText + android:layout_margin="50dip" + android:textSize="100dip" + android:layout_row="2" + android:layout_column="1" + /> + + <Button + android:text="center" + android:layout_row="3" + android:layout_column="0" + android:layout_gravity="center_horizontal" + /> + + <EditText + android:layout_height="fill_parent" + android:layout_row="3" + android:layout_column="1" + /> + +</GridLayout> diff --git a/tests/GridLayoutTest/res/layout/grid4.xml b/tests/GridLayoutTest/res/layout/grid4.xml new file mode 100644 index 0000000..116a256 --- /dev/null +++ b/tests/GridLayoutTest/res/layout/grid4.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<GridLayout + xmlns:android="http://schemas.android.com/apk/res/android" + + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <Button + android:text="fill" + android:layout_margin="100dip" + android:layout_marginLeft="100dip" + android:layout_marginBottom="100dip" + android:layout_marginTop="100dip" + android:layout_marginRight="100dip" + android:layout_row="0" + android:layout_column="0" + android:layout_gravity="fill_horizontal" + /> +</GridLayout> diff --git a/tests/GridLayoutTest/res/layout/grid5.xml b/tests/GridLayoutTest/res/layout/grid5.xml new file mode 100644 index 0000000..9b87f18 --- /dev/null +++ b/tests/GridLayoutTest/res/layout/grid5.xml @@ -0,0 +1,74 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<GridLayout + xmlns:android="http://schemas.android.com/apk/res/android" + + android:layout_width="match_parent" + android:layout_height="match_parent" + + android:orientation="horizontal" + android:columnCount="2" + > + + <TextView + android:text="Name:" + /> + + <EditText + android:text="fill" + android:width="200dip" + android:height="100dip" + android:layout_marginLeft="50dip" + android:layout_gravity="fill_horizontal" + /> + + <TextView + /> + +<!-- + <TextView + android:layout_row_weight="1" + android:layout_column_weight="1" + /> +--> + + <EditText + android:text="left" + /> + + <EditText/> + + <EditText + android:text="right" + android:layout_gravity="right" + /> + + <EditText + android:layout_margin="50dip" + android:textSize="100dip" + /> + + <Button + android:text="center" + android:layout_gravity="center_horizontal" + /> + + <EditText + android:layout_height="fill_parent" + /> + +</GridLayout> diff --git a/tests/GridLayoutTest/res/layout/grid6.xml b/tests/GridLayoutTest/res/layout/grid6.xml new file mode 100644 index 0000000..7486a2d --- /dev/null +++ b/tests/GridLayoutTest/res/layout/grid6.xml @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<GridLayout + xmlns:android="http://schemas.android.com/apk/res/android" + + android:layout_width="match_parent" + android:layout_height="match_parent" + + android:columnCount="2" + android:useDefaultMargins="true" + > + + <TextView android:text="Name:"/> + + <EditText/> + + <TextView android:text="Rank:"/> + + <EditText/> + + <TextView android:text="Serial Number:"/> + + <EditText/> + +</GridLayout> diff --git a/tests/GridLayoutTest/res/layout/grid7.xml b/tests/GridLayoutTest/res/layout/grid7.xml new file mode 100644 index 0000000..b97a00b --- /dev/null +++ b/tests/GridLayoutTest/res/layout/grid7.xml @@ -0,0 +1,71 @@ +<!-- Copyright (C) 2010 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. +--> + +<GridLayout + xmlns:android="http://schemas.android.com/apk/res/android" + + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <LinearLayout + android:orientation="horizontal" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:paddingTop="10dip" > + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:paddingRight="5dip" + android:text="flabe" /> + + <Button android:id="@+id/initialActivity" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="bax" /> + </LinearLayout> + + <LinearLayout + android:orientation="horizontal" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:paddingTop="5dip" > + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:paddingRight="5dip" + android:text="bar" /> + + <EditText android:id="@+id/numberOfEvents" + android:layout_marginLeft="2dip" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:background="@android:drawable/editbox_background" + android:numeric="integer" + android:scrollHorizontally="true" + android:maxLines="1" /> + </LinearLayout> + + <Button android:id="@+id/start" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:paddingTop="10dip" + android:text="Foo" /> + +</GridLayout> + + diff --git a/tests/GridLayoutTest/src/com/android/test/layout/Activity0.java b/tests/GridLayoutTest/src/com/android/test/layout/Activity0.java new file mode 100644 index 0000000..660c1c3 --- /dev/null +++ b/tests/GridLayoutTest/src/com/android/test/layout/Activity0.java @@ -0,0 +1,27 @@ +/* + * 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 com.android.test.layout; + +import android.app.Activity; +import android.os.Bundle; + +public class Activity0 extends Activity { + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.grid0); + } +} diff --git a/tests/GridLayoutTest/src/com/android/test/layout/Activity1.java b/tests/GridLayoutTest/src/com/android/test/layout/Activity1.java new file mode 100644 index 0000000..e6645f5 --- /dev/null +++ b/tests/GridLayoutTest/src/com/android/test/layout/Activity1.java @@ -0,0 +1,28 @@ +/* + * 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 com.android.test.layout; + +import android.app.Activity; +import android.os.Bundle; +import android.widget.GridLayout; + +public class Activity1 extends Activity { + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(new GridLayout(getBaseContext())); + } +} diff --git a/tests/GridLayoutTest/src/com/android/test/layout/Activity2.java b/tests/GridLayoutTest/src/com/android/test/layout/Activity2.java new file mode 100644 index 0000000..6359903 --- /dev/null +++ b/tests/GridLayoutTest/src/com/android/test/layout/Activity2.java @@ -0,0 +1,127 @@ +/* + * 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 com.android.test.layout; + +import android.app.Activity; +import android.content.Context; +import android.os.Bundle; +import android.view.View; + +import android.widget.*; + +import static android.text.InputType.TYPE_CLASS_TEXT; +import static android.view.inputmethod.EditorInfo.*; +import static android.widget.GridLayout.*; + +public class Activity2 extends Activity { + + public static View create(Context context) { + GridLayout vg = new GridLayout(context); + vg.setUseDefaultMargins(true); + + Group row1 = new Group(1, CENTER); + Group row2 = new Group(2, CENTER); + Group row3 = new Group(3, BASELINE); + Group row4 = new Group(4, BASELINE); + Group row5 = new Group(5, FILL); + Group row6 = new Group(6, CENTER); + Group row7 = new Group(7, CENTER); + + Group col1a = new Group(1, 5, CENTER); + Group col1b = new Group(1, 5, LEFT); + Group col1c = new Group(1, RIGHT); + Group col2 = new Group(2, LEFT); + Group col3 = new Group(3, FILL); + Group col4 = new Group(4, FILL); + + { + TextView v = new TextView(context); + v.setTextSize(48); + v.setText("Email setup"); + vg.addView(v, new LayoutParams(row1, col1a)); + } + + { + TextView v = new TextView(context); + v.setTextSize(20); + v.setText("You can configure email in just a few steps:"); + vg.addView(v, new LayoutParams(row2, col1b)); + } + + { + TextView v = new TextView(context); + v.setText("Email address:"); + vg.addView(v, new LayoutParams(row3, col1c)); + } + + { + EditText v = new EditText(context); + v.setInputType(TYPE_CLASS_TEXT | TYPE_TEXT_VARIATION_EMAIL_ADDRESS); + { + LayoutParams lp = new LayoutParams(row3, col2); + lp.width = (int) v.getPaint().measureText("Frederick.W.Flintstone@bedrock.com "); + vg.addView(v, lp); + } + } + + { + TextView v = new TextView(context); + v.setText("Password:"); + vg.addView(v, new LayoutParams(row4, col1c)); + } + + { + TextView v = new EditText(context); + v.setInputType(TYPE_CLASS_TEXT | TYPE_TEXT_VARIATION_PASSWORD); + { + LayoutParams lp = new LayoutParams(row4, col2); + lp.width = (int) v.getPaint().measureText("************"); + vg.addView(v, lp); + } + } + + { + Space v = new Space(context); + { + LayoutParams lp = new LayoutParams(row5, col3); + lp.rowWeight = 1; + lp.columnWeight = 1; + vg.addView(v, lp); + } + } + + { + Button v = new Button(context); + v.setText("Manual setup"); + vg.addView(v, new LayoutParams(row6, col4)); + } + + { + Button v = new Button(context); + v.setText("Next"); + vg.addView(v, new LayoutParams(row7, col4)); + } + + return vg; + } + + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(create(getBaseContext())); + } + +}
\ No newline at end of file diff --git a/tests/GridLayoutTest/src/com/android/test/layout/Activity3.java b/tests/GridLayoutTest/src/com/android/test/layout/Activity3.java new file mode 100644 index 0000000..fc0b382 --- /dev/null +++ b/tests/GridLayoutTest/src/com/android/test/layout/Activity3.java @@ -0,0 +1,27 @@ +/* + * 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 com.android.test.layout; + +import android.app.Activity; +import android.os.Bundle; + +public class Activity3 extends Activity { + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.grid3); + } +} diff --git a/tests/GridLayoutTest/src/com/android/test/layout/Activity4.java b/tests/GridLayoutTest/src/com/android/test/layout/Activity4.java new file mode 100644 index 0000000..444f87c --- /dev/null +++ b/tests/GridLayoutTest/src/com/android/test/layout/Activity4.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 20101 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 com.android.test.layout; + +import android.app.Activity; +import android.os.Bundle; + +public class Activity4 extends Activity { + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.grid4); + } +} diff --git a/tests/GridLayoutTest/src/com/android/test/layout/Activity5.java b/tests/GridLayoutTest/src/com/android/test/layout/Activity5.java new file mode 100644 index 0000000..d317526 --- /dev/null +++ b/tests/GridLayoutTest/src/com/android/test/layout/Activity5.java @@ -0,0 +1,27 @@ +/* + * 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 com.android.test.layout; + +import android.app.Activity; +import android.os.Bundle; + +public class Activity5 extends Activity { + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.grid5); + } +} diff --git a/tests/GridLayoutTest/src/com/android/test/layout/Activity6.java b/tests/GridLayoutTest/src/com/android/test/layout/Activity6.java new file mode 100644 index 0000000..9bc9f9f --- /dev/null +++ b/tests/GridLayoutTest/src/com/android/test/layout/Activity6.java @@ -0,0 +1,27 @@ +/* + * 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 com.android.test.layout; + +import android.app.Activity; +import android.os.Bundle; + +public class Activity6 extends Activity { + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.grid6); + } +} diff --git a/tests/GridLayoutTest/src/com/android/test/layout/Activity7.java b/tests/GridLayoutTest/src/com/android/test/layout/Activity7.java new file mode 100644 index 0000000..6350381 --- /dev/null +++ b/tests/GridLayoutTest/src/com/android/test/layout/Activity7.java @@ -0,0 +1,27 @@ +/* + * 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 com.android.test.layout; + +import android.app.Activity; +import android.os.Bundle; + +public class Activity7 extends Activity { + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.grid7); + } +} |