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

Commit 87260844 authored by Philip Milne's avatar Philip Milne
Browse files

Support for weights in GridLayout

Change-Id: I31993cb7da331e0bcf404e858f2438a579093e7d
parent 567d3f46
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
@@ -36565,6 +36567,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" />