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

Commit 60041862 authored by Robyn Coultas's avatar Robyn Coultas
Browse files

Fixed incorrect child measurement

Bug: 9367918

The measurement specification EllipsizeLayout gave the child views
did not cause the child to return the width it could use, but instead
it just returned the value it had in the original layout measurement
cycle.

Change-Id: I46d5a8e6582eb21725251376eccf57fac57d4817
parent 94a266ef
Loading
Loading
Loading
Loading
+70 −19
Original line number Diff line number Diff line
@@ -7,13 +7,33 @@ import android.widget.LinearLayout;
import android.widget.TextView;

/**
 * When this layout is in the Horizontal orientation and one and only one child
 * is a TextView with a non-null android:ellipsize, this layout will reduce
 * android:maxWidth of that TextView to ensure the other children are within the
 * layout. This layout has no effect if the children have weights.
 * When this layout is in the Horizontal orientation and one and only one child is a TextView with a
 * non-null android:ellipsize, this layout will reduce android:maxWidth of that TextView to ensure
 * the siblings are not truncated. This class is useful when that ellipsize-text-view "starts"
 * before other children of this view group. This layout has no effect if:
 * <ul>
 *     <li>the orientation is not horizontal</li>
 *     <li>any child has weights.</li>
 *     <li>more than one child has a non-null android:ellipsize.</li>
 * </ul>
 *
 * <p>The purpose of this horizontal-linear-layout is to ensure that when the sum of widths of the
 * children are greater than this parent, the maximum width of the ellipsize-text-view, is reduced
 * so that no siblings are truncated.</p>
 *
 * <p>For example: Given Text1 has android:ellipsize="end" and Text2 has android:ellipsize="none",
 * as Text1 and/or Text2 grow in width, both will consume more width until Text2 hits the end
 * margin, then Text1 will cease to grow and instead shrink to accommodate any further growth in
 * Text2.</p>
 * <ul>
 * <li>|[text1]|[text2]              |</li>
 * <li>|[text1 text1]|[text2 text2]  |</li>
 * <li>|[text...]|[text2 text2 text2]|</li>
 * </ul>
 */
public class EllipsizeLayout extends LinearLayout {

    @SuppressWarnings("unused")
    public EllipsizeLayout(Context context) {
        this(context, null);
    }
@@ -22,49 +42,80 @@ public class EllipsizeLayout extends LinearLayout {
        super(context, attrs);
    }

    /**
     * This override only acts when the LinearLayout is in the Horizontal orientation and is in it's
     * final measurement pass(MeasureSpec.EXACTLY). In this case only, this class
     * <ul>
     *     <li>Identifies the one TextView child with the non-null android:ellipsize.</li>
     *     <li>Re-measures the needed width of all children (by calling measureChildWithMargins with
     *     the width measure specification to MeasureSpec.UNSPECIFIED.)</li>
     *     <li>Sums the children's widths.</li>
     *     <li>Whenever the sum of the children's widths is greater than this parent was allocated,
     *     the maximum width of the one TextView child with the non-null android:ellipsize is
     *     reduced.</li>
     * </ul>
     *
     * @param widthMeasureSpec horizontal space requirements as imposed by the parent
     * @param heightMeasureSpec vertical space requirements as imposed by the parent
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        if (getOrientation() == HORIZONTAL
                && (MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY)) {
            int totalLength = 0;
            // If any of the constraints of this class are exceeded, outOfSpec becomes true
            // and the no alterations are made to the ellipsize-text-view.
            boolean outOfSpec = false;
            TextView ellipView = null;
            TextView ellipsizeView = null;
            final int count = getChildCount();
            final int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
            final int queryWidthMeasureSpec = MeasureSpec.
                    makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.UNSPECIFIED);

            for (int ii = 0; ii < count && !outOfSpec; ++ii) {
                final View child = getChildAt(ii);
                if (child != null && child.getVisibility() != GONE) {
                    // Identify the ellipsize view
                    if (child instanceof TextView) {
                        final TextView tv = (TextView) child;
                        if (tv.getEllipsize() != null) {
                            if (ellipView == null) {
                                ellipView = tv;
                                // clear maxWidth on mEllipView before measure
                                ellipView.setMaxWidth(Integer.MAX_VALUE);
                            if (ellipsizeView == null) {
                                ellipsizeView = tv;
                                // Clear the maximum width on ellipsizeView before measurement
                                ellipsizeView.setMaxWidth(Integer.MAX_VALUE);
                            } else {
                                // TODO: support multiple android:ellipsize
                                outOfSpec = true;
                            }
                        }
                    }
                    final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child
                            .getLayoutParams();
                    outOfSpec |= (lp.weight > 0f);
                    measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
                    totalLength += child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;
                    // Ask the child to measure itself
                    measureChildWithMargins(child, queryWidthMeasureSpec, 0, heightMeasureSpec, 0);

                    // Get the layout parameters to check for a weighted width and to add the
                    // child's margins to the total length.
                    final LinearLayout.LayoutParams layoutParams =
                            (LinearLayout.LayoutParams) child.getLayoutParams();
                    if (layoutParams != null) {
                        outOfSpec |= (layoutParams.weight > 0f);
                        totalLength += child.getMeasuredWidth()
                                + layoutParams.leftMargin + layoutParams.rightMargin;
                    } else {
                        outOfSpec = true;
                    }
                }
            }
            outOfSpec |= (ellipView == null) || (totalLength == 0);
            final int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
            // Last constraint test
            outOfSpec |= (ellipsizeView == null) || (totalLength == 0);

            if (!outOfSpec && totalLength > parentWidth) {
                int maxWidth = ellipView.getMeasuredWidth() - (totalLength - parentWidth);
                int maxWidth = ellipsizeView.getMeasuredWidth() - (totalLength - parentWidth);
                // TODO: Respect android:minWidth (easy with @TargetApi(16))
                int minWidth = 0;
                final int minWidth = 0;
                if (maxWidth < minWidth) {
                    maxWidth = minWidth;
                }
                ellipView.setMaxWidth(maxWidth);
                ellipsizeView.setMaxWidth(maxWidth);
            }
        }