summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPhilip Milne <pmilne@google.com>2011-05-27 18:38:01 -0700
committerPhilip Milne <pmilne@google.com>2011-06-03 13:22:52 -0700
commitaa616f31fe7c0c8e3657bb9a5889ec5e56ee5232 (patch)
tree7ed5a6e67f38bf2bd7264417a41508d5ca23dca9
parentb2450ce105086d1ac82d273a5292d9581c6ddec4 (diff)
downloadframeworks_base-aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232.zip
frameworks_base-aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232.tar.gz
frameworks_base-aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232.tar.bz2
Response to code review for GridLayout:
. Fixed spelling. . Added comments on internal methods. . Adopted the suggested internal name changes to improve clarity. . Added UNDEFINED constant to public API to avoid making reference to Integer.MAX_VALUE in docs. . Added final everywhere, then removed it. . Make the Interval class package private so that it can be put somewhere more general later. . Tidy code, removing maximize flag throughout. . Remove last of allocations taking place during layout. . Implement measureChild() etc. . Added LinearLayout alignment compatibility mode, and made it the default. Change-Id: I6a4ffa022d97d68138d1903d3830a20278815435 https://android-git.corp.google.com/g/#change,109891
-rw-r--r--api/current.txt38
-rw-r--r--core/java/android/widget/GridLayout.java716
-rwxr-xr-xcore/res/res/values/attrs.xml18
-rw-r--r--core/res/res/values/public.xml1
-rw-r--r--tests/GridLayoutTest/AndroidManifest.xml21
-rw-r--r--tests/GridLayoutTest/src/com/android/test/layout/AbstractLayoutTest.java71
-rw-r--r--tests/GridLayoutTest/src/com/android/test/layout/Activity2.java1
-rwxr-xr-xtests/GridLayoutTest/src/com/android/test/layout/AlignmentTest.java116
-rw-r--r--tests/GridLayoutTest/src/com/android/test/layout/GridLayoutTest.java52
-rw-r--r--tests/GridLayoutTest/src/com/android/test/layout/LinearLayoutTest.java50
10 files changed, 812 insertions, 272 deletions
diff --git a/api/current.txt b/api/current.txt
index edce92a..61e88a3 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -179,9 +179,9 @@ package android {
public static final class R.attr {
ctor public R.attr();
field public static final int absListViewStyle = 16842858; // 0x101006a
- field public static final int accessibilityEventTypes = 16843645; // 0x101037d
- field public static final int accessibilityFeedbackType = 16843647; // 0x101037f
- field public static final int accessibilityFlags = 16843649; // 0x1010381
+ field public static final int accessibilityEventTypes = 16843646; // 0x101037e
+ field public static final int accessibilityFeedbackType = 16843648; // 0x1010380
+ field public static final int accessibilityFlags = 16843650; // 0x1010382
field public static final int accountPreferences = 16843423; // 0x101029f
field public static final int accountType = 16843407; // 0x101028f
field public static final int action = 16842797; // 0x101002d
@@ -201,7 +201,7 @@ package android {
field public static final int actionModeCopyDrawable = 16843538; // 0x1010312
field public static final int actionModeCutDrawable = 16843537; // 0x1010311
field public static final int actionModePasteDrawable = 16843539; // 0x1010313
- field public static final int actionModeSelectAllDrawable = 16843643; // 0x101037b
+ field public static final int actionModeSelectAllDrawable = 16843644; // 0x101037c
field public static final int actionOverflowButtonStyle = 16843510; // 0x10102f6
field public static final int actionViewClass = 16843516; // 0x10102fc
field public static final int activatedBackgroundIndicator = 16843517; // 0x10102fd
@@ -273,7 +273,7 @@ package android {
field public static final int cacheColorHint = 16843009; // 0x1010101
field public static final int calendarViewShown = 16843596; // 0x101034c
field public static final int calendarViewStyle = 16843613; // 0x101035d
- field public static final int canRetrieveWindowContent = 16843650; // 0x1010382
+ field public static final int canRetrieveWindowContent = 16843651; // 0x1010383
field public static final int candidatesTextStyleSpans = 16843312; // 0x1010230
field public static final deprecated int capitalize = 16843113; // 0x1010169
field public static final int centerBright = 16842956; // 0x10100cc
@@ -532,7 +532,7 @@ package android {
field public static final int installLocation = 16843447; // 0x10102b7
field public static final int interpolator = 16843073; // 0x1010141
field public static final int isAlwaysSyncable = 16843571; // 0x1010333
- field public static final int isAuxiliary = 16843644; // 0x101037c
+ field public static final int isAuxiliary = 16843645; // 0x101037d
field public static final int isDefault = 16843297; // 0x1010221
field public static final int isIndicator = 16843079; // 0x1010147
field public static final int isModifier = 16843334; // 0x1010246
@@ -584,8 +584,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 = 16843641; // 0x1010379
- field public static final int layout_columnWeight = 16843642; // 0x101037a
+ field public static final int layout_columnSpan = 16843642; // 0x101037a
+ field public static final int layout_columnWeight = 16843643; // 0x101037b
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
@@ -593,9 +593,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 = 16843638; // 0x1010376
- field public static final int layout_rowSpan = 16843639; // 0x1010377
- field public static final int layout_rowWeight = 16843640; // 0x1010378
+ field public static final int layout_row = 16843639; // 0x1010377
+ field public static final int layout_rowSpan = 16843640; // 0x1010378
+ field public static final int layout_rowWeight = 16843641; // 0x1010379
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
@@ -625,6 +625,7 @@ package android {
field public static final int loopViews = 16843527; // 0x1010307
field public static final int manageSpaceActivity = 16842756; // 0x1010004
field public static final int mapViewStyle = 16842890; // 0x101008a
+ field public static final int marginsIncludedInAlignment = 16843638; // 0x1010376
field public static final int marqueeRepeatLimit = 16843293; // 0x101021d
field public static final int max = 16843062; // 0x1010136
field public static final int maxDate = 16843584; // 0x1010340
@@ -661,7 +662,7 @@ package android {
field public static final int nextFocusUp = 16842979; // 0x10100e3
field public static final int noHistory = 16843309; // 0x101022d
field public static final int normalScreens = 16843397; // 0x1010285
- field public static final int notificationTimeout = 16843648; // 0x1010380
+ field public static final int notificationTimeout = 16843649; // 0x1010381
field public static final int numColumns = 16843032; // 0x1010118
field public static final int numStars = 16843076; // 0x1010144
field public static final deprecated int numeric = 16843109; // 0x1010165
@@ -678,7 +679,7 @@ package android {
field public static final int overScrollFooter = 16843459; // 0x10102c3
field public static final int overScrollHeader = 16843458; // 0x10102c2
field public static final int overScrollMode = 16843457; // 0x10102c1
- field public static final int packageNames = 16843646; // 0x101037e
+ field public static final int packageNames = 16843647; // 0x101037f
field public static final int padding = 16842965; // 0x10100d5
field public static final int paddingBottom = 16842969; // 0x10100d9
field public static final int paddingLeft = 16842966; // 0x10100d6
@@ -24533,6 +24534,7 @@ package android.widget {
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 boolean getMarginsIncludedInAlignment();
method public int getOrientation();
method public int getRowCount();
method public boolean getUseDefaultMargins();
@@ -24541,6 +24543,7 @@ package android.widget {
method protected void onLayout(boolean, int, int, int, int);
method public void setColumnCount(int);
method public void setColumnOrderPreserved(boolean);
+ method public void setMarginsIncludedInAlignment(boolean);
method public void setOrientation(int);
method public void setRowCount(int);
method public void setRowOrderPreserved(boolean);
@@ -24553,6 +24556,7 @@ package android.widget {
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 UNDEFINED = -2147483648; // 0x80000000
field public static final int VERTICAL = 1; // 0x1
}
@@ -24562,17 +24566,9 @@ package android.widget {
}
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 {
diff --git a/core/java/android/widget/GridLayout.java b/core/java/android/widget/GridLayout.java
index 4889101..b99cd7f 100644
--- a/core/java/android/widget/GridLayout.java
+++ b/core/java/android/widget/GridLayout.java
@@ -38,6 +38,8 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import static android.view.View.MeasureSpec.EXACTLY;
+import static android.view.View.MeasureSpec.UNSPECIFIED;
import static java.lang.Math.max;
import static java.lang.Math.min;
@@ -46,7 +48,7 @@ import static java.lang.Math.min;
* <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
+ * by grid <em>indices</em>. A grid with <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
@@ -116,16 +118,27 @@ public class GridLayout extends ViewGroup {
* The horizontal orientation.
*/
public static final int HORIZONTAL = LinearLayout.HORIZONTAL;
+
/**
* The vertical orientation.
*/
public static final int VERTICAL = LinearLayout.VERTICAL;
+ /**
+ * The constant used to indicate that a value is undefined.
+ * Fields can use this value to indicate that their values
+ * have not yet been set. Similarly, methods can return this value
+ * to indicate that there is no suitable value that the implementation
+ * can return.
+ * The value used for the constant (currently {@link Integer#MIN_VALUE}) is
+ * intended to avoid confusion between valid values whose sign may not be known.
+ */
+ public static final int UNDEFINED = Integer.MIN_VALUE;
+
// 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;
@@ -138,6 +151,9 @@ public class GridLayout extends ViewGroup {
private static final int DEFAULT_COUNT = UNDEFINED;
private static final boolean DEFAULT_USE_DEFAULT_MARGINS = false;
private static final boolean DEFAULT_ORDER_PRESERVED = false;
+ private static final boolean DEFAULT_MARGINS_INCLUDED = true;
+ // todo remove this
+ private static final int DEFAULT_CONTAINER_MARGIN = 20;
// TypedArray indices
@@ -145,9 +161,16 @@ public class GridLayout extends ViewGroup {
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 MARGINS_INCLUDED = styleable.GridLayout_marginsIncludedInAlignment;
private static final int ROW_ORDER_PRESERVED = styleable.GridLayout_rowOrderPreserved;
private static final int COLUMN_ORDER_PRESERVED = styleable.GridLayout_columnOrderPreserved;
+ // Static initialization
+
+ static {
+ GRID_PAINT.setColor(Color.argb(50, 255, 255, 255));
+ }
+
// Instance variables
private final Axis mHorizontalAxis = new Axis(true);
@@ -155,9 +178,10 @@ public class GridLayout extends ViewGroup {
private boolean mLayoutParamsValid = false;
private int mOrientation = DEFAULT_ORIENTATION;
private boolean mUseDefaultMargins = DEFAULT_USE_DEFAULT_MARGINS;
+ private boolean mMarginsIncludedInAlignment = DEFAULT_MARGINS_INCLUDED;
private int mDefaultGravity = Gravity.NO_GRAVITY;
- boolean maximizing = false;
- boolean accommodateBothMinAndMax = false;
+
+ /* package */ boolean accommodateBothMinAndMax = false;
// Constructors
@@ -194,6 +218,7 @@ public class GridLayout extends ViewGroup {
setColumnCount(a.getInteger(COLUMN_COUNT, DEFAULT_COUNT));
mOrientation = a.getInteger(ORIENTATION, DEFAULT_ORIENTATION);
mUseDefaultMargins = a.getBoolean(USE_DEFAULT_MARGINS, DEFAULT_USE_DEFAULT_MARGINS);
+ mMarginsIncludedInAlignment = a.getBoolean(MARGINS_INCLUDED, DEFAULT_MARGINS_INCLUDED);
setRowOrderPreserved(a.getBoolean(ROW_ORDER_PRESERVED, DEFAULT_ORDER_PRESERVED));
setColumnOrderPreserved(a.getBoolean(COLUMN_ORDER_PRESERVED, DEFAULT_ORDER_PRESERVED));
} finally {
@@ -320,10 +345,15 @@ public class GridLayout extends ViewGroup {
* to the appropriate layout parameter.
* <p>
* When false, the default value of all margins is zero.
+ * <p>
+ * When setting to true, consider setting the value of the
+ * {@link #setMarginsIncludedInAlignment(boolean) marginsIncludedInAlignment}
+ * property to false.
*
* @param useDefaultMargins use true to make GridLayout allocate default margins
*
* @see #getUseDefaultMargins()
+ * @see #setMarginsIncludedInAlignment(boolean)
*
* @see MarginLayoutParams#leftMargin
* @see MarginLayoutParams#topMargin
@@ -334,6 +364,39 @@ public class GridLayout extends ViewGroup {
*/
public void setUseDefaultMargins(boolean useDefaultMargins) {
mUseDefaultMargins = useDefaultMargins;
+ requestLayout();
+ }
+
+ /**
+ * Returns whether GridLayout aligns the edges of the view or the edges
+ * of the larger rectangle created by extending the view by its associated
+ * margins.
+ *
+ * @see #setMarginsIncludedInAlignment(boolean)
+ *
+ * @return true if alignment is between edges including margins.
+ *
+ * @attr ref android.R.styleable#GridLayout_marginsIncludedInAlignment
+ */
+ public boolean getMarginsIncludedInAlignment() {
+ return mMarginsIncludedInAlignment;
+ }
+
+ /**
+ * When true, the bounds of a view are extended outwards according to its
+ * margins before the edges of the resulting rectangle are aligned.
+ * When false, alignment occurs between the bounds of the view - i.e.
+ * {@link #LEFT} alignment means align the left edges of the view.
+ *
+ * @param marginsIncludedInAlignment true if alignment is between edges including margins.
+ *
+ * @see #getMarginsIncludedInAlignment()
+ *
+ * @attr ref android.R.styleable#GridLayout_marginsIncludedInAlignment
+ */
+ public void setMarginsIncludedInAlignment(boolean marginsIncludedInAlignment) {
+ mMarginsIncludedInAlignment = marginsIncludedInAlignment;
+ requestLayout();
}
/**
@@ -353,11 +416,9 @@ public class GridLayout extends ViewGroup {
/**
* 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>
+ * When this property is <code>true</code>, GridLayout is forced to place the row boundaries
+ * so that their associated grid indices are 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.
@@ -377,6 +438,8 @@ public class GridLayout extends ViewGroup {
*/
public void setRowOrderPreserved(boolean rowOrderPreserved) {
mVerticalAxis.setOrderPreserved(rowOrderPreserved);
+ invalidateStructure();
+ requestLayout();
}
/**
@@ -396,11 +459,9 @@ public class GridLayout extends ViewGroup {
/**
* 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>
+ * When this property is <code>true</code>, GridLayout is forced to place the column boundaries
+ * so that their associated grid indices are 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.
@@ -420,13 +481,11 @@ public class GridLayout extends ViewGroup {
*/
public void setColumnOrderPreserved(boolean columnOrderPreserved) {
mHorizontalAxis.setOrderPreserved(columnOrderPreserved);
+ invalidateStructure();
+ requestLayout();
}
- private static int compare(int i, int j) {
- return i < j ? -1 : i > j ? 1 : 0;
- }
-
- private static int sum(int[] a) {
+ private static int sum(float[] a) {
int result = 0;
for (int i = 0, length = a.length; i < length; i++) {
result += a[i];
@@ -440,13 +499,12 @@ public class GridLayout extends ViewGroup {
// 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);
+ // todo remove DEFAULT_CONTAINER_MARGIN. Use padding? Seek advice on Themes/Styles, etc.
+ return isAtEdge ? DEFAULT_CONTAINER_MARGIN : getDefaultMargin(c, leading, horizontal);
}
private int getDefaultMarginValue(View c, LayoutParams p, boolean leading, boolean horizontal) {
@@ -456,7 +514,7 @@ public class GridLayout extends ViewGroup {
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();
+ boolean isAtEdge = leading ? (span.min == 0) : (span.max == axis.getCount());
return getDefaultMargin(c, isAtEdge, leading, horizontal);
}
@@ -464,8 +522,8 @@ public class GridLayout extends ViewGroup {
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;
+ (leading ? lp.leftMargin : lp.rightMargin) :
+ (leading ? lp.topMargin : lp.bottomMargin);
return margin == UNDEFINED ? getDefaultMarginValue(view, lp, leading, horizontal) : margin;
}
@@ -475,7 +533,7 @@ public class GridLayout extends ViewGroup {
private void validateLayoutParams() {
// install default indices for cells if *none* are defined
- if (mHorizontalAxis.maxIndex1() == UNDEFINED || mVerticalAxis.maxIndex1() == UNDEFINED) {
+ if (mHorizontalAxis.maxIndex1() == UNDEFINED || (mVerticalAxis.maxIndex1() == UNDEFINED)) {
boolean horizontal = mOrientation == HORIZONTAL;
int count = horizontal ? mHorizontalAxis.count : mVerticalAxis.count;
if (count == UNDEFINED) {
@@ -539,14 +597,17 @@ public class GridLayout extends ViewGroup {
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();
+ // Need null check because requestLayout() is called in View's initializer,
+ // before we are set up.
+ if (mHorizontalAxis != null && mVerticalAxis != null) {
+ mHorizontalAxis.invalidateValues();
+ mVerticalAxis.invalidateValues();
+ }
}
private LayoutParams getLayoutParams1(View c) {
@@ -605,10 +666,6 @@ public class GridLayout extends ViewGroup {
}
}
- static {
- GRID_PAINT.setColor(Color.argb(50, 255, 255, 255));
- }
-
// Add/remove
@Override
@@ -643,18 +700,49 @@ public class GridLayout extends ViewGroup {
// Measurement
+ private static int getChildMeasureSpec2(int spec, int padding, int childDimension) {
+ int resultSize;
+ int resultMode;
+
+ if (childDimension >= 0) {
+ resultSize = childDimension;
+ resultMode = EXACTLY;
+ } else {
+ /*
+ using the following lines would replicate the logic of ViewGroup.getChildMeasureSpec()
+
+ int specMode = MeasureSpec.getMode(spec);
+ int specSize = MeasureSpec.getSize(spec);
+ int size = Math.max(0, specSize - padding);
+
+ resultSize = size;
+ resultMode = (specMode == EXACTLY && childDimension == LayoutParams.WRAP_CONTENT) ?
+ AT_MOST : specMode;
+ */
+ resultSize = 0;
+ resultMode = UNSPECIFIED;
+ }
+ return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
+ }
+
@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);
+ protected void measureChild(View child, int parentWidthSpec, int parentHeightSpec) {
+ ViewGroup.LayoutParams lp = child.getLayoutParams();
- // todo - handle widthSpec and heightSpec properly
+ int childWidthMeasureSpec = getChildMeasureSpec2(parentWidthSpec,
+ mPaddingLeft + mPaddingRight, lp.width);
+ int childHeightMeasureSpec = getChildMeasureSpec2(parentHeightSpec,
+ mPaddingTop + mPaddingBottom, lp.height);
- int computedWidth = getPaddingLeft() + mHorizontalAxis.getPref() + getPaddingRight();
- int computedHeight = getPaddingTop() + mVerticalAxis.getPref() + getPaddingBottom();
+ child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
+ }
+
+ @Override
+ protected void onMeasure(int widthSpec, int heightSpec) {
+ measureChildren(widthSpec, heightSpec);
+
+ int computedWidth = getPaddingLeft() + mHorizontalAxis.getMin() + getPaddingRight();
+ int computedHeight = getPaddingTop() + mVerticalAxis.getMin() + getPaddingBottom();
setMeasuredDimension(
resolveSizeAndState(computedWidth, widthSpec, 0),
@@ -662,41 +750,54 @@ public class GridLayout extends ViewGroup {
}
private int protect(int alignment) {
- return alignment == UNDEFINED ? 0 : 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) {
+ return horizontal ? c.getMeasuredWidth() : c.getMeasuredHeight();
}
- 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;
+ private int getMeasurementIncludingMargin(View c, boolean horizontal, int measurementType) {
+ int result = getMeasurement(c, horizontal, measurementType);
+ if (mMarginsIncludedInAlignment) {
+ int leadingMargin = getMargin(c, true, horizontal);
+ int trailingMargin = getMargin(c, false, horizontal);
+ return result + leadingMargin + trailingMargin;
}
+ return result;
+ }
- // 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;
+ private int getAlignmentValue(Alignment alignment, View c, int dim, boolean horizontal, View c1) {
+ int result = alignment.getAlignmentValue(c, dim);
+ if (mMarginsIncludedInAlignment) {
+ int leadingMargin = getMargin(c1, true, horizontal);
+ return result + leadingMargin;
}
return result;
}
+ @Override
+ public void requestLayout() {
+ super.requestLayout();
+ invalidateValues();
+ }
+
// Layout container
+ /**
+ * {@inheritDoc}
+ */
+ /*
+ The layout operation is implemented by delegating the heavy lifting to the
+ to the mHorizontalAxis and mVerticalAxis instances of the internal Axis class.
+ Together they compute the locations of the vertical and horizontal lines of
+ the grid (respectively!).
+
+ This method is then left with the simpler task of applying margins, gravity
+ and sizing to each child view and then placing it in its cell.
+ */
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
- invalidateValues();
-
int targetWidth = r - l;
int targetHeight = b - t;
@@ -710,33 +811,43 @@ public class GridLayout extends ViewGroup {
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;
+ LayoutParams lp = getLayoutParams(view);
+ Group columnGroup = lp.columnGroup;
+ Group rowGroup = lp.rowGroup;
+
+ Interval colSpan = columnGroup.span;
+ Interval rowSpan = rowGroup.span;
- int x1 = getLocationIncludingMargin(mHorizontalAxis, hRange.min, true);
- int y1 = getLocationIncludingMargin(mVerticalAxis, vRange.min, true);
+ int x1 = mHorizontalAxis.getLocationIncludingMargin(view, true, colSpan.min);
+ int y1 = mVerticalAxis.getLocationIncludingMargin(view, true, rowSpan.min);
- int x2 = getLocationIncludingMargin(mHorizontalAxis, hRange.max, false);
- int y2 = getLocationIncludingMargin(mVerticalAxis, vRange.max, false);
+ int x2 = mHorizontalAxis.getLocationIncludingMargin(view, false, colSpan.max);
+ int y2 = mVerticalAxis.getLocationIncludingMargin(view, false, rowSpan.max);
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;
+ Alignment hAlignment = columnGroup.alignment;
+ Alignment vAlignment = rowGroup.alignment;
+
+ int dx, dy;
- int ddx = protect(hAlignment.getAlignmentValue(null, cellWidth - minMaxX.size()));
- int ddy = protect(vAlignment.getAlignmentValue(null, cellHeight - minMaxY.size()));
+ if (mMarginsIncludedInAlignment) {
+ dx = protect(hAlignment.getAlignmentValue(view, cellWidth - pWidth));
+ dy = protect(vAlignment.getAlignmentValue(view, cellHeight - pHeight));
+ } else {
+ Bounds colBounds = mHorizontalAxis.getGroupBounds().getValue(i);
+ Bounds rowBounds = mVerticalAxis.getGroupBounds().getValue(i);
- int dx = ddx + -minMaxX.below - hAlignment.getAlignmentValue(view, pWidth);
- int dy = ddy + -minMaxY.below - vAlignment.getAlignmentValue(view, pHeight);
+ int mx = protect(hAlignment.getAlignmentValue(null, cellWidth - colBounds.size()));
+ int my = protect(vAlignment.getAlignmentValue(null, cellHeight - rowBounds.size()));
+
+ dx = mx + -colBounds.below - hAlignment.getAlignmentValue(view, pWidth);
+ dy = my + -rowBounds.below - vAlignment.getAlignmentValue(view, pHeight);
+ }
int width = hAlignment.getSizeInCell(view, pWidth, cellWidth);
int height = vAlignment.getSizeInCell(view, pHeight, cellHeight);
@@ -749,9 +860,14 @@ public class GridLayout extends ViewGroup {
// Inner classes
+ /*
+ This internal class houses the algorithm for computing the locations of grid lines;
+ along either the horizontal or vertical axis. A GridLayout uses two instances of this class -
+ distinguished by the "horizontal" flag which is true for the horizontal axis and false
+ for the vertical one.
+ */
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;
@@ -766,20 +882,25 @@ public class GridLayout extends ViewGroup {
PackedMap<Group, Bounds> groupBounds;
public boolean groupBoundsValid = false;
- PackedMap<Interval, Int> spanSizes;
+ PackedMap<Interval, MutableInt> spanSizes;
public boolean spanSizesValid = false;
- public int[] locations;
-
public int[] leadingMargins;
+ public boolean leadingMarginsValid = false;
+
public int[] trailingMargins;
+ public boolean trailingMarginsValid = false;
public Arc[] arcs;
public boolean arcsValid = false;
- private boolean mOrderPreserved = DEFAULT_ORDER_PRESERVED;
+ public int[] minima;
+ public boolean minimaValid = false;
- public int prefSizeOfWeightedComponent;
+ public float[] weights;
+ public int[] locations;
+
+ private boolean mOrderPreserved = DEFAULT_ORDER_PRESERVED;
private Axis(boolean horizontal) {
this.horizontal = horizontal;
@@ -844,17 +965,17 @@ public class GridLayout extends ViewGroup {
for (int i = 0; i < groupBounds.values.length; i++) {
groupBounds.values[i].reset();
}
- for (int i = 0, size = getChildCount(); i < size; i++) {
+ for (int i = 0, N = getChildCount(); i < N; 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);
+
+ int size = getMeasurementIncludingMargin(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);
+ int below = getAlignmentValue(g.alignment, c, size, horizontal, c);
+ bounds.include(-below, size - below);
}
}
@@ -870,36 +991,36 @@ public class GridLayout extends ViewGroup {
}
// Add values computed by alignment - taking the max of all alignments in each span
- private PackedMap<Interval, Int> createSpanSizes() {
+ private PackedMap<Interval, MutableInt> createSpanSizes() {
PackedMap<Group, Bounds> groupBounds = getGroupBounds();
int N = groupBounds.keys.length;
Interval[] spans = new Interval[N];
- Int[] values = new Int[N];
+ MutableInt[] values = new MutableInt[N];
for (int i = 0; i < N; i++) {
Interval key = groupBounds.keys[i].span;
spans[i] = key;
- values[i] = new Int();
+ values[i] = new MutableInt();
}
- return new PackedMap<Interval, Int>(spans, values);
+ return new PackedMap<Interval, MutableInt>(spans, values);
}
private void computeSpanSizes() {
- Int[] spans = spanSizes.values;
+ MutableInt[] 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
+ Bounds[] bounds = getGroupBounds().values; // use getter to trigger a re-evaluation
for (int i = 0; i < bounds.length; i++) {
int value = bounds[i].size();
- Int valueHolder = spanSizes.getValue(i);
+ MutableInt valueHolder = spanSizes.getValue(i);
valueHolder.value = max(valueHolder.value, value);
}
}
- private PackedMap<Interval, Int> getSpanSizes() {
+ private PackedMap<Interval, MutableInt> getSpanSizes() {
if (spanSizes == null) {
spanSizes = createSpanSizes();
}
@@ -910,9 +1031,7 @@ public class GridLayout extends ViewGroup {
return spanSizes;
}
- private void include(List<Arc> arcs, Interval key, Int size, boolean maximizing) {
- key = maximizing ? key.inverse() : key;
- size = maximizing ? size.neg() : size;
+ private void include(List<Arc> arcs, Interval key, MutableInt 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) {
@@ -924,22 +1043,22 @@ public class GridLayout extends ViewGroup {
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);
+ private void include2(List<Arc> arcs, Interval span, MutableInt min, MutableInt max,
+ boolean both) {
+ include(arcs, span, min);
if (both) {
- include(arcs, span.inverse(), max.neg(), maximizing);
+ // todo
+// include(arcs, span.inverse(), max.neg());
}
}
- 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);
+ private void include2(List<Arc> arcs, Interval span, int min, int max, boolean both) {
+ include2(arcs, span, new MutableInt(min), new MutableInt(max), both);
}
- // Group arcs by their first index, returning an array of arrays.
+ // Group arcs by their first vertex, returning an array of arrays.
// This is linear in the number of arcs.
- private Arc[][] index(Arc[] arcs) {
+ private Arc[][] groupArcsByFirstVertex(Arc[] arcs) {
int N = getCount() + 1;// the number of vertices
Arc[][] result = new Arc[N][];
int[] sizes = new int[N];
@@ -959,18 +1078,21 @@ public class GridLayout extends ViewGroup {
return result;
}
- // todo do we always add first element?
- private Arc[] sort(final Arc[] arcs, int start) {
+ /*
+ Topological sort.
+ */
+ private Arc[] topologicalSort(final Arc[] arcs, int start) {
+ // todo ensure the <start> vertex is added in edge cases
final List<Arc> result = new ArrayList<Arc>();
new Object() {
- Arc[][] index = index(arcs);
+ Arc[][] arcsByFirstVertex = groupArcsByFirstVertex(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]) {
+ for (Arc arc : arcsByFirstVertex[loc]) {
Interval span = arc.span;
// the recursive call
if (completesCycle(span.max)) {
@@ -1006,7 +1128,7 @@ public class GridLayout extends ViewGroup {
return result;
}
- // todo unify with findUsed above
+ // todo unify with findUsed above. Both routines analyze which rows/columns are empty.
private Collection<Interval> getSpacers() {
List<Interval> result = new ArrayList<Interval>();
int N = getCount() + 1;
@@ -1038,16 +1160,16 @@ public class GridLayout extends ViewGroup {
return result;
}
- private Arc[] createArcs(boolean maximizing) {
+ private Arc[] createArcs() {
List<Arc> spanToSize = new ArrayList<Arc>();
// Add all the preferred elements that were not defined by the user.
- PackedMap<Interval, Int> spanSizes = getSpanSizes();
+ PackedMap<Interval, MutableInt> spanSizes = getSpanSizes();
for (int i = 0; i < spanSizes.keys.length; i++) {
Interval key = spanSizes.keys[i];
- Int value = spanSizes.values[i];
+ MutableInt value = spanSizes.values[i];
// todo remove value duplicate
- include2(spanToSize, key, value, value, accommodateBothMinAndMax, maximizing);
+ include2(spanToSize, key, value, value, accommodateBothMinAndMax);
}
// Find redundant rows/cols and glue them together with 0-length arcs to link the tree
@@ -1055,8 +1177,8 @@ public class GridLayout extends ViewGroup {
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);
+ include(spanToSize, span, new MutableInt(0));
+ include(spanToSize, span.inverse(), new MutableInt(0));
}
}
@@ -1064,21 +1186,21 @@ public class GridLayout extends ViewGroup {
// Add preferred gaps
for (int i = 0; i < getCount(); i++) {
if (used[i]) {
- include2(spanToSize, new Interval(i, i + 1), 0, 0, false, maximizing);
+ include2(spanToSize, new Interval(i, i + 1), 0, 0, false);
}
}
} else {
for (Interval gap : getSpacers()) {
- include2(spanToSize, gap, 0, 0, false, maximizing);
+ include2(spanToSize, gap, 0, 0, false);
}
}
Arc[] arcs = spanToSize.toArray(new Arc[spanToSize.size()]);
- return sort(arcs, maximizing ? getCount() : 0);
+ return topologicalSort(arcs, 0);
}
- public Arc[] getArcs(boolean maximizing) {
+ public Arc[] getArcs() {
if (arcs == null) {
- arcs = createArcs(maximizing);
+ arcs = createArcs();
}
if (!arcsValid) {
getSpanSizes();
@@ -1087,21 +1209,54 @@ public class GridLayout extends ViewGroup {
return arcs;
}
- private boolean relax(int[] locations, Arc entry, boolean maximizing) {
+ private boolean relax(int[] locations, Arc entry) {
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]) {
+ if (candidate > locations[v]) {
locations[v] = candidate;
return true;
}
return false;
}
- // Bellman-Ford variant
- private int[] solve(Arc[] arcs, int[] locations, boolean maximizing) {
+ /*
+ Bellman-Ford variant - modified to reduce typical running time from O(N^2) to O(N)
+
+ GridLayout converts its requirements into a system of linear constraints of the
+ form:
+
+ x[i] - x[j] < a[k]
+
+ Where the x[i] are variables and the a[k] are constants.
+
+ For example, if the variables were instead labeled x, y, z we might have:
+
+ x - y < 17
+ y - z < 23
+ z - x < 42
+
+ This is a special case of the Linear Programming problem that is, in turn,
+ equivalent to the single-source shortest paths problem on a digraph, for
+ which the O(n^2) Bellman-Ford algorithm the most commonly used general solution.
+
+ Other algorithms are faster in the case where no arcs have negative weights
+ but allowing negative weights turns out to be the same as accommodating maximum
+ size requirements as well as minimum ones.
+
+ Bellman-Ford works by iteratively 'relaxing' constraints over all nodes (an O(N)
+ process) and performing this step N times. Proof of correctness hinges on the
+ fact that there can be no negative weight chains of length > N - unless a
+ 'negative weight loop' exists. The algorithm catches this case in a final
+ checking phase that reports failure.
+
+ By topologically sorting the nodes and checking this condition at each step
+ typical layout problems complete after the first iteration and the algorithm
+ completes in O(N) steps with very low constants.
+ */
+ private int[] solve(Arc[] arcs, int[] locations) {
int N = getCount() + 1; // The number of vertices is the number of columns/rows + 1.
boolean changed = false;
@@ -1109,11 +1264,11 @@ public class GridLayout extends ViewGroup {
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);
+ changed = changed | relax(locations, arcs[j]);
}
if (!changed) {
if (DEBUG) {
- Log.d(TAG, "Iteration " + (maximizing ? "(max)" : "(min)") +
+ Log.d(TAG, "Iteration " +
" completed after " + (1 + i) + " steps out of " + N);
}
break;
@@ -1125,119 +1280,158 @@ public class GridLayout extends ViewGroup {
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];
+ private void computeMargins(boolean leading) {
+ int[] margins = leading ? leadingMargins : trailingMargins;
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));
+ margins[index] = max(margins[index], getMargin(c, leading, horizontal));
}
- return result;
}
- // has side effects
- private void computeLocations(int[] locations, boolean maximizing) {
- leadingMargins = computeMargins(true);
- trailingMargins = computeMargins(false);
+ private int[] getLeadingMargins() {
+ if (leadingMargins == null) {
+ leadingMargins = new int[getCount() + 1];
+ }
+ if (!leadingMarginsValid) {
+ computeMargins(true);
+ leadingMarginsValid = true;
+ }
+ return leadingMargins;
+ }
+
+ private int[] getTrailingMargins() {
+ if (trailingMargins == null) {
+ trailingMargins = new int[getCount() + 1];
+ }
+ if (!trailingMarginsValid) {
+ computeMargins(false);
+ trailingMarginsValid = true;
+ }
+ return trailingMargins;
+ }
- solve(getArcs(maximizing), locations, maximizing);
+ private void addMargins() {
+ int[] leadingMargins = getLeadingMargins();
+ int[] trailingMargins = getTrailingMargins();
- // Add margins
int delta = 0;
- for (int i = 0; i < getCount(); i++) {
+ for (int i = 0, N = getCount(); i < N; i++) {
int margins = leadingMargins[i] + trailingMargins[i + 1];
delta += margins;
- locations[i + 1] += delta;
+ minima[i + 1] += delta;
}
}
- private int size(int[] locations) {
- return locations[locations.length - 1] - locations[0];
+ private int getLocationIncludingMargin(View view, boolean leading, int index) {
+ int location = locations[index];
+ int margin;
+ if (!mMarginsIncludedInAlignment) {
+ margin = (leading ? leadingMargins : trailingMargins)[index];
+ } else {
+ margin = getMargin(view, leading, horizontal);
+ }
+ return leading ? (location + margin) : (location - margin);
}
- 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;
+ private void computeMinima(int[] a) {
+ Arrays.fill(a, MIN_VALUE);
+ a[0] = 0;
+ solve(getArcs(), a);
+ if (!mMarginsIncludedInAlignment) {
+ addMargins();
}
}
- // External entry points
+ private int[] getMinima() {
+ if (minima == null) {
+ int N = getCount() + 1;
+ minima = new int[N];
+ }
+ if (!minimaValid) {
+ computeMinima(minima);
+ minimaValid = true;
+ }
+ return minima;
+ }
- private int getMin() {
- int[] mins = getLimit(maximizing, maximizing);
- return size(mins);
+ private void computeWeights() {
+ for (int i = 0, N = getChildCount(); i < N; i++) {
+ LayoutParams lp = getLayoutParams(getChildAt(i));
+ Group g = horizontal ? lp.columnGroup : lp.rowGroup;
+ Interval span = g.span;
+ int penultimateIndex = span.max - 1;
+ weights[penultimateIndex] += horizontal ? lp.columnWeight : lp.rowWeight;
+ }
}
- private int getPref() {
- return accommodateBothMinAndMax ? getMax() : getMin();
+ private float[] getWeights() {
+ if (weights == null) {
+ int N = getCount() + 1;
+ weights = new float[N];
+ }
+ computeWeights();
+ return weights;
}
- private int getMax() {
- int[] maxs = getLimit(!maximizing, maximizing);
- return size(maxs);
+ private int[] getLocations() {
+ if (locations == null) {
+ int N = getCount() + 1;
+ locations = new int[N];
+ }
+ return locations;
}
- private int totalMarginSize() {
- return sum(leadingMargins) + sum(trailingMargins);
+ // External entry points
+
+ private int size(int[] locations) {
+ return locations[locations.length - 1] - locations[0];
+ }
+
+ private int getMin() {
+ return size(getMinima());
}
private void layout(int targetSize) {
- int N = getCount() + 1;
- int min = getMin();
- int max = getMax();
+ int[] mins = getMinima();
- int clippedTargetSize = max(min(max, targetSize), min); // confine size to valid range
+ int totalDelta = max(0, targetSize - size(mins)); // confine to expansion
- if (DEBUG) {
- Log.d(TAG, "Computing sizes for target " + clippedTargetSize + " for " +
- (horizontal ? "col" : "row") + "s from: " + arcs);
+ float[] weights = getWeights();
+ float totalWeight = sum(weights);
+
+ if (totalWeight == 0f) {
+ weights[weights.length - 1] = 1;
+ totalWeight = 1;
}
- 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));
+
+ int[] locations = getLocations();
+ int cumulativeDelta = 0;
+
+ for (int i = 0; i < locations.length; i++) {
+ float weight = weights[i];
+ int delta = (int) (totalDelta * weight / totalWeight);
+ cumulativeDelta += delta;
+ locations[i] = mins[i] + cumulativeDelta;
+
+ totalDelta -= delta;
+ totalWeight -= weight;
}
}
private void invalidateStructure() {
countValid = false;
+
groupBounds = null;
spanSizes = null;
+ leadingMargins = null;
+ trailingMargins = null;
+ minima = null;
+ weights = null;
+ locations = null;
invalidateValues();
}
@@ -1246,6 +1440,9 @@ public class GridLayout extends ViewGroup {
groupBoundsValid = false;
spanSizesValid = false;
arcsValid = false;
+ leadingMarginsValid = false;
+ trailingMarginsValid = false;
+ minimaValid = false;
}
}
@@ -1259,8 +1456,8 @@ public class GridLayout extends ViewGroup {
* {@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.
+ * The row and column groups contain the leading and trailing indices along each axis
+ * and 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
@@ -1277,19 +1474,19 @@ public class GridLayout extends ViewGroup {
* <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
+ * <code>false</code>; otherwise {@link #UNDEFINED}, 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
+ * <code>false</code>; otherwise {@link #UNDEFINED}, 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
+ * <code>false</code>; otherwise {@link #UNDEFINED}, 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
+ * <code>false</code>; otherwise {@link #UNDEFINED}, 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>
@@ -1324,7 +1521,8 @@ public class GridLayout extends ViewGroup {
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;
+ private static final int DEFAULT_WEIGHT_0 = 0;
+ private static final int DEFAULT_WEIGHT_1 = 1;
// Misc
@@ -1397,7 +1595,7 @@ public class GridLayout extends ViewGroup {
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);
+ rowGroup, columnGroup, DEFAULT_WEIGHT_0, DEFAULT_WEIGHT_0);
}
/**
@@ -1515,22 +1713,26 @@ public class GridLayout extends ViewGroup {
!definesVertical(gravity), defaultAlignment);
}
+ private int getDefaultWeight(int size) {
+ return (size == MATCH_PARENT) ? DEFAULT_WEIGHT_1 : DEFAULT_WEIGHT_0;
+ }
+
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 columnSpan = a.getInteger(COLUMN_SPAN, DEFAULT_SPAN_SIZE);
+ Interval hSpan = new Interval(column, column + columnSpan);
+ this.columnGroup = new Group(hSpan, getHorizontalAlignment(gravity, width));
+ this.columnWeight = a.getFloat(COLUMN_WEIGHT, getDefaultWeight(width));
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);
+ int rowSpan = a.getInteger(ROW_SPAN, DEFAULT_SPAN_SIZE);
+ Interval vSpan = new Interval(row, row + rowSpan);
+ this.rowGroup = new Group(vSpan, getVerticalAlignment(gravity, height));
+ this.rowWeight = a.getFloat(ROW_WEIGHT, getDefaultWeight(height));
} finally {
a.recycle();
}
@@ -1563,12 +1765,16 @@ public class GridLayout extends ViewGroup {
}
}
+ /*
+ In place of a HashMap from span to Int, use an array of key/value pairs - stored in Arcs.
+ Add the mutables completesCycle flag to avoid creating another hash table for detecting cycles.
+ */
private static class Arc {
public final Interval span;
- public final Int value;
+ public final MutableInt value;
public boolean completesCycle;
- public Arc(Interval span, Int value) {
+ public Arc(Interval span, MutableInt value) {
this.span = span;
this.value = value;
}
@@ -1581,27 +1787,36 @@ public class GridLayout extends ViewGroup {
// A mutable Integer - used to avoid heap allocation during the layout operation
- private static class Int {
+ private static class MutableInt {
public int value;
- private Int() {
+ private MutableInt() {
reset();
}
- private Int(int value) {
+ private MutableInt(int value) {
this.value = value;
}
private void reset() {
value = Integer.MIN_VALUE;
}
-
- private Int neg() {
- // this should never be called
- throw new UnsupportedOperationException();
- }
}
+ /*
+ This data structure is used in place of a Map where we have an index that refers to the order
+ in which each key/value pairs were added to the map. In this case we store keys and values
+ in arrays of a length that is equal to the number of unique keys. We also maintain an
+ array of indexes from insertion order to the compacted arrays of keys and values.
+
+ Note that behavior differs from that of a LinkedHashMap in that repeated entries
+ *do* get added multiples times. So the length of index is equals to the number of
+ items added.
+
+ This is useful in the GridLayout class where we can rely on the order of children not
+ changing during layout - to use integer-based lookup for our internal structures
+ rather than using (and storing) an implementation of Map<Key, ?>.
+ */
@SuppressWarnings(value = "unchecked")
private static class PackedMap<K, V> {
public final int[] index;
@@ -1611,8 +1826,8 @@ public class GridLayout extends ViewGroup {
private PackedMap(K[] keys, V[] values) {
this.index = createIndex(keys);
- this.keys = index(keys, index);
- this.values = index(values, index);
+ this.keys = compact(keys, index);
+ this.values = compact(values, index);
}
private K getKey(int i) {
@@ -1648,19 +1863,33 @@ public class GridLayout extends ViewGroup {
return result;
}
- private static <K> K[] index(K[] keys, int[] index) {
- int size = keys.length;
- Class<?> componentType = keys.getClass().getComponentType();
+ /*
+ Create a compact array of keys or values using the supplied index.
+ */
+ private static <K> K[] compact(K[] a, int[] index) {
+ int size = a.length;
+ Class<?> componentType = a.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];
+ result[index[i]] = a[i];
}
return result;
}
}
+ /*
+ For each Group (with a given alignment) we need to store the amount of space required
+ above the alignment point and the amount of space required below it. One side of this
+ calculation is always 0 for LEADING and TRAILING alignments but we don't make use of this.
+ For CENTER and BASELINE alignments both sides are needed and in the BASELINE case no
+ simple optimisations are possible.
+
+ The general algorithm therefore is to create a Map (actually a PackedMap) from
+ Group to Bounds and to loop through all Views in the group taking the maximum
+ of the values for each View.
+ */
private static class Bounds {
public int below;
public int above;
@@ -1708,11 +1937,12 @@ public class GridLayout extends ViewGroup {
* 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 {
+ /* package */ static class Interval {
/**
* The minimum value.
*/
public final int min;
+
/**
* The maximum value.
*/
@@ -1793,14 +2023,13 @@ public class GridLayout extends ViewGroup {
*/
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.
+ * The grid indices of the leading and trailing edges of this cell group for the
+ * appropriate axis.
* <p>
* See {@link GridLayout} for a description of the conventions used by GridLayout
* for grid indices.
*/
- public final Interval span;
+ /* package */ final Interval span;
/**
* Specifies how cells should be aligned in this group.
* For row groups, this specifies the vertical alignment.
@@ -1818,7 +2047,7 @@ public class GridLayout extends ViewGroup {
* @param span the span.
* @param alignment the alignment.
*/
- public Group(Interval span, Alignment alignment) {
+ /* package */ Group(Interval span, Alignment alignment) {
this.span = span;
this.alignment = alignment;
}
@@ -1861,7 +2090,7 @@ public class GridLayout extends ViewGroup {
}
/**
- * Returns true if the {@link #getClass class}, {@link #alignment} and {@link #span}
+ * Returns true if the {@link #getClass class}, {@link #alignment} and <code>span</code>
* properties of this Group and the supplied parameter are pairwise equal; false otherwise.
*
* @param that the object to compare this group with.
@@ -1898,9 +2127,6 @@ public class GridLayout extends ViewGroup {
}
}
- // Alignments
-
-
/**
* Alignments specify where a view should be placed within a cell group and
* what size it should be.
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index b3520da..9c55627 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -2522,10 +2522,10 @@
</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
+ allocate row and column parameters 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
+ depending on the value of this flag. In the horizontal 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. -->
@@ -2539,6 +2539,12 @@
The default value is false.
See {@link android.widget.GridLayout#setUseDefaultMargins(boolean)}.-->
<attr name="useDefaultMargins" format="boolean" />
+ <!-- When set to true, causes alignment to take place between the outer
+ boundary of a view, as defined by its margins. When set to false,
+ causes alignment to take place between the edges of the view.
+ The default is true.
+ See {@link android.widget.GridLayout#setMarginsIncludedInAlignment(boolean)}.-->
+ <attr name="marginsIncludedInAlignment" format="boolean" />
<!-- When set to true, forces row boundaries to appear in the same order
as row indices.
The default is false.
@@ -3256,9 +3262,9 @@
<!-- 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}. -->
+ See {@link android.widget.GridLayout.Group}. -->
<attr name="layout_rowSpan" format="integer" min="1" />
- <!-- A number indicating the relative proportion of availible space that
+ <!-- A number indicating the relative proportion of available space that
should be taken by this group of cells.
The default is zero.
See {@link android.widget.GridLayout.LayoutParams#columnWeight}. -->
@@ -3269,9 +3275,9 @@
<!-- 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}. -->
+ See {@link android.widget.GridLayout.Group}. -->
<attr name="layout_columnSpan" format="integer" min="1" />
- <!-- A number indicating the relative proportion of availible space that
+ <!-- A number indicating the relative proportion of available space that
should be taken by this group of cells.
The default is zero.
See {@link android.widget.GridLayout.LayoutParams#columnWeight}.-->
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 6cdb957..4eb59a5 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1684,6 +1684,7 @@
<public type="attr" name="columnCount" />
<public type="attr" name="columnOrderPreserved" />
<public type="attr" name="useDefaultMargins" />
+ <public type="attr" name="marginsIncludedInAlignment" />
<public type="attr" name="layout_row" />
<public type="attr" name="layout_rowSpan" />
diff --git a/tests/GridLayoutTest/AndroidManifest.xml b/tests/GridLayoutTest/AndroidManifest.xml
index 53ca4ce..1b72357 100644
--- a/tests/GridLayoutTest/AndroidManifest.xml
+++ b/tests/GridLayoutTest/AndroidManifest.xml
@@ -76,6 +76,27 @@
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
+
+ <activity android:name="AlignmentTest" android:label="AlignmentTest">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+
+ <activity android:name="LinearLayoutTest" android:label="LinearLayoutTest">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+
+ <activity android:name="GridLayoutTest" android:label="GridLayoutTest">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
</application>
diff --git a/tests/GridLayoutTest/src/com/android/test/layout/AbstractLayoutTest.java b/tests/GridLayoutTest/src/com/android/test/layout/AbstractLayoutTest.java
new file mode 100644
index 0000000..937eacb
--- /dev/null
+++ b/tests/GridLayoutTest/src/com/android/test/layout/AbstractLayoutTest.java
@@ -0,0 +1,71 @@
+/*
+ * 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.os.Debug;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+
+public abstract class AbstractLayoutTest extends Activity {
+
+ public static final String[] HORIZONTAL_NAMES = new String[] { "LEFT", "center", "east", "fill" };
+ public static final int[] HORIZONTAL_ALIGNMENTS = new int[] { Gravity.LEFT, Gravity.CENTER, Gravity.RIGHT, Gravity.FILL };
+ public static final String[] VERTICAL_NAMES = new String[] { "north", "center", "baseline", "south", "fill" };
+ public static final int[] VERTICAL_ALIGNMENTS = new int[] { Gravity.TOP, Gravity.CENTER, Gravity.NO_GRAVITY, Gravity.BOTTOM, Gravity.FILL };
+
+ public View create(Context context, String name, int size) {
+ Button result = new Button(context);
+ result.setText(name);
+ result.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ animate(v);
+ }
+ });
+ return result;
+ }
+
+ public abstract ViewGroup create(Context context);
+ public abstract String tag();
+
+ public void animate(View v) {
+ long start = System.currentTimeMillis();
+ int N = 1000;
+ for (int i = 0; i < N; i++) {
+ ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) v.getLayoutParams();
+ lp.topMargin = (lp.topMargin + 1) % 31;
+ lp.leftMargin = (lp.leftMargin + 1) % 31;
+
+ v.requestLayout();
+ v.invalidate();
+ ViewGroup p = (ViewGroup) v.getParent();
+ p.layout(0, 0, 1000 + (i % 2), 500 + (i % 2));
+ }
+ Log.d(tag(), "Time: " + (float) (System.currentTimeMillis() - start) / N * 1000 + "mics");
+ }
+
+ 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/Activity2.java b/tests/GridLayoutTest/src/com/android/test/layout/Activity2.java
index 6359903..5e29cf1 100644
--- a/tests/GridLayoutTest/src/com/android/test/layout/Activity2.java
+++ b/tests/GridLayoutTest/src/com/android/test/layout/Activity2.java
@@ -32,6 +32,7 @@ public class Activity2 extends Activity {
public static View create(Context context) {
GridLayout vg = new GridLayout(context);
vg.setUseDefaultMargins(true);
+ vg.setMarginsIncludedInAlignment(false);
Group row1 = new Group(1, CENTER);
Group row2 = new Group(2, CENTER);
diff --git a/tests/GridLayoutTest/src/com/android/test/layout/AlignmentTest.java b/tests/GridLayoutTest/src/com/android/test/layout/AlignmentTest.java
new file mode 100755
index 0000000..c6f390e
--- /dev/null
+++ b/tests/GridLayoutTest/src/com/android/test/layout/AlignmentTest.java
@@ -0,0 +1,116 @@
+/*
+ * 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.view.ViewGroup;
+import android.view.ViewParent;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.GridLayout;
+import android.widget.TextView;
+
+import static android.widget.GridLayout.*;
+
+public class AlignmentTest extends Activity {
+
+ public static final String[] HORIZONTAL_NAMES = new String[]{"LEFT", "center", "east", "fill"};
+ public static final Alignment[] HORIZONTAL_ALIGNMENTS = new Alignment[]{LEFT, CENTER, RIGHT, FILL};
+ public static final String[] VERTICAL_NAMES = new String[]{"north", "center", "baseline", "south", "fill"};
+ public static final Alignment[] VERTICAL_ALIGNMENTS = new Alignment[]{TOP, CENTER, BASELINE, BOTTOM, FILL};
+ private static Context CONTEXT;
+
+ public static interface ViewFactory {
+ View create(String name, int size);
+ }
+
+ public static final ViewFactory BUTTON_FACTORY = new ViewFactory() {
+ public View create(String name, int size) {
+ Button result = new Button(CONTEXT);
+ result.setText(name);
+ result.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ animate(v);
+ }
+ });
+ return result;
+ }
+ };
+
+ public static final ViewFactory LABEL_FACTORY = new ViewFactory() {
+ public View create(String name, int size) {
+ TextView result = new TextView(CONTEXT);
+ result.setText(name);
+ result.setTextSize(40);
+ return result;
+ }
+ };
+
+ public static final ViewFactory TEXT_FIELD_FACTORY = new ViewFactory() {
+ public View create(String name, int size) {
+ EditText result = new EditText(CONTEXT);
+ result.setText(name);
+ return result;
+ }
+ };
+
+ public static final ViewFactory[] FACTORIES = new ViewFactory[]{BUTTON_FACTORY, LABEL_FACTORY, TEXT_FIELD_FACTORY};
+
+ public static ViewGroup create(Context context1) {
+ CONTEXT = context1;
+ GridLayout container = new GridLayout(context1);
+ container.setUseDefaultMargins(true);
+
+ for (int i = 0; i < VERTICAL_ALIGNMENTS.length; i++) {
+ Alignment va = VERTICAL_ALIGNMENTS[i];
+ for (int j = 0; j < HORIZONTAL_ALIGNMENTS.length; j++) {
+ Alignment ha = HORIZONTAL_ALIGNMENTS[j];
+ Group rowGroup = new Group(i, va);
+ Group colGroup = new Group(j, ha);
+ LayoutParams layoutParams = new LayoutParams(rowGroup, colGroup);
+ container.addView(FACTORIES[(i + j) % FACTORIES.length].create(VERTICAL_NAMES[i] + "-" + HORIZONTAL_NAMES[j], 20), layoutParams);
+ }
+ }
+
+ return container;
+ }
+
+ public static void animate(View v) {
+
+ long start = System.currentTimeMillis();
+ int N = 1000;
+ for (int i = 0; i < N; i++) {
+ ViewGroup.LayoutParams lp = v.getLayoutParams();
+ lp.width += 1; // width;
+ lp.height += 1; // height;
+ v.requestLayout();
+ GridLayout p = (GridLayout) v.getParent();
+ p.layout(0, 0, 1000 + (i % 2), 500 + (i % 2));
+ }
+ System.out.println("Time: " + (float) (System.currentTimeMillis() - start) / N * 1000 + "mics");
+ }
+
+ 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/GridLayoutTest.java b/tests/GridLayoutTest/src/com/android/test/layout/GridLayoutTest.java
new file mode 100644
index 0000000..b4451e8
--- /dev/null
+++ b/tests/GridLayoutTest/src/com/android/test/layout/GridLayoutTest.java
@@ -0,0 +1,52 @@
+/*
+ * 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.content.Context;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.GridLayout;
+
+import static android.widget.GridLayout.*;
+
+public class GridLayoutTest extends AbstractLayoutTest {
+ public ViewGroup create(Context context) {
+ GridLayout container = new GridLayout(context);
+ container.setOrientation(VERTICAL);
+// container.setUseDefaultMargins(true);
+
+ for (int i = 0; i < VERTICAL_ALIGNMENTS.length; i++) {
+ int va = VERTICAL_ALIGNMENTS[i];
+ for (int j = 0; j < HORIZONTAL_ALIGNMENTS.length; j++) {
+ int ha = HORIZONTAL_ALIGNMENTS[j];
+ GridLayout.Group rowGroup = new GridLayout.Group(UNDEFINED, null);
+ GridLayout.Group colGroup = new GridLayout.Group(UNDEFINED, null);
+ GridLayout.LayoutParams lp = new GridLayout.LayoutParams(rowGroup, colGroup);
+ lp.setGravity(va | ha);
+// View v = create(VERTICAL_NAMES[i] + "-" + HORIZONTAL_NAMES[j], 20);
+ View v = create(context, VERTICAL_NAMES[i] + "-" + HORIZONTAL_NAMES[j], 20);
+ container.addView(v, lp);
+ }
+ }
+
+ return container;
+ }
+
+ public String tag() {
+ return GridLayoutTest.class.getName();
+ }
+}
diff --git a/tests/GridLayoutTest/src/com/android/test/layout/LinearLayoutTest.java b/tests/GridLayoutTest/src/com/android/test/layout/LinearLayoutTest.java
new file mode 100644
index 0000000..fbd1239
--- /dev/null
+++ b/tests/GridLayoutTest/src/com/android/test/layout/LinearLayoutTest.java
@@ -0,0 +1,50 @@
+/*
+ * 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.content.Context;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+
+import static android.widget.LinearLayout.*;
+
+public class LinearLayoutTest extends AbstractLayoutTest {
+ public ViewGroup create(Context context) {
+ LinearLayout container = new LinearLayout(context);
+ container.setOrientation(LinearLayout.VERTICAL);
+// container.setUseDefaultMargins(true);
+
+ for (int i = 0; i < VERTICAL_ALIGNMENTS.length; i++) {
+ int va = VERTICAL_ALIGNMENTS[i];
+ for (int j = 0; j < HORIZONTAL_ALIGNMENTS.length; j++) {
+ int ha = HORIZONTAL_ALIGNMENTS[j];
+ LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
+ lp.gravity = va | ha;
+// View v = create(VERTICAL_NAMES[i] + "-" + HORIZONTAL_NAMES[j], 20);
+ View v = create(context, VERTICAL_NAMES[i] + "-" + HORIZONTAL_NAMES[j], 20);
+ container.addView(v, lp);
+ }
+ }
+
+ return container;
+ }
+
+ public String tag() {
+ return LinearLayoutTest.class.getName();
+ }
+}