Merge "Support for weights in GridLayout"

This commit is contained in:
Philip Milne
2014-05-23 01:29:07 +00:00
committed by Android (Google) Code Review
4 changed files with 190 additions and 33 deletions

View File

@@ -749,6 +749,7 @@ package android {
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 = 16843645; // 0x101037d
field public static final int layout_columnWeight = 16843868; // 0x101045c
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
@@ -760,6 +761,7 @@ package android {
field public static final int layout_marginTop = 16843000; // 0x10100f8
field public static final int layout_row = 16843643; // 0x101037b
field public static final int layout_rowSpan = 16843644; // 0x101037c
field public static final int layout_rowWeight = 16843867; // 0x101045b
field public static final int layout_scale = 16843155; // 0x1010193
field public static final int layout_span = 16843085; // 0x101014d
field public static final int layout_toEndOf = 16843704; // 0x10103b8
@@ -36576,6 +36578,10 @@ package android.widget {
method public void setRowCount(int);
method public void setRowOrderPreserved(boolean);
method public void setUseDefaultMargins(boolean);
method public static android.widget.GridLayout.Spec spec(int, int, android.widget.GridLayout.Alignment, float);
method public static android.widget.GridLayout.Spec spec(int, android.widget.GridLayout.Alignment, float);
method public static android.widget.GridLayout.Spec spec(int, int, float);
method public static android.widget.GridLayout.Spec spec(int, float);
method public static android.widget.GridLayout.Spec spec(int, int, android.widget.GridLayout.Alignment);
method public static android.widget.GridLayout.Spec spec(int, android.widget.GridLayout.Alignment);
method public static android.widget.GridLayout.Spec spec(int, int);

View File

@@ -104,14 +104,16 @@ import static java.lang.Math.min;
*
* <h4>Excess Space Distribution</h4>
*
* GridLayout's distribution of excess space is based on <em>priority</em>
* rather than <em>weight</em>.
* As of API 21, GridLayout's distribution of excess space accomodates the principle of weight.
* In the event that no weights are specified, the previous conventions are respected and
* columns and rows are taken as flexible if their views specify some form of alignment
* within their groups.
* <p>
* A child's ability to stretch is inferred from the alignment properties of
* its row and column groups (which are typically set by setting the
* {@link LayoutParams#setGravity(int) gravity} property of the child's layout parameters).
* If alignment was defined along a given axis then the component
* is taken as <em>flexible</em> in that direction. If no alignment was set,
* The flexibility of a view is therefore influenced by its alignment which is,
* in turn, typically defined by setting the
* {@link LayoutParams#setGravity(int) gravity} property of the child's layout parameters.
* If either a weight or alignment were defined along a given axis then the component
* is taken as <em>flexible</em> in that direction. If no weight or alignment was set,
* the component is instead assumed to be <em>inflexible</em>.
* <p>
* Multiple components in the same row or column group are
@@ -122,12 +124,16 @@ import static java.lang.Math.min;
* elements is flexible if <em>one</em> of its elements is flexible.
* <p>
* To make a column stretch, make sure all of the components inside it define a
* gravity. To prevent a column from stretching, ensure that one of the components
* in the column does not define a gravity.
* weight or a gravity. To prevent a column from stretching, ensure that one of the components
* in the column does not define a weight or a gravity.
* <p>
* When the principle of flexibility does not provide complete disambiguation,
* GridLayout's algorithms favour rows and columns that are closer to its <em>right</em>
* and <em>bottom</em> edges.
* and <em>bottom</em> edges. To be more precise, GridLayout treats each of its layout
* parameters as a constraint in the a set of variables that define the grid-lines along a
* given axis. During layout, GridLayout solves the constraints so as to return the unique
* solution to those constraints for which all variables are less-than-or-equal-to
* the corresponding value in any other valid solution.
*
* <h4>Interpretation of GONE</h4>
*
@@ -140,18 +146,6 @@ import static java.lang.Math.min;
* had never been added to it.
* These statements apply equally to rows as well as columns, and to groups of rows or columns.
*
* <h5>Limitations</h5>
*
* GridLayout does not provide support for the principle of <em>weight</em>, as defined in
* {@link LinearLayout.LayoutParams#weight}. In general, it is not therefore possible
* to configure a GridLayout to distribute excess space between multiple components.
* <p>
* Some common use-cases may nevertheless be accommodated as follows.
* To place equal amounts of space around a component in a cell group;
* use {@link #CENTER} alignment (or {@link LayoutParams#setGravity(int) gravity}).
* For complete control over excess space distribution in a row or column;
* use a {@link LinearLayout} subview to hold the components in the associated cell group.
* When using either of these techniques, bear in mind that cell groups may be defined to overlap.
* <p>
* See {@link GridLayout.LayoutParams} for a full description of the
* layout parameters used by GridLayout.
@@ -1693,8 +1687,74 @@ public class GridLayout extends ViewGroup {
return trailingMargins;
}
private void computeLocations(int[] a) {
private void solve(int[] a) {
solve(getArcs(), a);
}
private void resetDeltas() {
Bounds[] values = getGroupBounds().values;
for (int i = 0, N = values.length; i < N; i++) {
values[i].shareOfDelta = 0;
}
}
private boolean requiresWeightDistribution() {
Bounds[] values = getGroupBounds().values;
for (int i = 0, N = values.length; i < N; i++) {
if (values[i].weight != 0) {
return true;
}
}
return false;
}
private void shareOutDelta(int[] locations) {
PackedMap<Spec, Bounds> groupBounds = getGroupBounds();
Spec[] specs = groupBounds.keys;
Bounds[] bounds = groupBounds.values;
int totalDelta = 0;
float totalWeight = 0;
final int N = bounds.length;
for (int i = 0; i < N; i++) {
Spec spec = specs[i];
Bounds bound = bounds[i];
if (bound.weight != 0) {
int minSize = bound.size(true);
Interval span = spec.span;
int actualSize = locations[span.max] - locations[span.min];
int delta = actualSize - minSize;
totalDelta += delta;
totalWeight += bound.weight;
}
}
for (int i = 0; i < N; i++) {
Bounds bound = bounds[i];
float weight = bound.weight;
if (weight != 0) {
int delta = Math.round((weight * totalDelta / totalWeight));
bound.shareOfDelta = delta;
// the two adjustments below compensate for the rounding above
totalDelta -= delta;
totalWeight -= weight;
}
}
}
private void solveAndDistributeSpace(int[] a) {
resetDeltas();
solve(a);
if (requiresWeightDistribution()) {
shareOutDelta(a);
arcsValid = false;
forwardLinksValid = false;
backwardLinksValid = false;
solve(a);
}
}
private void computeLocations(int[] a) {
solveAndDistributeSpace(a);
if (!orderPreserved) {
// Solve returns the smallest solution to the constraint system for which all
// values are positive. One value is therefore zero - though if the row/col
@@ -1810,6 +1870,23 @@ public class GridLayout extends ViewGroup {
* 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>
* The weight property is also included in Spec and specifies the proportion of any
* excess space that is due to the enclosing row or column.
* GridLayout's model of flexibility and weight is broadly based on the physical properties of
* systems of mechanical springs. For example, a column in a GridLayout is modeled as
* if a set of springs were attached in parallel at their ends. Columns are therefore
* flexible only if and only if all views inside them are flexible. Similarly, the combined
* weight of a column is defined as the <em>minimum</em> of the weights of the components it
* contains. So, if any one component in a column of components has a weight of zero,
* the entire group has a weight of zero and will not receive any of the
* excess space that GridLayout distributes in its second
* (internal) layout pass. The default weight of a component is zero,
* reflecting inflexibility, but the default weight of a column (with nothing in it)
* is effectively infinite, reflecting the fact that a parallel group of
* springs is infinitely flexible when it contains no springs.
* <p>
* The above comments apply equally to rows and groups of rows and columns.
*
* <h4>WRAP_CONTENT and MATCH_PARENT</h4>
*
@@ -1851,9 +1928,11 @@ public class GridLayout extends ViewGroup {
* <li>{@link #rowSpec}<code>.row</code> = {@link #UNDEFINED} </li>
* <li>{@link #rowSpec}<code>.rowSpan</code> = 1 </li>
* <li>{@link #rowSpec}<code>.alignment</code> = {@link #BASELINE} </li>
* <li>{@link #rowSpec}<code>.weight</code> = 0 </li>
* <li>{@link #columnSpec}<code>.column</code> = {@link #UNDEFINED} </li>
* <li>{@link #columnSpec}<code>.columnSpan</code> = 1 </li>
* <li>{@link #columnSpec}<code>.alignment</code> = {@link #START} </li>
* <li>{@link #columnSpec}<code>.weight</code> = 0 </li>
* </ul>
*
* See {@link GridLayout} for a more complete description of the conventions
@@ -1861,8 +1940,10 @@ public class GridLayout extends ViewGroup {
*
* @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 {
@@ -1889,9 +1970,11 @@ public class GridLayout extends ViewGroup {
private static final int COLUMN = R.styleable.GridLayout_Layout_layout_column;
private static final int COLUMN_SPAN = R.styleable.GridLayout_Layout_layout_columnSpan;
private static final int COLUMN_WEIGHT = R.styleable.GridLayout_Layout_layout_columnWeight;
private static final int ROW = R.styleable.GridLayout_Layout_layout_row;
private static final int ROW_SPAN = R.styleable.GridLayout_Layout_layout_rowSpan;
private static final int ROW_WEIGHT = R.styleable.GridLayout_Layout_layout_rowWeight;
private static final int GRAVITY = R.styleable.GridLayout_Layout_layout_gravity;
@@ -2034,11 +2117,13 @@ public class GridLayout extends ViewGroup {
int column = a.getInt(COLUMN, DEFAULT_COLUMN);
int colSpan = a.getInt(COLUMN_SPAN, DEFAULT_SPAN_SIZE);
this.columnSpec = spec(column, colSpan, getAlignment(gravity, true));
float colWeight = a.getFloat(COLUMN_WEIGHT, Spec.DEFAULT_WEIGHT);
this.columnSpec = spec(column, colSpan, getAlignment(gravity, true), colWeight);
int row = a.getInt(ROW, DEFAULT_ROW);
int rowSpan = a.getInt(ROW_SPAN, DEFAULT_SPAN_SIZE);
this.rowSpec = spec(row, rowSpan, getAlignment(gravity, false));
float rowWeight = a.getFloat(ROW_WEIGHT, Spec.DEFAULT_WEIGHT);
this.rowSpec = spec(row, rowSpan, getAlignment(gravity, false), rowWeight);
} finally {
a.recycle();
}
@@ -2244,6 +2329,8 @@ public class GridLayout extends ViewGroup {
public int before;
public int after;
public int flexibility; // we're flexible iff all included specs are flexible
public float weight; // the min of the weights of the individual specs
public int shareOfDelta;
private Bounds() {
reset();
@@ -2253,6 +2340,7 @@ public class GridLayout extends ViewGroup {
before = Integer.MIN_VALUE;
after = Integer.MIN_VALUE;
flexibility = CAN_STRETCH; // from the above, we're flexible when empty
weight = Float.MAX_VALUE; // default is large => row/cols take all slack when empty
}
protected void include(int before, int after) {
@@ -2266,7 +2354,7 @@ public class GridLayout extends ViewGroup {
return MAX_SIZE;
}
}
return before + after;
return before + after + shareOfDelta;
}
protected int getOffset(GridLayout gl, View c, Alignment a, int size, boolean horizontal) {
@@ -2275,6 +2363,7 @@ public class GridLayout extends ViewGroup {
protected final void include(GridLayout gl, View c, Spec spec, Axis axis) {
this.flexibility &= spec.getFlexibility();
weight = Math.min(weight, spec.weight);
boolean horizontal = axis.horizontal;
int size = gl.getMeasurementIncludingMargin(c, horizontal);
Alignment alignment = gl.getAlignment(spec.alignment, horizontal);
@@ -2401,36 +2490,43 @@ public class GridLayout extends ViewGroup {
* <li>{@link #spec(int, int)}</li>
* <li>{@link #spec(int, Alignment)}</li>
* <li>{@link #spec(int, int, Alignment)}</li>
* <li>{@link #spec(int, float)}</li>
* <li>{@link #spec(int, int, float)}</li>
* <li>{@link #spec(int, Alignment, float)}</li>
* <li>{@link #spec(int, int, Alignment, float)}</li>
* </ul>
*
*/
public static class Spec {
static final Spec UNDEFINED = spec(GridLayout.UNDEFINED);
static final float DEFAULT_WEIGHT = 0;
final boolean startDefined;
final Interval span;
final Alignment alignment;
final float weight;
private Spec(boolean startDefined, Interval span, Alignment alignment) {
private Spec(boolean startDefined, Interval span, Alignment alignment, float weight) {
this.startDefined = startDefined;
this.span = span;
this.alignment = alignment;
this.weight = weight;
}
private Spec(boolean startDefined, int start, int size, Alignment alignment) {
this(startDefined, new Interval(start, start + size), alignment);
private Spec(boolean startDefined, int start, int size, Alignment alignment, float weight) {
this(startDefined, new Interval(start, start + size), alignment, weight);
}
final Spec copyWriteSpan(Interval span) {
return new Spec(startDefined, span, alignment);
return new Spec(startDefined, span, alignment, weight);
}
final Spec copyWriteAlignment(Alignment alignment) {
return new Spec(startDefined, span, alignment);
return new Spec(startDefined, span, alignment, weight);
}
final int getFlexibility() {
return (alignment == UNDEFINED_ALIGNMENT) ? INFLEXIBLE : CAN_STRETCH;
return (alignment == UNDEFINED_ALIGNMENT && weight == 0) ? INFLEXIBLE : CAN_STRETCH;
}
/**
@@ -2478,6 +2574,7 @@ public class GridLayout extends ViewGroup {
* <ul>
* <li> {@code spec.span = [start, start + size]} </li>
* <li> {@code spec.alignment = alignment} </li>
* <li> {@code spec.weight = weight} </li>
* </ul>
* <p>
* To leave the start index undefined, use the value {@link #UNDEFINED}.
@@ -2485,9 +2582,55 @@ public class GridLayout extends ViewGroup {
* @param start the start
* @param size the size
* @param alignment the alignment
* @param weight the weight
*/
public static Spec spec(int start, int size, Alignment alignment, float weight) {
return new Spec(start != UNDEFINED, start, size, alignment, weight);
}
/**
* Equivalent to: {@code spec(start, 1, alignment, weight)}.
*
* @param start the start
* @param alignment the alignment
* @param weight the weight
*/
public static Spec spec(int start, Alignment alignment, float weight) {
return spec(start, 1, alignment, weight);
}
/**
* Equivalent to: {@code spec(start, 1, default_alignment, weight)} -
* where {@code default_alignment} is specified in
* {@link android.widget.GridLayout.LayoutParams}.
*
* @param start the start
* @param size the size
* @param weight the weight
*/
public static Spec spec(int start, int size, float weight) {
return spec(start, size, UNDEFINED_ALIGNMENT, weight);
}
/**
* Equivalent to: {@code spec(start, 1, weight)}.
*
* @param start the start
* @param weight the weight
*/
public static Spec spec(int start, float weight) {
return spec(start, 1, weight);
}
/**
* Equivalent to: {@code spec(start, size, alignment, 0f)}.
*
* @param start the start
* @param size the size
* @param alignment the alignment
*/
public static Spec spec(int start, int size, Alignment alignment) {
return new Spec(start != UNDEFINED, start, size, alignment);
return spec(start, size, alignment, Spec.DEFAULT_WEIGHT);
}
/**

View File

@@ -4057,6 +4057,9 @@
The default is one.
See {@link android.widget.GridLayout.Spec}. -->
<attr name="layout_rowSpan" format="integer" min="1" />
<!-- The relative proportion of horizontal space that should be allocated to this view
during excess space distribution. -->
<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" />
@@ -4065,6 +4068,9 @@
The default is one.
See {@link android.widget.GridLayout.Spec}. -->
<attr name="layout_columnSpan" format="integer" min="1" />
<!-- The relative proportion of vertical space that should be allocated to this view
during excess space distribution. -->
<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)}. -->

View File

@@ -2178,6 +2178,8 @@
<public type="attr" name="contentInsetLeft" />
<public type="attr" name="contentInsetRight" />
<public type="attr" name="paddingMode" />
<public type="attr" name="layout_rowWeight" />
<public type="attr" name="layout_columnWeight" />
<public-padding type="dimen" name="l_resource_pad" end="0x01050010" />