【问题标题】:How can i span this text over multiple lines?我怎样才能跨越多行这个文本?
【发布时间】:2014-09-29 16:31:08
【问题描述】:

我很高兴地遇到了一段漂亮的代码,它可以动态测量 textView 框并相应地调整其中的文本大小。这一切都很好,但是它只对一行文本有用,我如何确保文本到达框的边缘后开始换行?

填充文本视图

public class FillTextView extends TextView {

// Scaling factor
private static final float VERTICAL_FONT_SCALING_FACTOR = 0.9f;

// Attributes
private Paint mTestPaint;

public FillTextView(Context context) {
    super(context);
    initialise();
}

public FillTextView(Context context, AttributeSet attrs) {
    super(context, attrs);
    initialise();
}

private void initialise() {
    mTestPaint = new Paint();
    mTestPaint.set(this.getPaint());
}

/*
 * Resize the font so the specified text fits in the text box assuming the
 * text box is the specified width.
 */
private void refitText(String text, int textWidth, int textHeight) {
    if (textHeight <= 0 || textWidth <= 0) {
        return;
    }

    // Find target height
    float targetTextSizeVertical = (textHeight - this.getPaddingTop() - this.getPaddingBottom()) * VERTICAL_FONT_SCALING_FACTOR;

    // Find target width
    float targetWidth = textWidth - this.getPaddingLeft() - this.getPaddingRight();

    float hi = 800;
    float lo = 2;
    final float threshold = 0.5f; // How close we have to be

    mTestPaint.set(this.getPaint());

    while ((hi - lo) > threshold) {
        float size = (hi + lo) / 2;
        mTestPaint.setTextSize(size);
        if (mTestPaint.measureText(text) >= targetWidth)
            hi = size; // too big
        else
            lo = size; // too small
    }
    float targetTextSizeHorizontal = lo;

    // Set the text size
    float targetTextSize = Math.min(targetTextSizeVertical, targetTextSizeHorizontal);

    this.setTextSize(TypedValue.COMPLEX_UNIT_PX, (int) targetTextSize);
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);

    int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
    int parentWidth = MeasureSpec.getSize(widthMeasureSpec);

    refitText(this.getText().toString(), parentWidth, parentHeight);
    this.setMeasuredDimension(parentWidth, parentHeight);
}

@Override
protected void onTextChanged(final CharSequence text, final int start, final int before, final int after) {
    refitText(text.toString(), this.getWidth(), this.getHeight());
}

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    if (h != oldh || w != oldw) {
        refitText(this.getText().toString(), w, h);
    }
}

}

【问题讨论】:

    标签: android dynamic textview scale


    【解决方案1】:

    经过一番彻底的搜索后,这里很管用

    public class FillTextView extends TextView {
    
    /** Our ellipsis string. */
    private static final String mEllipsis = "\u2026";
    
    /**
     * Upper bounds for text size.
     * This acts as a starting point for resizing.
     */
    private int mMaxTextSizePixels;
    
    /** Lower bounds for text size. */
    private int mMinTextSizePixels;
    
    /** TextView line spacing multiplier. */
    private float mLineSpacingMultiplier = 1.0f;
    
    /** TextView additional line spacing. */
    private float mLineSpacingExtra = 0.0f;
    
    /**
     * Default constructor override.
     *
     * @param context
     */
    public FillTextView(Context context) {
        this(context, null);
        initialise();
    }
    
    /**
     * Default constructor when inflating from XML file.
     *
     * @param context
     * @param attrs
     */
    public FillTextView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
        initialise();
    }
    
    /**
     * Default constructor override.
     *
     * @param context
     * @param attrs
     * @param defStyle
     */
    public FillTextView(
            Context context,
            AttributeSet attrs,
            int defStyle) {
        super(context, attrs, defStyle);
        initialise();
    }
    
    @Override
    protected void onLayout (
            boolean changed,
            int left,
            int top,
            int right,
            int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        resizeText();
    }
    
    @Override
    protected void onTextChanged(
            CharSequence text,
            int start,
            int lengthBefore,
            int lengthAfter) {
        super.onTextChanged(text, start, lengthBefore, lengthAfter);
        requestLayout();
    }
    
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
    
        if (w != oldw || h != oldh) {
            requestLayout();
        }
    }
    
    @Override
    public void setTextSize(float size) {
        setTextSize(TypedValue.COMPLEX_UNIT_SP, size);
    }
    
    @Override
    public void setTextSize(int unit, float size) {
        super.setTextSize(unit, size);
        mMaxTextSizePixels = (int) getTextSize();
        requestLayout();
    }
    
    @Override
    public void setLineSpacing(float add, float mult) {
        super.setLineSpacing(add, mult);
        mLineSpacingMultiplier = mult;
        mLineSpacingExtra = add;
        requestLayout();
    }
    
    @Override
    public void setEllipsize(TextUtils.TruncateAt where) {
        super.setEllipsize(where);
        requestLayout();
    }
    
    /**
     * Sets the lower text size limit and invalidates the view.
     *
     * @param minTextSizeScaledPixels the minimum size to use for text in this view,
     * in scaled pixels.
     */
    public void setMinTextSize(int minTextSizeScaledPixels) {
        mMinTextSizePixels = convertScaledPixelsToPixels(minTextSizeScaledPixels);
        requestLayout();
    }
    
    /**
     * @return lower text size limit, in pixels.
     */
    public float getMinTextSizePixels() {
        return mMinTextSizePixels;
    }
    
    private void initialise() {
        mMaxTextSizePixels = (int) getTextSize();
    }
    
    /**
     * Resizes this view's text size with respect to its width and height
     * (minus padding).
     */
    private void resizeText() {
        final int availableHeightPixels = getHeight()
                - getCompoundPaddingBottom() - getCompoundPaddingTop();
    
        final int availableWidthPixels = getWidth()
                - getCompoundPaddingLeft() - getCompoundPaddingRight();
    
        final CharSequence text = getText();
    
        // Safety check
        // (Do not resize if the view does not have dimensions or if there is no text)
        if (text == null
                || text.length() == 0
                || availableHeightPixels <= 0
                || availableWidthPixels <= 0
                || mMaxTextSizePixels == 0) {
            return;
        }
    
        int targetTextSizePixels = mMaxTextSizePixels;
        int targetTextHeightPixels = getTextHeightPixels(
                text,
                availableWidthPixels,
                targetTextSizePixels);
    
        // Until we either fit within our TextView
        // or we have reached our minimum text size,
        // incrementally try smaller sizes
        while (targetTextHeightPixels > availableHeightPixels
                && targetTextSizePixels > mMinTextSizePixels) {
            targetTextSizePixels = Math.max(
                    targetTextSizePixels - 2,
                    mMinTextSizePixels);
    
            targetTextHeightPixels = getTextHeightPixels(
                    text,
                    availableWidthPixels,
                    targetTextSizePixels);
        }
    
        // If we have reached our minimum text size and the text still doesn't fit,
        // append an ellipsis
        // (NOTE: Auto-ellipsize doesn't work hence why we have to do it here)
        // (TODO: put ellipsis at the beginning, middle or end
        // depending on the value of getEllipsize())
        if (getEllipsize() != null
                && targetTextSizePixels == mMinTextSizePixels
                && targetTextHeightPixels > availableHeightPixels) {
            // Make a copy of the original TextPaint object for measuring
            TextPaint textPaintCopy = new TextPaint(getPaint());
            textPaintCopy.setTextSize(targetTextSizePixels);
    
            // Measure using a StaticLayout instance
            StaticLayout staticLayout = new StaticLayout(
                    text,
                    textPaintCopy,
                    availableWidthPixels,
                    Layout.Alignment.ALIGN_NORMAL,
                    mLineSpacingMultiplier,
                    mLineSpacingExtra,
                    false);
    
            // Check that we have a least one line of rendered text
            if (staticLayout.getLineCount() > 0) {
                // Since the line at the specific vertical position would be cut off,
                // we must trim up to the previous line and add an ellipsis
                int lastLine = staticLayout.getLineForVertical(availableHeightPixels) - 1;
    
                if (lastLine >= 0) {
                    int startOffset = staticLayout.getLineStart(lastLine);
                    int endOffset = staticLayout.getLineEnd(lastLine);
                    float lineWidthPixels = staticLayout.getLineWidth(lastLine);
                    float ellipseWidth = textPaintCopy.measureText(mEllipsis);
    
                    // Trim characters off until we have enough room to draw the ellipsis
                    while (availableWidthPixels < lineWidthPixels + ellipseWidth) {
                        endOffset--;
                        lineWidthPixels = textPaintCopy.measureText(
                                text.subSequence(startOffset, endOffset + 1).toString());
                    }
    
                    setText(text.subSequence(0, endOffset) + mEllipsis);
                }
            }
        }
    
        super.setTextSize(TypedValue.COMPLEX_UNIT_PX, targetTextSizePixels);
        // Some devices try to auto adjust line spacing, so force default line spacing
        super.setLineSpacing(mLineSpacingExtra, mLineSpacingMultiplier);
    }
    
    /**
     * Sets the text size of a clone of the view's {@link TextPaint} object
     * and uses a {@link StaticLayout} instance to measure the height of the text.
     *
     * @param source
     * @param availableWidthPixels
     * @param textSizePixels
     * @return the height of the text when placed in a view
     * with the specified width
     * and when the text has the specified size.
     */
    private int getTextHeightPixels(
            CharSequence source,
            int availableWidthPixels,
            float textSizePixels) {
        // Make a copy of the original TextPaint object
        // since the object gets modified while measuring
        // (see also the docs for TextView.getPaint()
        // which states to access it read-only)
        TextPaint textPaintCopy = new TextPaint(getPaint());
        textPaintCopy.setTextSize(textSizePixels);
    
        // Measure using a StaticLayout instance
        StaticLayout staticLayout = new StaticLayout(
                source,
                textPaintCopy,
                availableWidthPixels,
                Layout.Alignment.ALIGN_NORMAL,
                mLineSpacingMultiplier,
                mLineSpacingExtra,
                true);
    
        return staticLayout.getHeight();
    }
    
    /**
     * @param scaledPixels
     * @return the number of pixels which
    scaledPixels
    corresponds to on the device.
     */
    private int convertScaledPixelsToPixels(int scaledPixels) {
        float pixels = scaledPixels * getContext().getResources().getDisplayMetrics().scaledDensity;
        return (int) pixels;
    }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-05-15
      • 1970-01-01
      • 2019-04-10
      • 2020-10-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多