如何根据视图最大尺寸在多行TextView上自动调整文本大小?

我一直在寻找一种自动适应textview内的文本的方式。 通过我的search,我发现了许多解决scheme:

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

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

基本上我的目标是这个:

阶段1阶段2阶段3阶段4

正如你所看到的,文本根据宽度,高度来resize,同时也关注换行符,创build一个多行文本视图。 也能够改变字体

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

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中的文本大小)

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中宽度是有限的,只有视图的高度需要考虑,因为调整它会在需要的时候自动创build一个多行。

虽然这是一个潜在的解决scheme并不完美(远离它),它不支持resize(当你删除文本)。

任何诉求和/或想法?

Solutions Collecting From Web of "如何根据视图最大尺寸在多行TextView上自动调整文本大小?"

在等待这个问题的一个可能的解决scheme的同时,我一直在试验并试图找出答案。

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

基本上油漆有一个名为“breaktext”的方法:

public int breakText(CharSequence text,int start,int end,boolean measureForWidth,float maxWidth,float [] measuredWidth)

在API级别1中添加

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

我把它与油漆“getTextBounds”结合起来:

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

在API级别1中添加

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

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

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

此外,您将不得不为每两行代表行间距(不包括在getTextBounds中)添加一个variables高度。

作为示例代码,知道多行文本高度的函数是这样的:

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

注意:maxWidthvariables以像素为单位

那么你将不得不在一段时间内调用这个方法来确定这个高度的最大字体大小是多less。 示例代码将是:

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

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

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

这个问题你有一个很好的解决scheme,但我从不同的angular度来看待这个问题。 至less对于新的android开发者来说,我认为这个逻辑更简单,更易于理解。 这个想法是基于在大string的空格之间插入'\ n'字符。 然后,我将编辑的string设置为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是我们要编辑的string,variables步定义了方法将在多less个空格中插入'/ n'字符。 那么,你只需要这样调用:

 myTextView.setText(insertNewLineCharactersAtString(yourDisplayingStr); 

希望这可以帮助你们许多人,你不想深入TextPaint,Rects和math函数..

感谢您的解决scheme,你是超人! 我已经在派生类的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" /> 

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

在这里输入图像说明 在这里输入图像说明 在这里输入图像说明

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

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

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