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

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

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

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

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


当前回答

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

其他回答

我为此写了一篇博客。

我根据Kirill Grouchnikov关于在新的android market应用中使用的自定义组件的博客文章创建了一个名为ResizableButton的组件。我把src代码放在这里。

另一方面,mosabua读了我的文章,并告诉我他将开源他的实现,这比我的更快。我希望他能尽快发布:)

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

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

我只是借用了一些其他人的想法,并在下面写了一些代码,可能会有帮助。

import android.content.Context;
import android.graphics.Canvas;
import android.text.Layout.Alignment;
import android.text.StaticLayout;
import android.text.TextPaint;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.widget.TextView;

public class AutoResizeTextView extends TextView {
    private static final int MAX_SIZE = 1000;

    private static final int MIN_SIZE = 5;

    private TextPaint mTextPaint;

    private float mSpacingMult = 1.0f;

    private float mSpacingAdd = 0.0f;

    private boolean needAdapt = false;

    private boolean adapting = false;

    public AutoResizeTextView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init();
    }

    public AutoResizeTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public AutoResizeTextView(Context context) {
        super(context);
        init();
    }

    private void init() {
        mTextPaint = new TextPaint();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        if (adapting) {
            return;
        }
        if (needAdapt) {
            adaptTextSize();
        } else {
            super.onDraw(canvas);
        }
    }

    private void adaptTextSize() {
        CharSequence text = getText();
        int viewWidth = getMeasuredWidth();
        int viewHeight = getMeasuredHeight();

        if (viewWidth==0 || viewHeight==0
                || TextUtils.isEmpty(text)) {
            return;
        }

        adapting = true;
        /* binary search */
        int bottom=MIN_SIZE, top=MAX_SIZE, mid = 0;
        while (bottom <= top) {
            mid = (bottom + top)/2;
            mTextPaint.setTextSize(mid);
            int textWidth = (int) mTextPaint.measureText(text, 0, text.length());
            int textHeight = getTextHeight(text, viewWidth);
            if (textWidth<viewWidth && textHeight<viewHeight) {
                bottom = mid+1;
            } else {
                top = mid-1;
            }
        }

        int newSize = mid-1;
        setTextSize(TypedValue.COMPLEX_UNIT_PX, newSize);

        adapting=false;
        needAdapt = false;

        invalidate();
    }

    private int getTextHeight(CharSequence text, int targetWidth) {
        StaticLayout layout = new StaticLayout(text, mTextPaint, targetWidth,
                Alignment.ALIGN_NORMAL, mSpacingMult, mSpacingAdd, true);
        return layout.getHeight();
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        needAdapt = true;
    }

    @Override
    protected void onTextChanged(CharSequence text, int start,
            int lengthBefore, int lengthAfter) {
        super.onTextChanged(text, start, lengthBefore, lengthAfter);
        needAdapt = true;
    }

    @Override
    public void setLineSpacing(float add, float mult) {
        super.setLineSpacing(add, mult);
        mSpacingMult = mult;
        mSpacingAdd = add;
    }
}

从2018年6月起,Android正式开始支持Android 4.0 (API级别14)及更高版本的此功能。 查看它在:Autosizing TextViews

Android 8.0 (API级别26)及更高版本:

<?xml version="1.0" encoding="utf-8"?>
<TextView
    android:layout_width="match_parent"
    android:layout_height="200dp"
    android:autoSizeTextType="uniform"
    android:autoSizeMinTextSize="12sp"
    android:autoSizeMaxTextSize="100sp"
    android:autoSizeStepGranularity="2sp" />

编程:

setAutoSizeTextTypeUniformWithConfiguration(int autoSizeMinTextSize, int autoSizeMaxTextSize, 
        int autoSizeStepGranularity, int unit)

textView.setAutoSizeTextTypeUniformWithConfiguration(
                1, 17, 1, TypedValue.COMPLEX_UNIT_DIP);

Android 8.0之前的Android版本(API级别26):

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

  <TextView
      android:layout_width="match_parent"
      android:layout_height="200dp"
      app:autoSizeTextType="uniform"
      app:autoSizeMinTextSize="12sp"
      app:autoSizeMaxTextSize="100sp"
      app:autoSizeStepGranularity="2sp" />

</LinearLayout>

编程:

TextViewCompat.setAutoSizeTextTypeUniformWithConfiguration(
TextView textView, int autoSizeMinTextSize, int autoSizeMaxTextSize, int autoSizeStepGranularity, int unit) 

TextViewCompat.setAutoSizeTextTypeUniformWithConfiguration(textView, 1, 17, 1,
TypedValue.COMPLEX_UNIT_DIP);

注意:TextView必须有layout_width="match_parent"或绝对大小!

Android 4.x的一个变通方案:

我找到了AutoResizeTextView,它在我的Android 2.1模拟器上工作得很好。我非常喜欢它。但不幸的是,它在我自己的4.0.4手机和4.1模拟器上失败了。经过尝试,我发现它可以通过在xml中的AutoResizeTextView类中添加以下属性轻松解决:

android:ellipsize=“none” android:singleLine=“true”

有了上面的两行,现在AutoResizeTextView在我的2.1和4.1模拟器和我自己的4.0.4手机上完美地工作。

希望这对你有所帮助。: -)