我怎样才能跨越多行?

时间:2014-09-29 16:31:08

标签: android dynamic textview scale

我非常感激地看到一段漂亮的代码,它可以动态测量textView框并相应地调整其中的文本大小。这一切都很棒但是它只对一行文本有用,我如何确保文本一旦到达框的边缘就开始一个新的行?

FillTextView

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);
    }
}

}

1 个答案:

答案 0 :(得分:0)

经过一番彻底的搜索,这里有一个处理

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;
}
}