23

我一直在寻找一种在文本视图中自动调整文本的方法。通过我的搜索,我发现了许多解决方案,例如:

但像许多其他人一样,这些并不能解决我的问题。当我们使用带有多行的 TextView 时,它们无法按预期工作。

基本上我的目标是这个:

阶段1 阶段2 第三阶段 第 4 阶段

如您所见,文本根据宽度、高度调整大小并注意换行,创建多行文本视图。还可以更改字体。

我解决这个问题的一个想法是这样的:

int size = CONSTANT_MAX_SIZE;
TextView tv = (TextView) findViewById(R.id.textview1)
while(Math.abs(tv.getMeasuredHeight()) >= TEXTVIEW_MAX_HEIGHT) {
    size--;
    tv.setTextSize(size);
    tv.measure(MeasureSpec.UNSPECIFIED,MeasureSpec.UNSPECIFIED);
    i++;
}

CONSTANT_MAX_SIZE 是一个常量,定义了字体的最大尺寸(TextView 中的 textsize 专有)

TEXTVIEW_MAX_HEIGHT 是一个常量,它定义了 textview 可以拥有的最大尺寸。

每次更改 textview 中的文本时都会调用此方法。

textview xml 是这样的:

<TextView
     android:id="@+id/textview1"
     android:layout_width="200dp"
     android:layout_height="wrap_content"
     android:singleLine="false"
     android:inputType="textMultiLine"
     android:text=""
     android:textSize="150sp" />

由于 XML 中的宽度会受到限制,因此只需要考虑视图的高度,因为调整它 android 会在需要时自动创建多行。

尽管这是一个潜在的解决方案,但效果并不理想(远非如此),并且它不支持缩小尺寸(当您删除文本时)。

有什么建议和/或想法吗?

4

6 回答 6

21

在等待这个问题的可能解决方案时,我一直在尝试并试图弄清楚。

最接近这个问题的解决方案是基于油漆的方法。

基本上,paint 有一个名为“breaktext”的方法,它:

public int breakText (CharSequence text, int start, int end, boolean measureForwards, float maxWidth, float[] measureWidth)

在 API 级别 1 中添加

测量文本,如果测量的宽度超过 maxWidth,则提前停止。返回测量的字符数,如果测量宽度不为空,则返回测量的实际宽度。

我将它与paint 'getTextBounds' 结合起来:

public void getTextBounds (String text, int start, int end, Rect bounds)

在 API 级别 1 中添加

返回边界(由调用者分配)包围所有 > 字符的最小矩形,隐含原点位于 (0,0)。

所以现在我可以获得适合给定宽度的字符数和这些字符的高度。

使用一段时间,您可以继续从要测量的字符串中移动删除字符并获取行数(通过使用 while(index < string.length))并将其乘以在 getTextBounds 中获得的高度。

此外,您必须为每两行添加一个可变高度,表​​示行之间的空间(不计入 getTextBounds)。

作为示例代码,了解多行文本高度的函数如下所示:

public int getHeightOfMultiLineText(String text,int textSize, int maxWidth) {
    paint = new TextPaint();
    paint.setTextSize(textSize);
    int index = 0;
    int linecount = 0;
    while(index < text.length()) {
        index += paint.breakText(text,index,text.length,true,maxWidth,null);
        linecount++;
    }

    Rect bounds = new Rect();
    paint.getTextBounds("Yy", 0, 2, bounds);
    // obtain space between lines
    double lineSpacing = Math.max(0,((lineCount - 1) * bounds.height()*0.25)); 

    return (int)Math.floor(lineSpacing + lineCount * bounds.height());

注意:maxWidth 变量以像素为单位

然后您将不得不在一段时间内调用此方法以确定该高度的最大字体大小。一个示例代码是:

textSize = 100;
int maxHeight = 50;
while(getHeightOfMultiLineText(text,textSize,maxWidth) > maxHeight)
  textSize--;

不幸的是,这是我能够从上面的图像中实现这一方面的唯一(据我所知)方法。

希望这对任何试图克服这一障碍的人有所帮助。

于 2013-05-21T14:07:27.817 回答
3

在此处输入图像描述 在此处输入图像描述 在此处输入图像描述

phamducgiam 的一些改进答案。它显示已经适合大小的文本,而不是显示后的草图。由于启动 textSize 100,在编辑器中看起来很难看,但在运行时可以正常工作。这是代码:

import android.content.Context;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.widget.TextView;

public class FontFitTextView extends TextView {

    public FontFitTextView(Context context) {
        super(context);
    }

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

    public FontFitTextView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        // start decreasing font size from 100
        setTextSize(TypedValue.COMPLEX_UNIT_PX, 100);

        // calculate dimensions. don't forget about padding
        final float maxWidth = getWidth()-(getPaddingLeft()+getPaddingRight());
        final float maxHeight = getHeight()-(getPaddingTop()+getPaddingBottom());

        if (maxWidth < 1.0f || maxHeight < 1.0f) {
            return;
        }

        CharSequence text = getText();
        int lineCount = getLineCount(maxWidth, text);
        float height = getHeight(lineCount);
        // keep decreasing font size until it fits
        while (height > maxHeight) {
            final float textSize = getTextSize();
            setTextSize(TypedValue.COMPLEX_UNIT_PX, (textSize - 1));
            height = getHeight(getLineCount(maxWidth, getText()));
        }
        // show fitted textView
        requestLayout();
    }

    private float getHeight(int lineCount) {
        return lineCount * getLineHeight() + (lineCount > 0 ? (lineCount - 1) * getPaint().getFontSpacing() : 0);
    }

    private int getLineCount(float maxWidth, CharSequence text) {
        int lineCount = 0;
        int index = 0;
        final TextPaint paint = getPaint();
        while (index < text.length()) {
            index += paint.breakText(text, index, text.length(), true, maxWidth, null);
            lineCount++;
        }
        return lineCount;
    }
}
于 2015-09-10T04:05:00.580 回答
1

你对这个问题有一个很好的解决方案,但我从不同的角度来看待这个问题。我认为至少对于新的android开发人员来说,逻辑更简单,更容易理解。这个想法是基于在大字符串的空格之间插入 '\n' 字符。然后,我将 textView 处的编辑字符串设置为显示文本。所以,这里是代码..

private String insertNewLineCharAtString(String str, int step){
    StringBuilder sb = new StringBuilder();
    int spaceCounter = 0;
    String[] strArray = str.split(" ");
    for(int i = 0; i < strArray.length; i++){



        if(spaceCounter == step){
            sb.append('\n');
            sb.append(strArray[i]);
            spaceCounter = 0;
        }else{
            sb.append(" "+strArray[i]);
        }
        spaceCounter++;


    }
    return sb.toString();
}

参数很简单。Str 是我们要编辑的字符串,变量 step 定义该方法将在多少空格中插入“/n”字符。然后,您只需调用以下代码:

myTextView.setText(insertNewLineCharactersAtString(yourDisplayingStr);

希望这对你们中的许多人有所帮助,因为你们不想更深入地使用 TextPaint 、 Rects 和数学函数。

于 2013-09-26T15:49:42.667 回答
1

使用nunofmendes 的想法,我编写了一个 TextView 的派生类,它可以自动调整文本大小并支持多行。

import android.content.Context;
import android.os.Handler;
import android.text.Layout;
import android.text.TextPaint;
import android.text.TextUtils.TruncateAt;
import android.util.AttributeSet;
import android.util.TypedValue;

public class AutoResizeTextView extends TextView {

    private Handler measureHandler = new Handler();
    private Runnable requestLayout = new Runnable() {
        @Override
        public void run() {
            requestLayout();
        }
    };

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

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

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

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

        final float maxWidth = getWidth();
        final float maxHeight = getHeight();
        if (maxWidth < 1.0f || maxHeight < 1.0f) {
            return;
        }

        int index = 0;
        int lineCount = 0;
        CharSequence text = getText();
        final TextPaint paint = getPaint();
        while (index < text.length()) {
            index += paint.breakText(text, index, text.length(), true, maxWidth, null);
            lineCount++;
        }
        final float height = lineCount * getLineHeight() + (lineCount > 0 ?
                (lineCount - 1) * paint.getFontSpacing() : 0);
        if (height > maxHeight) {
            final float textSize = getTextSize();
            setTextSize(TypedValue.COMPLEX_UNIT_PX, (textSize - 1));
            measureHandler.post(requestLayout);
        }
    }
}
于 2014-07-08T18:29:29.037 回答
1

感谢您的解决方案,您是超人!我已经在 TextView 的派生类中实现了您的代码。该代码将转换为 Xamarin android 的 C# 代码。如果需要,您可以轻松转换为 Java(删除 Java 的第一个构造函数)。

public class AutoFitTextView : TextView
{
    public AutoFitTextView(System.IntPtr javaReference, Android.Runtime.JniHandleOwnership transfer)
        : base(javaReference, transfer)
    {
    }

    public AutoFitTextView(Context context)
        : base(context)
    {

    }

    public AutoFitTextView(Context context, IAttributeSet attrs)
        : base(context, attrs)
    {

    }


    public void ResizeFontSize()
    {
        int textSize = 50;
        int maxHeight = this.Height;
        while (GetHeightOfMultiLineText(this.Text, textSize, this.Width) > maxHeight)
        {
            textSize--;
        }
        float scaleFactor = Context.Resources.DisplayMetrics.ScaledDensity;
        float additionalFactor = 1.2f;

        TextSize = ((float)(textSize / (additionalFactor * scaleFactor)));
    }


    private int GetHeightOfMultiLineText(string text, int textSize, int maxWidth)
    {
        TextPaint paint = new TextPaint();
        paint.TextSize = textSize;
        int index = 0;
        int lineCount = 0;
        while (index < text.Length)
        {
            index += paint.BreakText(text, index, text.Length, true, maxWidth, null);
            lineCount++;
        }

        Rect bounds = new Rect();
        paint.GetTextBounds("Yy", 0, 2, bounds);
        // obtain space between lines
        double lineSpacing = Math.Max(0, ((lineCount - 1) * bounds.Height() * 0.25));

        return (int)Math.Floor(lineSpacing + lineCount * bounds.Height());
    }

}

这是AXML代码

        <touchtest.AutoFitTextView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/tvLabel2"
        android:singleLine="false"
        android:inputType="textMultiLine"
        android:text="I am here To test this text" />
于 2013-11-21T04:06:14.257 回答
-2

可编译的、固定的代码:复制粘贴这个,而不是接受的,因为你需要修复它:)

public static int getHeightOfMultiLineText(String text, int textSize, int maxWidth) {
    TextPaint paint = new TextPaint();
    paint.setTextSize(textSize);
    int index = 0;
    int lineCount = 0;
    while (index < text.length()) {
        index += paint.breakText(text, index, text.length(), true, maxWidth, null);
        lineCount++;
    }

    Rect bounds = new Rect();
    paint.getTextBounds("Yy", 0, 2, bounds);
    // obtain space between lines
    double lineSpacing = Math.max(0, ((lineCount - 1) * bounds.height() * 0.25));

    return (int) Math.floor(lineSpacing + lineCount * bounds.height());
}

需要使用,谢谢。

重要提示:您需要考虑比例因子

    int textSize = 50;
    int maxHeight = boundsTv.height();
    while (getHeightOfMultiLineText(text, textSize, params.width) > maxHeight) {
        textSize--;
    }
    float scaleFactor =  mainActivity.getResources().getDisplayMetrics().scaledDensity;     
    float temp = 1.2f;

    tvText.setTextSize((float) (textSize / (temp*scaleFactor)));
于 2013-11-02T14:22:28.100 回答