Home » Android » Auto-fit TextView for Android

Auto-fit TextView for Android

Posted by: admin March 10, 2020 Leave a comment

Questions:

Background

Many times we need to auto-fit the font of the TextView to the boundaries given to it.

The problem

Sadly, even though there are many threads and posts (and suggested solutions) talking about this problem (example here, here and here), none of them actually work well.

That’s why, I’ve decided to test each of them till I find the real deal.

I think that the requirements from such a textView should be:

  1. Should allow using any font, typeface, style, and set of characters.

  2. Should handle both width and height

  3. No truncation unless text cannot fit because of the limitation, we’ve
    given to it (example: too long text, too small available size). However, we could request for horizontal/vertical scrollbar if we wish, just for those cases.

  4. Should allow multi-line or single-line. In case of multi-line, allow max & min lines.

  5. Should not be slow in computation. Using a loop for finding the best size? At least optimize it and don’t increment your sampling by 1 each time.

  6. In case of multi-line, should allow to prefer resizing or using more lines, and/or allow to choose the lines ourselves by using the “\n” character.

What I’ve tried

I’ve tried so many samples (including those of the links, I’ve written about), and I’ve also tried to modify them to handle the cases, I’ve talked about, but none really work.

I’ve made a sample project that allows me to visually see if the TextView auto-fits correctly.

Currently, my sample project only randomize the text (the English alphabet plus digits) and the size of the textView, and let it stay with single line, but even this doesn’t work well on any of the samples I’ve tried.

Here’s the code (also available here):

File res/layout/activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
  android:layout_height="match_parent" tools:context=".MainActivity">
  <Button android:id="@+id/button1" android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignParentBottom="true"
    android:layout_centerHorizontal="true" android:text="Button" />
  <FrameLayout android:layout_width="match_parent"
    android:layout_height="wrap_content" android:layout_above="@+id/button1"
    android:layout_alignParentLeft="true" android:background="#ffff0000"
    android:layout_alignParentRight="true" android:id="@+id/container"
    android:layout_alignParentTop="true" />

</RelativeLayout>

File src/.../MainActivity.java

public class MainActivity extends Activity
  {
  private final Random        _random            =new Random();
  private static final String ALLOWED_CHARACTERS ="qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890";

  @Override
  protected void onCreate(final Bundle savedInstanceState)
    {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    final ViewGroup container=(ViewGroup)findViewById(R.id.container);
    findViewById(R.id.button1).setOnClickListener(new OnClickListener()
      {
        @Override
        public void onClick(final View v)
          {
          container.removeAllViews();
          final int maxWidth=container.getWidth();
          final int maxHeight=container.getHeight();
          final FontFitTextView fontFitTextView=new FontFitTextView(MainActivity.this);
          final int width=_random.nextInt(maxWidth)+1;
          final int height=_random.nextInt(maxHeight)+1;
          fontFitTextView.setLayoutParams(new LayoutParams(width,height));
          fontFitTextView.setSingleLine();
          fontFitTextView.setBackgroundColor(0xff00ff00);
          final String text=getRandomText();
          fontFitTextView.setText(text);
          container.addView(fontFitTextView);
          Log.d("DEBUG","width:"+width+" height:"+height+" text:"+text);
          }
      });
    }

  private String getRandomText()
    {
    final int textLength=_random.nextInt(20)+1;
    final StringBuilder builder=new StringBuilder();
    for(int i=0;i<textLength;++i)
      builder.append(ALLOWED_CHARACTERS.charAt(_random.nextInt(ALLOWED_CHARACTERS.length())));
    return builder.toString();
    }
  }

The question

Does anybody know of a solution for this common problem that actually work?

Even a solution that has much less features that what I’ve written about, for example one that has just a constant number of lines of text, and adjusts its font according to its size, yet never have weird glitches and having the text get too large/small compared to its available space.


GitHub project

Since this is such an important TextView, I’ve decided to publish a library, so that everyone could easily use it, and contribute to it, here.

How to&Answers:

Thanks to MartinH’s simple fix here, this code also takes care of android:drawableLeft, android:drawableRight, android:drawableTop and android:drawableBottom tags.


My answer here should make you happy Auto Scale TextView Text to Fit within Bounds

I have modified your test case:

@Override
protected void onCreate(final Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    final ViewGroup container = (ViewGroup) findViewById(R.id.container);
    findViewById(R.id.button1).setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(final View v) {
            container.removeAllViews();
            final int maxWidth = container.getWidth();
            final int maxHeight = container.getHeight();
            final AutoResizeTextView fontFitTextView = new AutoResizeTextView(MainActivity.this);
            final int width = _random.nextInt(maxWidth) + 1;
            final int height = _random.nextInt(maxHeight) + 1;
            fontFitTextView.setLayoutParams(new FrameLayout.LayoutParams(
                    width, height));
            int maxLines = _random.nextInt(4) + 1;
            fontFitTextView.setMaxLines(maxLines);
            fontFitTextView.setTextSize(500);// max size
            fontFitTextView.enableSizeCache(false);
            fontFitTextView.setBackgroundColor(0xff00ff00);
            final String text = getRandomText();
            fontFitTextView.setText(text);
            container.addView(fontFitTextView);
            Log.d("DEBUG", "width:" + width + " height:" + height
                    + " text:" + text + " maxLines:" + maxLines);
        }
    });
}

I am posting code here at per android developer’s request:

Final effect:

Enter image description here

Sample Layout file:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp" >

<com.vj.widgets.AutoResizeTextView
    android:layout_width="match_parent"
    android:layout_height="100dp"
    android:ellipsize="none"
    android:maxLines="2"
    android:text="Auto Resized Text, max 2 lines"
    android:textSize="100sp" /> <!-- maximum size -->

<com.vj.widgets.AutoResizeTextView
    android:layout_width="match_parent"
    android:layout_height="100dp"
    android:ellipsize="none"
    android:gravity="center"
    android:maxLines="1"
    android:text="Auto Resized Text, max 1 line"
    android:textSize="100sp" /> <!-- maximum size -->

<com.vj.widgets.AutoResizeTextView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="Auto Resized Text"
    android:textSize="500sp" /> <!-- maximum size -->

</LinearLayout>

And the Java code:

Warning:

Beware of this resolved bug in Android 3.1 (Honeycomb) though.

Answer:

I’ve modified M-WaJeEh’s answer a bit to take into account compound drawables on the sides.

The getCompoundPaddingXXXX() methods return padding of the view + drawable space. See for example: TextView.getCompoundPaddingLeft()

Issue:
This fixes the measurement of the width and height of the TextView space available for the text. If we don’t take the drawable size into account, it is ignored and the text will end up overlapping the drawable.


Updated segment adjustTextSize(String):

Answer:

Ok I have used the last week to massively rewrite my code to precisely fit your test. You can now copy this 1:1 and it will immediately work – including setSingleLine(). Please remember to adjust MIN_TEXT_SIZE and MAX_TEXT_SIZE if you’re going for extreme values.

Converging algorithm looks like this:

And the whole class can be found here.

The result is very flexible now. This works the same declared in xml like so:

… as well as built programmatically like in your test.

I really hope you can use this now. You can call setText(CharSequence text) now to use it by the way. The class takes care of stupendously rare exceptions and should be rock-solid. The only thing that the algorithm does not support yet is:

  • Calls to setMaxLines(x) where x >= 2

But I have added extensive comments to help you build this if you wish to!


Please note:

If you just use this normally without limiting it to a single line then there might be word-breaking as you mentioned before. This is an Android feature, not the fault of the AutoFitText. Android will always break words that are too long for a TextView and it’s actually quite a convenience. If you want to intervene here than please see my comments and code starting at line 203. I have already written an adequate split and the recognition for you, all you’d need to do henceforth is to devide the words and then modify as you wish.

In conclusion: You should highly consider rewriting your test to also support space chars, like so:

This will produce very beutiful (and more realistic) results.
You will find commenting to get you started in this matter as well.

Good luck and best regards

Answer:

My requirement is to

  • Click on the ScalableTextView
  • Open a listActivity and display various length string items.
  • Select a text from list.
  • Set the text back to the ScalableTextView in another activity.

I referred the link: Auto Scale TextView Text to Fit within Bounds (including comments) and also the DialogTitle.java

I found that the solution provided is nice and simple but it does not dynamically change the size of the text box. It works great when
the selected text length from the list view is greater in size than the existing text lenght in the ScalableTextView. When selected the text having length smaller than the existing text in the ScalableTextView, it do not increase the size of the text, showing the text in the smaller size.

I modified the ScalableTextView.java to readjust the text size based on the text length. Here is my ScalableTextView.java

Happy Coding….

Answer:

I’ll explain how works this attribute lower android versions step by step:

1- Import android support library 26.x.x on your project gradle file. If there is no support library on IDE, they will download automatically.

2- Open your layout XML file and refactor like this tag your TextView. This scenario is: when incrased font size on system, fit text to avaliable width, not word wrap.

Answer:

Warning, bug in Android 3 (Honeycomb) and Android 4.0 (Ice Cream Sandwich)

Androids versions: 3.1 – 4.04 have a bug, that setTextSize() inside of TextView works only for the first time (first invocation).

The bug is described in Issue 22493: TextView height bug in Android 4.0 and Issue 17343: button’s height and text does not return to its original state after increase and decrease the text size on HoneyComb.

The workaround is to add a newline character to text assigned to TextView before changing size:

I use it in my code as follow:

I add this “\u3000” character on left and right of my text, to keep it centered. If you have it aligned to left then append to the right only. Of course it can be also embedded with AutoResizeTextView widget, but I wanted to keep fix code outside.

Answer:

There’s now an official solution to this problem. Autosizing TextViews introduced with Android O are available in the Support Library 26 and is backwards compatible all the way down to Android 4.0.

https://developer.android.com/preview/features/autosizing-textview.html

I’m not sure why https://stackoverflow.com/a/42940171/47680 which also included this information was deleted by an admin.

Answer:

From June 2018 Android officially support this feature for Android 4.0 (API level 14) and higher.
With Android 8.0 (API level 26) and higher:

Android versions prior to Android 8.0 (API level 26):

Check out my detail answer.

Answer:

Convert the text view to an image, and the scale the image within the boundaries.

Here’s an example on how to convert a view to an Image: Converting a view to Bitmap without displaying it in Android?

The problem is, your text will not be selectable, but it should do the trick. I haven’t tried it, so I’m not sure how it would look (because of the scaling).

Answer:

Below is avalancha TextView with added functionality for custom Font.

Usage:

Don’t forget to add: xmlns:foo=”http://schemas.android.com/apk/res-auto”. Font should be in assets firectory

import java.util.ArrayList; import java.util.List; import android.annotation.SuppressLint; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Paint; import android.graphics.Typeface; import android.os.Build; import android.util.AttributeSet; import android.util.Log; import android.util.TypedValue; import android.view.View; import android.view.ViewGroup.LayoutParams; import android.view.ViewTreeObserver; import android.view.ViewTreeObserver.OnGlobalLayoutListener; import android.widget.TextView; import de.meinprospekt.androidhd.R; import de.meinprospekt.androidhd.adapter.BrochuresHorizontalAdapter; import de.meinprospekt.androidhd.util.LOG; /** * https://stackoverflow.com/a/16174468/2075875 This class builds a new android Widget named AutoFitText which can be used instead of a TextView to * have the text font size in it automatically fit to match the screen width. Credits go largely to Dunni, gjpc, gregm and speedplane from * Stackoverflow, method has been (style-) optimized and rewritten to match android coding standards and our MBC. This version upgrades the original * "AutoFitTextView" to now also be adaptable to height and to accept the different TextView types (Button, TextClock etc.) * * @author pheuschk * @createDate: 18.04.2013 * * combined with: https://stackoverflow.com/a/7197867/2075875 */ @SuppressWarnings("unused") public class AutoFitText extends TextView { private static final String TAG = AutoFitText.class.getSimpleName(); /** Global min and max for text size. Remember: values are in pixels! */ private final int MIN_TEXT_SIZE = 10; private final int MAX_TEXT_SIZE = 400; /** Flag for singleLine */ private boolean mSingleLine = false; /** * A dummy {@link TextView} to test the text size without actually showing anything to the user */ private TextView mTestView; /** * A dummy {@link Paint} to test the text size without actually showing anything to the user */ private Paint mTestPaint; /** * Scaling factor for fonts. It's a method of calculating independently (!) from the actual density of the screen that is used so users have the * same experience on different devices. We will use DisplayMetrics in the Constructor to get the value of the factor and then calculate SP from * pixel values */ private float mScaledDensityFactor; /** * Defines how close we want to be to the factual size of the Text-field. Lower values mean higher precision but also exponentially higher * computing cost (more loop runs) */ private final float mThreshold = 0.5f; /** * Constructor for call without attributes --> invoke constructor with AttributeSet null * * @param context */ public AutoFitText(Context context) { this(context, null); } public AutoFitText(Context context, AttributeSet attrs) { super(context, attrs); init(context, attrs); } public AutoFitText(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(context, attrs); } private void init(Context context, AttributeSet attrs) { //TextViewPlus part https://stackoverflow.com/a/7197867/2075875 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.AutoFitText); String customFont = a.getString(R.styleable.AutoFitText_customFont); setCustomFont(context, customFont); a.recycle(); // AutoFitText part mScaledDensityFactor = context.getResources().getDisplayMetrics().scaledDensity; mTestView = new TextView(context); mTestPaint = new Paint(); mTestPaint.set(this.getPaint()); this.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() { @Override public void onGlobalLayout() { // make an initial call to onSizeChanged to make sure that refitText is triggered onSizeChanged(AutoFitText.this.getWidth(), AutoFitText.this.getHeight(), 0, 0); // Remove the LayoutListener immediately so we don't run into an infinite loop //AutoFitText.this.getViewTreeObserver().removeOnGlobalLayoutListener(this); removeOnGlobalLayoutListener(AutoFitText.this, this); } }); } public boolean setCustomFont(Context ctx, String asset) { Typeface tf = null; try { tf = Typeface.createFromAsset(ctx.getAssets(), asset); } catch (Exception e) { LOG.e(TAG, "Could not get typeface: "+e.getMessage()); return false; } setTypeface(tf); return true; } @SuppressLint("NewApi") public static void removeOnGlobalLayoutListener(View v, ViewTreeObserver.OnGlobalLayoutListener listener){ if (Build.VERSION.SDK_INT < 16) { v.getViewTreeObserver().removeGlobalOnLayoutListener(listener); } else { v.getViewTreeObserver().removeOnGlobalLayoutListener(listener); } } /** * Main method of this widget. Resizes the font so the specified text fits in the text box assuming the text box has the specified width. This is * done via a dummy text view that is refit until it matches the real target width and height up to a certain threshold factor * * @param targetFieldWidth The width that the TextView currently has and wants filled * @param targetFieldHeight The width that the TextView currently has and wants filled */ private void refitText(String text, int targetFieldWidth, int targetFieldHeight) { // Variables need to be visible outside the loops for later use. Remember size is in pixels float lowerTextSize = MIN_TEXT_SIZE; float upperTextSize = MAX_TEXT_SIZE; // Force the text to wrap. In principle this is not necessary since the dummy TextView // already does this for us but in rare cases adding this line can prevent flickering this.setMaxWidth(targetFieldWidth); // Padding should not be an issue since we never define it programmatically in this app // but just to to be sure we cut it off here targetFieldWidth = targetFieldWidth - this.getPaddingLeft() - this.getPaddingRight(); targetFieldHeight = targetFieldHeight - this.getPaddingTop() - this.getPaddingBottom(); // Initialize the dummy with some params (that are largely ignored anyway, but this is // mandatory to not get a NullPointerException) mTestView.setLayoutParams(new LayoutParams(targetFieldWidth, targetFieldHeight)); // maxWidth is crucial! Otherwise the text would never line wrap but blow up the width mTestView.setMaxWidth(targetFieldWidth); if (mSingleLine) { // the user requested a single line. This is very easy to do since we primarily need to // respect the width, don't have to break, don't have to measure... /*************************** Converging algorithm 1 ***********************************/ for (float testSize; (upperTextSize - lowerTextSize) > mThreshold;) { // Go to the mean value... testSize = (upperTextSize + lowerTextSize) / 2; mTestView.setTextSize(TypedValue.COMPLEX_UNIT_SP, testSize / mScaledDensityFactor); mTestView.setText(text); mTestView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED); if (mTestView.getMeasuredWidth() >= targetFieldWidth) { upperTextSize = testSize; // Font is too big, decrease upperSize } else { lowerTextSize = testSize; // Font is too small, increase lowerSize } } /**************************************************************************************/ // In rare cases with very little letters and width > height we have vertical overlap! mTestView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED); if (mTestView.getMeasuredHeight() > targetFieldHeight) { upperTextSize = lowerTextSize; lowerTextSize = MIN_TEXT_SIZE; /*************************** Converging algorithm 1.5 *****************************/ for (float testSize; (upperTextSize - lowerTextSize) > mThreshold;) { // Go to the mean value... testSize = (upperTextSize + lowerTextSize) / 2; mTestView.setTextSize(TypedValue.COMPLEX_UNIT_SP, testSize / mScaledDensityFactor); mTestView.setText(text); mTestView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED); if (mTestView.getMeasuredHeight() >= targetFieldHeight) { upperTextSize = testSize; // Font is too big, decrease upperSize } else { lowerTextSize = testSize; // Font is too small, increase lowerSize } } /**********************************************************************************/ } } else { /*********************** Converging algorithm 2 ***************************************/ // Upper and lower size converge over time. As soon as they're close enough the loop // stops // TODO probe the algorithm for cost (ATM possibly O(n^2)) and optimize if possible for (float testSize; (upperTextSize - lowerTextSize) > mThreshold;) { // Go to the mean value... testSize = (upperTextSize + lowerTextSize) / 2; // ... inflate the dummy TextView by setting a scaled textSize and the text... mTestView.setTextSize(TypedValue.COMPLEX_UNIT_SP, testSize / mScaledDensityFactor); mTestView.setText(text); // ... call measure to find the current values that the text WANTS to occupy mTestView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED); int tempHeight = mTestView.getMeasuredHeight(); // int tempWidth = mTestView.getMeasuredWidth(); // LOG.debug("Measured: " + tempWidth + "x" + tempHeight); // LOG.debug("TextSize: " + testSize / mScaledDensityFactor); // ... decide whether those values are appropriate. if (tempHeight >= targetFieldHeight) { upperTextSize = testSize; // Font is too big, decrease upperSize } else { lowerTextSize = testSize; // Font is too small, increase lowerSize } } /**************************************************************************************/ // It is possible that a single word is wider than the box. The Android system would // wrap this for us. But if you want to decide fo yourself where exactly to break or to // add a hyphen or something than you're going to want to implement something like this: mTestPaint.setTextSize(lowerTextSize); List<String> words = new ArrayList<String>(); for (String s : text.split(" ")) { Log.i("tag", "Word: " + s); words.add(s); } for (String word : words) { if (mTestPaint.measureText(word) >= targetFieldWidth) { List<String> pieces = new ArrayList<String>(); // pieces = breakWord(word, mTestPaint.measureText(word), targetFieldWidth); // Add code to handle the pieces here... } } } /** * We are now at most the value of threshold away from the actual size. To rather undershoot than overshoot use the lower value. To match * different screens convert to SP first. See {@link http://developer.android.com/guide/topics/resources/more-resources.html#Dimension} for * more details */ this.setTextSize(TypedValue.COMPLEX_UNIT_SP, lowerTextSize / mScaledDensityFactor); return; } /** * This method receives a call upon a change in text content of the TextView. Unfortunately it is also called - among others - upon text size * change which means that we MUST NEVER CALL {@link #refitText(String)} from this method! Doing so would result in an endless loop that would * ultimately result in a stack overflow and termination of the application * * So for the time being this method does absolutely nothing. If you want to notify the view of a changed text call {@link #setText(CharSequence)} */ @Override protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) { // Super implementation is also intentionally empty so for now we do absolutely nothing here super.onTextChanged(text, start, lengthBefore, lengthAfter); } @Override protected void onSizeChanged(int width, int height, int oldWidth, int oldHeight) { if (width != oldWidth && height != oldHeight) { refitText(this.getText().toString(), width, height); } } /** * This method is guaranteed to be called by {@link TextView#setText(CharSequence)} immediately. Therefore we can safely add our modifications * here and then have the parent class resume its work. So if text has changed you should always call {@link TextView#setText(CharSequence)} or * {@link TextView#setText(CharSequence, BufferType)} if you know whether the {@link BufferType} is normal, editable or spannable. Note: the * method will default to {@link BufferType#NORMAL} if you don't pass an argument. */ @Override public void setText(CharSequence text, BufferType type) { int targetFieldWidth = this.getWidth(); int targetFieldHeight = this.getHeight(); if (targetFieldWidth <= 0 || targetFieldHeight <= 0 || text.equals("")) { // Log.v("tag", "Some values are empty, AutoFitText was not able to construct properly"); } else { refitText(text.toString(), targetFieldWidth, targetFieldHeight); } super.setText(text, type); } /** * TODO add sensibility for {@link #setMaxLines(int)} invocations */ @Override public void setMaxLines(int maxLines) { // TODO Implement support for this. This could be relatively easy. The idea would probably // be to manipulate the targetHeight in the refitText-method and then have the algorithm do // its job business as usual. Nonetheless, remember the height will have to be lowered // dynamically as the font size shrinks so it won't be a walk in the park still if (maxLines == 1) { this.setSingleLine(true); } else { throw new UnsupportedOperationException("MaxLines != 1 are not implemented in AutoFitText yet, use TextView instead"); } } @Override public void setSingleLine(boolean singleLine) { // save the requested value in an instance variable to be able to decide later mSingleLine = singleLine; super.setSingleLine(singleLine); } } 

known bugs:
Doesn’t work with Android 4.03 – fonts are invisible or very small (original avalancha doesn’t work too)
below is workaround for that bug: https://stackoverflow.com/a/21851239/2075875

Answer:

Try this

TextWatcher changeText = new TextWatcher() { @Override public void onTextChanged(CharSequence s, int start, int before, int count) { tv3.setText(et.getText().toString()); tv3.post(new Runnable() { @Override public void run() { while(tv3.getLineCount() >= 3){ tv3.setTextSize((tv3.getTextSize())-1); } } }); } @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {} @Override public void afterTextChanged(Editable s) { } }; 

Answer:

If you are looking for something easier:

 public MyTextView extends TextView{ public void resize(String text, float textViewWidth, float textViewHeight) { Paint p = new Paint(); Rect bounds = new Rect(); p.setTextSize(1); p.getTextBounds(text, 0, text.length(), bounds); float widthDifference = (textViewWidth)/bounds.width(); float heightDifference = (textViewHeight); textSize = Math.min(widthDifference, heightDifference); setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize); } 

Answer:

Quick fix for the issue described by @Malachiasz

I’ve fixed the issue by adding custom support for this in the auto resize class:

public void setTextCompat(final CharSequence text) { setTextCompat(text, BufferType.NORMAL); } public void setTextCompat(final CharSequence text, BufferType type) { // Quick fix for Android Honeycomb and Ice Cream Sandwich which sets the text only on the first call if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1 && Build.VERSION.SDK_INT <= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) { super.setText(DOUBLE_BYTE_WORDJOINER + text + DOUBLE_BYTE_WORDJOINER, type); } else { super.setText(text, type); } } @Override public CharSequence getText() { String originalText = super.getText().toString(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1 && Build.VERSION.SDK_INT <= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) { // We try to remove the word joiners we added using compat method - if none found - this will do nothing. return originalText.replaceAll(DOUBLE_BYTE_WORDJOINER, ""); } else { return originalText; } } 

Just call yourView.setTextCompat(newTextValue) instead of yourView.setText(newTextValue)

Answer:

Try adding LayoutParams and MaxWidth and MaxHeight to the TextView. It will force the layout to respect the parent container and not overflow.

textview.setLayoutParams(new LayoutParams(LinearLayout.MATCH_PARENT,LinearLayout.WRAP_CONTENT)); int GeneralApproxWidthOfContainer = 400; int GeneralApproxHeightOfContainer = 600; textview.setMaxWidth(400); textview.setMaxHeight(600);` 

Answer:

Since Android O, it’s possible to auto resize text in xml:

https://developer.android.com/preview/features/autosizing-textview.html

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

Android O allows you to instruct a TextView to let the text size
expand or contract automatically to fill its layout based on the
TextView’s characteristics and boundaries. This setting makes it
easier to optimize the text size on different screens with dynamic
content.

The Support Library 26.0 Beta provides full support to the autosizing
TextView feature on devices running Android versions prior to Android
O. The library provides support to Android 4.0 (API level 14) and
higher. The android.support.v4.widget package contains the
TextViewCompat class to access features in a backward-compatible
fashion.

Answer:

After i tried Android official Autosizing TextView, i found if your Android version is prior to Android 8.0 (API level 26), you need use android.support.v7.widget.AppCompatTextView, and make sure your support library version is above 26.0.0. Example:

<android.support.v7.widget.AppCompatTextView android:layout_width="130dp" android:layout_height="32dp" android:maxLines="1" app:autoSizeMaxTextSize="22sp" app:autoSizeMinTextSize="12sp" app:autoSizeStepGranularity="2sp" app:autoSizeTextType="uniform" /> 

update:

According to @android-developer’s reply, i check the AppCompatActivity source code, and found these two lines in onCreate

final AppCompatDelegate delegate = getDelegate();
delegate.installViewFactory();

and in AppCompatDelegateImpl‘s createView

 if (mAppCompatViewInflater == null) { mAppCompatViewInflater = new AppCompatViewInflater(); } 

it use AppCompatViewInflater inflater view, when AppCompatViewInflater createView it will use AppCompatTextView for “TextView”.

public final View createView(){ ... View view = null; switch (name) { case "TextView": view = new AppCompatTextView(context, attrs); break; case "ImageView": view = new AppCompatImageView(context, attrs); break; case "Button": view = new AppCompatButton(context, attrs); break; ... } 

In my project i don’t use AppCompatActivity, so i need use <android.support.v7.widget.AppCompatTextView> in xml.