Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5a4a3e93 authored by Philip Milne's avatar Philip Milne Committed by Android (Google) Code Review
Browse files

Merge "Support for weights in GridLayout"

parents 1dfce0cf 87260844
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -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);
+176 −33
Original line number Diff line number Diff line
@@ -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);
    }

    /**
+6 −0
Original line number Diff line number Diff line
@@ -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)}. -->
+2 −0
Original line number Diff line number Diff line
@@ -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" />