我正在寻找一种最佳的方法来调整包装文本在一个TextView,使它将适合它的getHeight和getWidth界限。我不是简单地寻找一种方法来包装文本-我想确保它既包装,又足够小,完全适合在屏幕上。

我在StackOverflow上看到了一些需要自动调整大小的情况,但它们要么是非常特殊的情况下的hack解决方案,没有解决方案,或涉及重新绘制TextView递归直到它足够小(这是内存紧张,迫使用户观看文本收缩一步一步与每次递归)。

但我相信有人已经找到了一个很好的解决方案,它不涉及我正在做的事情:编写几个繁重的例程来解析和测量文本,调整文本的大小,然后重复,直到找到一个合适的小尺寸。

TextView使用什么例程来包装文本?难道这些不能用来预测文本是否足够小吗?

是否有一个最佳实践的方法来自动调整TextView的大小,以适应,包装,在它的getHeight和getWidth边界?


当前回答

参考ScalableTextView.java这里自动适合Android的TextView。我已经添加了代码来根据文本长度收缩和扩展TextView

其他回答

我已经使用代码从追逐和M-WaJeEh 我发现了一些优点和缺点

从追逐

优势: 这是完美的1行TextView 劣势: 如果它超过1行自定义字体,一些文本将消失 如果它是使能椭圆,它没有为椭圆准备空间 如果是自定义字体(字体),则不支持

从M-WaJeEh

优势: 非常适合多线使用 劣势: 如果将height设置为wrap-content,这段代码将从最小大小开始,并尽可能减小到最小,而不是从setSize开始并减小有限的宽度 如果是自定义字体(字体),则不支持

我从Chase的解决方案开始,但在我的设备(Galaxy Nexus, Android 4.1)上正常工作之前,我必须调整两件事:

using a copy of TextPaint for measuring layout The documentation for TextView.getPaint() states that it should be used read-only, so I made a copy in both places where we use the paint object for measuring: // 1. in resizeText() if (mAddEllipsis && targetTextSize == mMinTextSize && textHeight > height) { // Draw using a static layout // modified: use a copy of TextPaint for measuring TextPaint paint = new TextPaint(textPaint); // 2. in getTextHeight() private int getTextHeight(CharSequence source, TextPaint originalPaint, int width, float textSize) { // modified: make a copy of the original TextPaint object for measuring // (apparently the object gets modified while measuring, see also the // docs for TextView.getPaint() (which states to access it read-only) TextPaint paint = new TextPaint(originalPaint); // Update the text paint object paint.setTextSize(textSize); ... adding a unit to setting the text size // modified: setting text size via this.setTextSize (instead of textPaint.setTextSize(targetTextSize)) setTextSize(TypedValue.COMPLEX_UNIT_PX, targetTextSize); setLineSpacing(mSpacingAdd, mSpacingMult);

With these two modifications the solution is working perfectly for me, thanks Chase! I don't know whether it is due to Android 4.x that the original solution was not working. In case you want to see it in action or test whether it really works on your device, you can have a look at my flashcard app Flashcards ToGo where I use this solution to scale the text of a flashcard. The text can have arbitrary length, and the flashcards are displayed in different activities, sometimes smaller sometimes bigger, plus in landscape + portrait mode, and I haven't found any corner case where the solution would not work properly...

我刚刚创建了以下方法(基于Chase的想法),如果你想在任何画布上绘制文本,它可能会帮助你:

private static void drawText(Canvas canvas, int xStart, int yStart,
        int xWidth, int yHeigth, String textToDisplay,
        TextPaint paintToUse, float startTextSizeInPixels,
        float stepSizeForTextSizeSteps) {

    // Text view line spacing multiplier
    float mSpacingMult = 1.0f;
    // Text view additional line spacing
    float mSpacingAdd = 0.0f;
    StaticLayout l = null;
    do {
        paintToUse.setTextSize(startTextSizeInPixels);
        startTextSizeInPixels -= stepSizeForTextSizeSteps;
        l = new StaticLayout(textToDisplay, paintToUse, xWidth,
                Alignment.ALIGN_NORMAL, mSpacingMult, mSpacingAdd, true);
    } while (l.getHeight() > yHeigth);

    int textCenterX = xStart + (xWidth / 2);
    int textCenterY = (yHeigth - l.getHeight()) / 2;

    canvas.save();
    canvas.translate(textCenterX, textCenterY);
    l.draw(canvas);
    canvas.restore();
}

这可以在任何自定义视图的任何onDraw()方法中使用。

这里还有另一个解决方案,只是为了好玩。它可能不是很有效,但它确实处理了文本的高度和宽度,以及有标记的文本。

@Override
protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec)
{
    if ((MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.UNSPECIFIED)
            && (MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.UNSPECIFIED)) {

        final float desiredWidth = MeasureSpec.getSize(widthMeasureSpec);
        final float desiredHeight = MeasureSpec.getSize(heightMeasureSpec);

        float textSize = getTextSize();
        float lastScale = Float.NEGATIVE_INFINITY;
        while (textSize > MINIMUM_AUTO_TEXT_SIZE_PX) {
            // Measure how big the textview would like to be with the current text size.
            super.onMeasure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);

            // Calculate how much we'd need to scale it to fit the desired size, and
            // apply that scaling to the text size as an estimate of what we need.
            final float widthScale = desiredWidth / getMeasuredWidth();
            final float heightScale = desiredHeight / getMeasuredHeight();
            final float scale = Math.min(widthScale, heightScale);

            // If we don't need to shrink the text, or we don't seem to be converging, we're done.
            if ((scale >= 1f) || (scale <= lastScale)) {
                break;
            }

            // Shrink the text size and keep trying.
            textSize = Math.max((float) Math.floor(scale * textSize), MINIMUM_AUTO_TEXT_SIZE_PX);
            setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
            lastScale = scale;
        }
    }
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}

参考ScalableTextView.java这里自动适合Android的TextView。我已经添加了代码来根据文本长度收缩和扩展TextView