How to adjust text font size to fit textview How to adjust text font size to fit textview android android

How to adjust text font size to fit textview


The solution below incorporates all of the suggestions here. It starts with what was originally posted by Dunni. It uses a binary search like gjpc's, but it is a bit more readable. It also include's gregm's bug fixes and a bug-fix of my own.

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


I've written a class that extends TextView and does this. It just uses measureText as you suggest. Basically it has a maximum text size and minimum text size (which can be changed) and it just runs through the sizes between them in decrements of 1 until it finds the biggest one that will fit. Not particularly elegant, but I don't know of any other way.

Here is the code:

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


This is speedplane's FontFitTextView, but it only decreases font size if needed to make the text fit, and keeps its font size otherwise. It does not increase the font size to fit height.

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

Here is an example how it could be used in 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" />

This would keep the font size to 60sp as long as the text fits in width. If the text is longer, it will decrease font size. In this case, the TextViews height will also change because of height=wrap_content.

If you find any bugs, feel free to edit.