如何调整文本字体大小以适应textview

有什么办法可以在android中调整文本视图中的文本大小,以适应它占据的空间?

例如,我正在使用TableLayout并添加几个TextView的每一行。 由于我不希望TextView包装文本,我宁愿看到它降低了内容的字体大小。

有任何想法吗?

我已经尝试了measureText ,但是因为我不知道列的大小,所以使用起来似乎很麻烦。 这是我想要改变字体大小的代码

 TableRow row = new TableRow(this); for (int i=0; i < ColumnNames.length; i++) { TextView textColumn = new TextView(this); textColumn.setText(ColumnNames[i]); textColumn.setPadding(0, 0, 1, 0); textColumn.setTextColor(getResources().getColor(R.drawable.text_default)); row.addView(textColumn, new TableRow.LayoutParams()); } table.addView(row, new TableLayout.LayoutParams()); 

下面的解决scheme结合了这里的所有build议。 它从Dunni最初发布的内容开始。 它使用像gjpc这样的二进制search,但它更可读。 它还包括gregm的错误修复和我自己的错误修复。

 import android.content.Context; import android.graphics.Paint; import android.util.AttributeSet; import android.util.TypedValue; import android.widget.TextView; public class FontFitTextView extends TextView { public FontFitTextView(Context context) { super(context); initialise(); } public FontFitTextView(Context context, AttributeSet attrs) { super(context, attrs); initialise(); } private void initialise() { mTestPaint = new Paint(); mTestPaint.set(this.getPaint()); //max size defaults to the initially specified text size unless it is too small } /* Re size the font so the specified text fits in the text box * assuming the text box is the specified width. */ private void refitText(String text, int textWidth) { if (textWidth <= 0) return; int targetWidth = textWidth - this.getPaddingLeft() - this.getPaddingRight(); float hi = 100; float lo = 2; final float threshold = 0.5f; // How close we have to be mTestPaint.set(this.getPaint()); while((hi - lo) > threshold) { float size = (hi+lo)/2; mTestPaint.setTextSize(size); if(mTestPaint.measureText(text) >= targetWidth) hi = size; // too big else lo = size; // too small } // Use lo so that we undershoot rather than overshoot this.setTextSize(TypedValue.COMPLEX_UNIT_PX, lo); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int parentWidth = MeasureSpec.getSize(widthMeasureSpec); int height = getMeasuredHeight(); refitText(this.getText().toString(), parentWidth); this.setMeasuredDimension(parentWidth, height); } @Override protected void onTextChanged(final CharSequence text, final int start, final int before, final int after) { refitText(text.toString(), this.getWidth()); } @Override protected void onSizeChanged (int w, int h, int oldw, int oldh) { if (w != oldw) { refitText(this.getText().toString(), w); } } //Attributes private Paint mTestPaint; } 

我写了一个扩展TextView的类并执行此操作。 它只是像你所build议的那样使用measureText。 基本上它有一个最大的文本大小和最小的文本大小(可以改变),它只是贯穿在他们之间的大小递减1,直到它find最适合的最大的一个。 不是特别优雅,但我不知道有任何其他的方式。

这里是代码:

 import android.content.Context; import android.graphics.Paint; import android.util.AttributeSet; import android.widget.TextView; public class FontFitTextView extends TextView { public FontFitTextView(Context context) { super(context); initialise(); } public FontFitTextView(Context context, AttributeSet attrs) { super(context, attrs); initialise(); } private void initialise() { testPaint = new Paint(); testPaint.set(this.getPaint()); //max size defaults to the intially specified text size unless it is too small maxTextSize = this.getTextSize(); if (maxTextSize < 11) { maxTextSize = 20; } minTextSize = 10; } /* Re size the font so the specified text fits in the text box * assuming the text box is the specified width. */ private void refitText(String text, int textWidth) { if (textWidth > 0) { int availableWidth = textWidth - this.getPaddingLeft() - this.getPaddingRight(); float trySize = maxTextSize; testPaint.setTextSize(trySize); while ((trySize > minTextSize) && (testPaint.measureText(text) > availableWidth)) { trySize -= 1; if (trySize <= minTextSize) { trySize = minTextSize; break; } testPaint.setTextSize(trySize); } this.setTextSize(trySize); } } @Override protected void onTextChanged(final CharSequence text, final int start, final int before, final int after) { refitText(text.toString(), this.getWidth()); } @Override protected void onSizeChanged (int w, int h, int oldw, int oldh) { if (w != oldw) { refitText(this.getText().toString(), w); } } //Getters and Setters public float getMinTextSize() { return minTextSize; } public void setMinTextSize(int minTextSize) { this.minTextSize = minTextSize; } public float getMaxTextSize() { return maxTextSize; } public void setMaxTextSize(int minTextSize) { this.maxTextSize = minTextSize; } //Attributes private Paint testPaint; private float minTextSize; private float maxTextSize; } 

这是speedplane的FontFitTextView ,但是如果需要使文本适合,它只会减小字体大小,否则保持字体大小。 它不会增加字体大小以适应高度。

 public class FontFitTextView extends TextView { // Attributes private Paint mTestPaint; private float defaultTextSize; public FontFitTextView(Context context) { super(context); initialize(); } public FontFitTextView(Context context, AttributeSet attrs) { super(context, attrs); initialize(); } private void initialize() { mTestPaint = new Paint(); mTestPaint.set(this.getPaint()); defaultTextSize = getTextSize(); } /* Re size the font so the specified text fits in the text box * assuming the text box is the specified width. */ private void refitText(String text, int textWidth) { if (textWidth <= 0 || text.isEmpty()) return; int targetWidth = textWidth - this.getPaddingLeft() - this.getPaddingRight(); // this is most likely a non-relevant call if( targetWidth<=2 ) return; // text already fits with the xml-defined font size? mTestPaint.set(this.getPaint()); mTestPaint.setTextSize(defaultTextSize); if(mTestPaint.measureText(text) <= targetWidth) { this.setTextSize(TypedValue.COMPLEX_UNIT_PX, defaultTextSize); return; } // adjust text size using binary search for efficiency float hi = defaultTextSize; float lo = 2; final float threshold = 0.5f; // How close we have to be while (hi - lo > threshold) { float size = (hi + lo) / 2; mTestPaint.setTextSize(size); if(mTestPaint.measureText(text) >= targetWidth ) hi = size; // too big else lo = size; // too small } // Use lo so that we undershoot rather than overshoot this.setTextSize(TypedValue.COMPLEX_UNIT_PX, lo); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int parentWidth = MeasureSpec.getSize(widthMeasureSpec); int height = getMeasuredHeight(); refitText(this.getText().toString(), parentWidth); this.setMeasuredDimension(parentWidth, height); } @Override protected void onTextChanged(final CharSequence text, final int start, final int before, final int after) { refitText(text.toString(), this.getWidth()); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { if (w != oldw || h != oldh) { refitText(this.getText().toString(), w); } } } 

这里是一个如何在XML中使用的例子:

 <com.your.package.activity.widget.FontFitTextView android:id="@+id/my_id" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:text="My Text" android:textSize="60sp" /> 

只要文本适合宽度,这将保持字体大小为60sp。 如果文字较长,则会减小字体大小。 在这种情况下, TextView的高度也会因为height=wrap_content改变。

如果您发现任何错误,请随时编辑。

这是我的解决scheme,它可以在模拟器和手机上工作,但在Eclipse布局编辑器上不太好。 它的灵感来自于kilaka的代码,但文本的大小并不是从Paint获得的,而是从测量TextView本身的调用measure(0, 0)

Java类:

 public class FontFitTextView extends TextView { private static final float THRESHOLD = 0.5f; private enum Mode { Width, Height, Both, None } private int minTextSize = 1; private int maxTextSize = 1000; private Mode mode = Mode.None; private boolean inComputation; private int widthMeasureSpec; private int heightMeasureSpec; public FontFitTextView(Context context) { super(context); } public FontFitTextView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public FontFitTextView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); TypedArray tAttrs = context.obtainStyledAttributes(attrs, R.styleable.FontFitTextView, defStyle, 0); maxTextSize = tAttrs.getDimensionPixelSize(R.styleable.FontFitTextView_maxTextSize, maxTextSize); minTextSize = tAttrs.getDimensionPixelSize(R.styleable.FontFitTextView_minTextSize, minTextSize); tAttrs.recycle(); } private void resizeText() { if (getWidth() <= 0 || getHeight() <= 0) return; if(mode == Mode.None) return; final int targetWidth = getWidth(); final int targetHeight = getHeight(); inComputation = true; float higherSize = maxTextSize; float lowerSize = minTextSize; float textSize = getTextSize(); while(higherSize - lowerSize > THRESHOLD) { textSize = (higherSize + lowerSize) / 2; if (isTooBig(textSize, targetWidth, targetHeight)) { higherSize = textSize; } else { lowerSize = textSize; } } setTextSize(TypedValue.COMPLEX_UNIT_PX, lowerSize); measure(widthMeasureSpec, heightMeasureSpec); inComputation = false; } private boolean isTooBig(float textSize, int targetWidth, int targetHeight) { setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize); measure(0, 0); if(mode == Mode.Both) return getMeasuredWidth() >= targetWidth || getMeasuredHeight() >= targetHeight; if(mode == Mode.Width) return getMeasuredWidth() >= targetWidth; else return getMeasuredHeight() >= targetHeight; } private Mode getMode(int widthMeasureSpec, int heightMeasureSpec) { int widthMode = MeasureSpec.getMode(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); if(widthMode == MeasureSpec.EXACTLY && heightMode == MeasureSpec.EXACTLY) return Mode.Both; if(widthMode == MeasureSpec.EXACTLY) return Mode.Width; if(heightMode == MeasureSpec.EXACTLY) return Mode.Height; return Mode.None; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); if(!inComputation) { this.widthMeasureSpec = widthMeasureSpec; this.heightMeasureSpec = heightMeasureSpec; mode = getMode(widthMeasureSpec, heightMeasureSpec); resizeText(); } } protected void onTextChanged(final CharSequence text, final int start, final int before, final int after) { resizeText(); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { if (w != oldw || h != oldh) resizeText(); } public int getMinTextSize() { return minTextSize; } public void setMinTextSize(int minTextSize) { this.minTextSize = minTextSize; resizeText(); } public int getMaxTextSize() { return maxTextSize; } public void setMaxTextSize(int maxTextSize) { this.maxTextSize = maxTextSize; resizeText(); } } 

XML属性文件:

 <resources> <declare-styleable name="FontFitTextView"> <attr name="minTextSize" format="dimension" /> <attr name="maxTextSize" format="dimension" /> </declare-styleable> </resources> 

检查我的github这个类的最新版本。 我希望这对某个人有用。 如果发现错误或代码需要解释,请随时在Github上打开一个问题。

非常感谢https://stackoverflow.com/users/234270/speedplane 。 很好的回答!

这是他的回应的改进版本,也照顾高度,并附带一个maxFontSize属性来限制字体大小(在我的情况是有用的,所以我想分享它):

 package com.<your_package>; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Paint; import android.graphics.Rect; import android.util.AttributeSet; import android.util.TypedValue; import android.widget.TextView; public class FontFitTextView extends TextView { private Paint mTestPaint; private float maxFontSize; private static final float MAX_FONT_SIZE_DEFAULT_VALUE = 20f; public FontFitTextView(Context context) { super(context); initialise(context, null); } public FontFitTextView(Context context, AttributeSet attributeSet) { super(context, attributeSet); initialise(context, attributeSet); } public FontFitTextView(Context context, AttributeSet attributeSet, int defStyle) { super(context, attributeSet, defStyle); initialise(context, attributeSet); } private void initialise(Context context, AttributeSet attributeSet) { if(attributeSet!=null) { TypedArray styledAttributes = context.obtainStyledAttributes(attributeSet, R.styleable.FontFitTextView); maxFontSize = styledAttributes.getDimension(R.styleable.FontFitTextView_maxFontSize, MAX_FONT_SIZE_DEFAULT_VALUE); styledAttributes.recycle(); } else { maxFontSize = MAX_FONT_SIZE_DEFAULT_VALUE; } mTestPaint = new Paint(); mTestPaint.set(this.getPaint()); //max size defaults to the initially specified text size unless it is too small } /* Re size the font so the specified text fits in the text box * assuming the text box is the specified width. */ private void refitText(String text, int textWidth, int textHeight) { if (textWidth <= 0) return; int targetWidth = textWidth - this.getPaddingLeft() - this.getPaddingRight(); int targetHeight = textHeight - this.getPaddingTop() - this.getPaddingBottom(); float hi = maxFontSize; float lo = 2; // final float threshold = 0.5f; // How close we have to be final float threshold = 1f; // How close we have to be mTestPaint.set(this.getPaint()); Rect bounds = new Rect(); while ((hi - lo) > threshold) { float size = (hi + lo) / 2; mTestPaint.setTextSize(size); mTestPaint.getTextBounds(text, 0, text.length(), bounds); if (bounds.width() >= targetWidth || bounds.height() >= targetHeight) hi = size; // too big else lo = size; // too small // if (mTestPaint.measureText(text) >= targetWidth) // hi = size; // too big // else // lo = size; // too small } // Use lo so that we undershoot rather than overshoot this.setTextSize(TypedValue.COMPLEX_UNIT_PX, lo); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int parentWidth = MeasureSpec.getSize(widthMeasureSpec); int height = getMeasuredHeight(); refitText(this.getText().toString(), parentWidth, height); this.setMeasuredDimension(parentWidth, height); } @Override protected void onTextChanged(final CharSequence text, final int start, final int before, final int after) { refitText(text.toString(), this.getWidth(), this.getHeight()); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { if (w != oldw) { refitText(this.getText().toString(), w, h); } } } 

对应的/res/values/attr.xml文件:

 <?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="FontFitTextView"> <attr name="maxFontSize" format="dimension" /> </declare-styleable> </resources> 

例:

 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:res-auto="http://schemas.android.com/apk/res-auto" android:id="@+id/home_Layout" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@drawable/background" tools:ignore="ContentDescription" > ... <com.<your_package>.FontFitTextView android:layout_width="match_parent" android:layout_height="wrap_content" android:singleLine="true" android:text="Sample Text" android:textSize="28sp" res-auto:maxFontSize="35sp"/> ... </RelativeLayout> 

要使用新的maxFontSize属性,请不要忘记添加xmlns:res-auto="http://schemas.android.com/apk/res-auto"如示例中所示。

我有同样的问题,写了一个似乎为我工作的课程。 基本上,我使用静态布局在单独的canvas中绘制文本,并重新测量,直到find适合的字体大小。 您可以在下面的主题中看到发布的课程。 我希望它有帮助。

自动缩放TextView文本以适合边界

与修改工作

您需要像这样设置文本视图大小,否则setTextSize将假定值为SP单位:

 setTextSize(TypedValue.COMPLEX_UNIT_PX, trySize); 

你需要明确地添加这个代码。

 @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int parentWidth = MeasureSpec.getSize(widthMeasureSpec); int parentHeight = MeasureSpec.getSize(heightMeasureSpec); refitText(this.getText().toString(), parentWidth); } 

对onMeasure稍作修改:

 @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int parentWidth = MeasureSpec.getSize(widthMeasureSpec); int parentHeight = MeasureSpec.getSize(heightMeasureSpec); refitText(this.getText().toString(), parentWidth); this.setMeasuredDimension(parentWidth, parentHeight); } 

并在refitText上进行二进制search:

 private void refitText(String text, int textWidth) { if (textWidth > 0) { int availableWidth = textWidth - this.getPaddingLeft() - this.getPaddingRight(); int trySize = (int)maxTextSize; int increment = ~( trySize - (int)minTextSize ) / 2; testPaint.setTextSize(trySize); while ((trySize > minTextSize) && (testPaint.measureText(text) > availableWidth)) { trySize += increment; increment = ( increment == 0 ) ? -1 : ~increment / 2; if (trySize <= minTextSize) { trySize = (int)minTextSize; break; } testPaint.setTextSize(trySize); } this.setTextSize( TypedValue.COMPLEX_UNIT_PX, trySize); } } 

我发现以下对我来说很好。 它不会循环并计算高度和宽度。 请注意,在视图上调用setTextSize时指定PX单位非常重要。 感谢上一篇文章的提示!

 Paint paint = adjustTextSize(getPaint(), numChars, maxWidth, maxHeight); setTextSize(TypedValue.COMPLEX_UNIT_PX,paint.getTextSize()); 

这里是我使用的例程,从视图中传入getPaint()。 使用“宽”字符的10个字符的string来估计与实际string无关的宽度。

 private static final String text10="OOOOOOOOOO"; public static Paint adjustTextSize(Paint paint, int numCharacters, int widthPixels, int heightPixels) { float width = paint.measureText(text10)*numCharacters/text10.length(); float newSize = (int)((widthPixels/width)*paint.getTextSize()); paint.setTextSize(newSize); // remeasure with font size near our desired result width = paint.measureText(text10)*numCharacters/text10.length(); newSize = (int)((widthPixels/width)*paint.getTextSize()); paint.setTextSize(newSize); // Check height constraints FontMetricsInt metrics = paint.getFontMetricsInt(); float textHeight = metrics.descent-metrics.ascent; if (textHeight > heightPixels) { newSize = (int)(newSize * (heightPixels/textHeight)); paint.setTextSize(newSize); } return paint; } 

我使用了上面的Dunni解决scheme的一个变种,但是这个特定的代码对我来说不起作用。 特别是,当试图使用Paint对象设置具有视图的Paint对象的特性,然后调用measureText()时,它不会像直接调用视图的Paint对象那样返回相同的值。 也许我的观点build立的方式有一些差异,使行为不同。

我的解决scheme是直接使用视图的Paint,即使多次更改视图的字体大小可能会有一些性能损失。

我一直在努力从飞机改进出色的解决scheme,并提出了这个问题。 它pipe理的高度,包括设置边距,使文本应垂直居中。

这使用相同的函数来获得宽度,因为它看起来工作得最好,但它使用不同的函数来获得高度,因为高度不提供任何地方。 有一些需要改正的地方,但是我想出了一个方法来做到这一点,同时看上去很悦目。

 import android.content.Context; import android.graphics.Paint; import android.graphics.Rect; import android.util.AttributeSet; import android.util.TypedValue; import android.widget.TextView; public class FontFitTextView extends TextView { public FontFitTextView(Context context) { super(context); initialize(); } public FontFitTextView(Context context, AttributeSet attrs) { super(context, attrs); initialize(); } private void initialize() { mTestPaint = new Paint(); mTestPaint.set(this.getPaint()); //max size defaults to the initially specified text size unless it is too small } /* Re size the font so the specified text fits in the text box * assuming the text box is the specified width. */ private void refitText(String text, int textWidth,int textHeight) { if (textWidth <= 0) return; int targetWidth = textWidth - this.getPaddingLeft() - this.getPaddingRight(); int targetHeight = textHeight - this.getPaddingTop() - this.getPaddingBottom(); float hi = Math.min(targetHeight,100); float lo = 2; final float threshold = 0.5f; // How close we have to be Rect bounds = new Rect(); mTestPaint.set(this.getPaint()); while((hi - lo) > threshold) { float size = (hi+lo)/2; mTestPaint.setTextSize(size); mTestPaint.getTextBounds(text, 0, text.length(), bounds); if((mTestPaint.measureText(text)) >= targetWidth || (1+(2*(size+(float)bounds.top)-bounds.bottom)) >=targetHeight) hi = size; // too big else lo = size; // too small } // Use lo so that we undershoot rather than overshoot this.setTextSize(TypedValue.COMPLEX_UNIT_PX,(float) lo); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int parentWidth = MeasureSpec.getSize(widthMeasureSpec); int parentHeight = MeasureSpec.getSize(heightMeasureSpec); int height = getMeasuredHeight(); refitText(this.getText().toString(), parentWidth,height); this.setMeasuredDimension(parentWidth, height); } @Override protected void onTextChanged(final CharSequence text, final int start, final int before, final int after) { refitText(text.toString(), this.getWidth(),this.getHeight()); } @Override protected void onSizeChanged (int w, int h, int oldw, int oldh) { if (w != oldw) { refitText(this.getText().toString(), w,h); } } //Attributes private Paint mTestPaint; } 

受到之前海报的启发,我想分享我的解决scheme。 它使用适用于以前字体大小的比例因子,以使其适合可用空间。 除了防止TextViews onDraw方法出现意外的行为,它只是自己绘制文本。

 public class FontFitTextView extends TextView { // How much of the available space should be used in percent. private static final float MARGINHEIGHT = 0.8f; private static final float MARGINWIDTH = 0.8f; private Paint paint; private int viewWidth; private int viewHeight; private float textHeight; private float textWidth; public FontFitTextView(Context c) { this(c, null); } public FontFitTextView(Context c, AttributeSet attrs) { super(c, attrs); initComponent(); } // Default constructor override public FontFitTextView(Context c, AttributeSet attrs, int defStyle) { super(c, attrs, defStyle); initComponent(); } private void initComponent() { paint = new Paint(); paint.setTextSize(30); paint.setTextAlign(Align.CENTER); paint.setAntiAlias(true); } public void setFontColor(int c) { paint.setColor(c); } private void calcTextSize(String s, Canvas c) { float availableHeight = viewHeight; float availableWidth = viewWidth; // This value scales the old font up or down to match the available // space. float scale = 1.0f; // Rectangle for measuring the text dimensions Rect rect = new Rect(); float oldFontSize = paint.getTextSize(); // Calculate the space used with old font size paint.getTextBounds(s, 0, s.length(), rect); textWidth = rect.width(); textHeight = rect.height(); // find scale-value to fit the text horizontally float scaleWidth = 1f; if (textWidth > 0.0f) { scaleWidth = (availableWidth) / textWidth * MARGINWIDTH; } // find scale-value to fit the text vertically float scaleHeight = 1f; if (textHeight > 0.0f) { scaleHeight = (availableHeight) / textHeight * MARGINHEIGHT; } // We are always limited by the smaller one if (scaleWidth < scaleHeight) { scale = scaleWidth; } else { scale = scaleHeight; } // We apply the scale to the old font size to make it bigger or smaller float newFontSize = (oldFontSize * scale); paint.setTextSize(newFontSize); } /** * Calculates the origin on the Y-Axis (width) for the text in this view. * * @return */ private float calcStartDrawingPosX() { float left = getMeasuredWidth(); float centerY = left - (viewWidth / 2); return centerY; } /** * Calculates the origin on the Y-Axis (height) for the text in this view. * * @return */ private float calcStartDrawingPosY() { float bottom = getMeasuredHeight(); // The paint only centers horizontally, origin on the Y-Axis stays at // the bottom, thus we have to lift the origin additionally by the // height of the font. float centerX = bottom - (viewHeight / 2) + (textHeight / 2); return centerX; } @Override protected void onDraw(Canvas canvas) { String text = getText().toString(); if (text.length() > 0) { calcTextSize(text, canvas); canvas.drawText(text, calcStartDrawingPosX(), calcStartDrawingPosY(), paint); } }; @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { viewWidth = w; viewHeight = h; super.onSizeChanged(w, h, oldw, oldh); } } 
 /* get your context */ Context c = getActivity().getApplicationContext(); LinearLayout l = new LinearLayout(c); l.setOrientation(LinearLayout.VERTICAL); LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT, 0); l.setLayoutParams(params); l.setBackgroundResource(R.drawable.border); TextView tv=new TextView(c); tv.setText(" your text here"); /* set typeface if needed */ Typeface tf = Typeface.createFromAsset(c.getAssets(),"fonts/VERDANA.TTF"); tv.setTypeface(tf); // LayoutParams lp = new LayoutParams(); tv.setTextColor(Color.parseColor("#282828")); tv.setGravity(Gravity.CENTER | Gravity.BOTTOM); // tv.setLayoutParams(lp); tv.setTextSize(20); l.addView(tv); return l; 

这应该是一个简单的解决scheme:

 public void correctWidth(TextView textView, int desiredWidth) { Paint paint = new Paint(); Rect bounds = new Rect(); paint.setTypeface(textView.getTypeface()); float textSize = textView.getTextSize(); paint.setTextSize(textSize); String text = textView.getText().toString(); paint.getTextBounds(text, 0, text.length(), bounds); while (bounds.width() > desiredWidth) { textSize--; paint.setTextSize(textSize); paint.getTextBounds(text, 0, text.length(), bounds); } textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize); } 

用下面的代码扩展TextView并覆盖onDraw。 它将保持文本的纵横比,但是将其大小填充空间。 如果需要,您可以轻松修改代码以进行拉伸。

  @Override protected void onDraw(@NonNull Canvas canvas) { TextPaint textPaint = getPaint(); textPaint.setColor(getCurrentTextColor()); textPaint.setTextAlign(Paint.Align.CENTER); textPaint.drawableState = getDrawableState(); String text = getText().toString(); float desiredWidth = getMeasuredWidth() - getPaddingLeft() - getPaddingRight() - 2; float desiredHeight = getMeasuredHeight() - getPaddingTop() - getPaddingBottom() - 2; float textSize = textPaint.getTextSize(); for (int i = 0; i < 10; i++) { textPaint.getTextBounds(text, 0, text.length(), rect); float width = rect.width(); float height = rect.height(); float deltaWidth = width - desiredWidth; float deltaHeight = height - desiredHeight; boolean fitsWidth = deltaWidth <= 0; boolean fitsHeight = deltaHeight <= 0; if ((fitsWidth && Math.abs(deltaHeight) < 1.0) || (fitsHeight && Math.abs(deltaWidth) < 1.0)) { // close enough break; } float adjustX = desiredWidth / width; float adjustY = desiredHeight / height; textSize = textSize * (adjustY < adjustX ? adjustY : adjustX); // adjust text size textPaint.setTextSize(textSize); } float x = desiredWidth / 2f; float y = desiredHeight / 2f - rect.top - rect.height() / 2f; canvas.drawText(text, x, y, textPaint); } 

I wrote a short helper class that makes a textview fit within a certain width and adds ellipsize "…" at the end if the minimum textsize cannot be achieved.

Keep in mind that it only makes the text smaller until it fits or until the minimum text size is reached. To test with large sizes, set the textsize to a large number before calling the help method.

It takes Pixels, so if you are using values from dimen, you can call it like this:


 float minTextSizePx = getResources().getDimensionPixelSize(R.dimen.min_text_size); float maxTextWidthPx = getResources().getDimensionPixelSize(R.dimen.max_text_width); WidgetUtils.fitText(textView, text, minTextSizePx, maxTextWidthPx); 

This is the class I use:


 public class WidgetUtils { public static void fitText(TextView textView, String text, float minTextSizePx, float maxWidthPx) { textView.setEllipsize(null); int size = (int)textView.getTextSize(); while (true) { Rect bounds = new Rect(); Paint textPaint = textView.getPaint(); textPaint.getTextBounds(text, 0, text.length(), bounds); if(bounds.width() < maxWidthPx){ break; } if (size <= minTextSizePx) { textView.setEllipsize(TextUtils.TruncateAt.END); break; } size -= 1; textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, size); } } } 

If a tranformation like allCaps is set, speedplane's approach is buggy. I fixed it, resulting in the following code (sorry, my reputation does not allow me to add this as a comment to speedplane's solution):

 import android.content.Context; import android.graphics.Paint; import android.util.AttributeSet; import android.util.TypedValue; import android.widget.TextView; public class FontFitTextView extends TextView { public FontFitTextView(Context context) { super(context); initialise(); } public FontFitTextView(Context context, AttributeSet attrs) { super(context, attrs); initialise(); } private void initialise() { mTestPaint = new Paint(); mTestPaint.set(this.getPaint()); //max size defaults to the initially specified text size unless it is too small } /* Re size the font so the specified text fits in the text box * assuming the text box is the specified width. */ private void refitText(String text, int textWidth) { if (getTransformationMethod() != null) { text = getTransformationMethod().getTransformation(text, this).toString(); } if (textWidth <= 0) return; int targetWidth = textWidth - this.getPaddingLeft() - this.getPaddingRight(); float hi = 100; float lo = 2; final float threshold = 0.5f; // How close we have to be mTestPaint.set(this.getPaint()); while((hi - lo) > threshold) { float size = (hi+lo)/2; if(mTestPaint.measureText(text) >= targetWidth) hi = size; // too big else lo = size; // too small } // Use lo so that we undershoot rather than overshoot this.setTextSize(TypedValue.COMPLEX_UNIT_PX, lo); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int parentWidth = MeasureSpec.getSize(widthMeasureSpec); int height = getMeasuredHeight(); refitText(this.getText().toString(), parentWidth); this.setMeasuredDimension(parentWidth, height); } @Override protected void onTextChanged(final CharSequence text, final int start, final int before, final int after) { refitText(text.toString(), this.getWidth()); } @Override protected void onSizeChanged (int w, int h, int oldw, int oldh) { if (w != oldw) { refitText(this.getText().toString(), w); } } //Attributes private Paint mTestPaint; } 

I don't known this is correct way or not bt its working …take your view and check OnGlobalLayoutListener() and get textview linecount then set textSize.

  yourView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { if (textView.getLineCount()>=3) { textView.setTextSize(20); }else{ //add somthing } } }); 

Its very simple few line code..

I had this pain in my projects for soooo long until I found this library:

 compile 'me.grantland:autofittextview:0.2.+' 

You just need to add the xml by your needs and it's done. 例如:

 <me.grantland.widget.AutofitTextView android:layout_width="match_parent" android:layout_height="wrap_content" android:singleLine="true" android:maxLines="2" android:textSize="40sp" autofit:minTextSize="16sp" /> 

You can now do this without a third party library or a widget. It's built into TextView in API level 26. Add android:autoSizeTextType="uniform" to your TextView and set height to it. 就这样。

https://developer.android.com/guide/topics/ui/look-and-feel/autosizing-textview.html

 <?xml version="1.0" encoding="utf-8"?> <TextView android:layout_width="match_parent" android:layout_height="200dp" android:autoSizeTextType="uniform" /> 

You can also use TextViewCompat for compatibility.

Google already made this feature.

 <TextView android:layout_width="match_parent" android:layout_height="200dp" android:autoSizeTextType="uniform" /> 

https://developer.android.com/guide/topics/ui/look-and-feel/autosizing-textview.html