如何在多线自动调整文字大小的TextView根据视图最大尺寸是多少?(How to auto-adj

2019-08-31 11:33发布

我一直在寻找汽车的一种方式适合一个TextView内的文本。 通过我的搜索,我发现了很多解决方案,如:

  • FontFitTextView
  • AutoResizeTextView
  • 自动定标/调整

但是,和许多人一样,这些不解决我的问题。 当我们用一个TextView与多预期他们不工作。

基本上我的目标是这个:

正如你所看到的,文本的大小调整基于宽度,高度,也注重换行符,创造了多行的TextView。 还能够改变字体。

我的一个思路来解决,这是这样的:

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是有限的,因为需要调节它在需要的时候机器人将自动地创建多被认为仅视图的高度。

尽管这是一个潜在的解决方案是不是(远非如此)完美的工作,它不支持调整下来(当你删除文本)。

任何sugestions和/或想法?

Answer 1:

虽然我等了一个可能的解决这个问题,我一直在尝试,并试图弄明白。

最接近的解决这个问题是基于从涂料的方法。

基本上涂料有一个名为“BREAKTEXT”该方法中:

公众诠释BREAKTEXT(CharSequence的文字,INT开始,INT端,布尔measureForwards,浮maxWidth,上浮[]是measuredWidth)

在API级别1

测量的文本,如果所测量的宽度超过maxWidth早期停止。 返回测量该字符的数目,并且如果measuredWidth可以是不为空,实际宽度测量返回它。

我结合起来,与油漆“getTextBounds”其中:

公共无效getTextBounds(字符串文本,诠释开始,诠释年底,矩形边界)

在API级别1

返回边界(由调用者分配的),在(0,0)包围了所有>字符,有一个隐含的起源的最小矩形。

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

使用而可以保持(通过使用一段时间(指数<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--;

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

希望这可以帮助任何人试图克服这一障碍。



Answer 2:

您对这个问题的一个很好的解决方案,但我看了看这个从不同的角度来看。 我认为逻辑更简单,更容易理解至少在新的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的是,我们要编辑的路线和变步长的方法有多少空间插入“/ n”字符定义。 然后,你只需要调用是这样的:

myTextView.setText(insertNewLineCharactersAtString(yourDisplayingStr);

希望这可以帮助很多的你,你不想去深入与TextPaint,Rects,和数学函数..



Answer 3:

你是感谢您的解决方案超人! 我在派生类中的TextView的实现代码。 该代码被转换为C#代码Xamarin机器人。 您可以轻松地转换成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" />


Answer 4:

phamducgiam的一点点改进的答案。 它显示已经安装尺寸,没有显示后skretchs文本。 看起来是因为开始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;
    }
}


Answer 5:

使用从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);
        }
    }
}


Answer 6:

在编译的,固定的代码:复制粘贴此一个,而不是接受一个,因为你需要解决它:)

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


文章来源: How to auto-adjust text size on a multi-line TextView according to the view max dimensions?