kankan
kankan

Reputation: 599

Calculate text size according to width of text area

I have a text that should be set to TextView with specified width. It needs to calculate the text size to fit it into TextView.

In other words: Is there a way to fit text into TextView area, like the ImageView scale type feature?

Upvotes: 32

Views: 46193

Answers (7)

Ultimo_m
Ultimo_m

Reputation: 4897

Now there is a new API for text auto sizing

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

<TextView
    android:layout_width="match_parent"
    android:layout_height="200dp"
    app:autoSizeTextType="uniform"
    pp:autoSizeMinTextSize="12sp"       
    app:autoSizeMaxTextSize="100sp".     
    app:autoSizeStepGranularity="2sp" /> 

Note: If you use autosizing, it is not recommended to use the value "wrap_content" for the layout_width or layout_height attributes

Upvotes: 0

artem
artem

Reputation: 16777

This solution in Kotlin takes into account both width growth and reduction:

    private fun TextView.updateTextSize(desiredTextViewWidth: Int, text: String) {
        var lastFitSize = 0f
        var foundMaxSize = false

        while (!foundMaxSize && lastFitSize < Float.MAX_VALUE) {
            paint.textSize = lastFitSize + 1

            foundMaxSize = paint.measureText(text) > desiredTextViewWidth
            if (!foundMaxSize) {
                lastFitSize = paint.textSize
            }
        }

        setTextSize(TypedValue.COMPLEX_UNIT_PX, lastFitSize)
    }

Upvotes: 0

FreddaP
FreddaP

Reputation: 141

I needed one that calculates best fit for both width and height, in pixels. This is my solution:

private static int getFitTextSize(Paint paint, int width, int height, String text) {
   int maxSizeToFitWidth = (int)((float)width / paint.measureText(text) * paint.getTextSize());
   return Math.min(maxSizeToFitWidth, height);
}

Upvotes: 3

Ben Barkay
Ben Barkay

Reputation: 5622

I also had to face the same problem when having to ensure that text fits into a specific box. The following is the best performing and most accurate solution that I have for it at the moment:

/**
 * A paint that has utilities dealing with painting text.
 * @author <a href="maillto:nospam">Ben Barkay</a>
 * @version 10, Aug 2014
 */
public class TextPaint extends android.text.TextPaint {
    /**
     * Constructs a new {@code TextPaint}.
     */
    public TextPaint() {
        super();
    }

    /**
     * Constructs a new {@code TextPaint} using the specified flags
     * @param flags
     */
    public TextPaint(int flags) {
        super(flags);
    }

    /**
     * Creates a new {@code TextPaint} copying the specified {@code source} state.
     * @param source The source paint to copy state from.
     */
    public TextPaint(Paint source) {
        super(source);
    }

    // Some more utility methods...

    /**
     * Calibrates this paint's text-size to fit the specified text within the specified width.
     * @param text      The text to calibrate for.
     * @param boxWidth  The width of the space in which the text has to fit.
     */
    public void calibrateTextSize(String text, float boxWidth) {
        calibrateTextSize(text, 0, Float.MAX_VALUE, boxWidth);
    }

    /**
     * Calibrates this paint's text-size to fit the specified text within the specified width.
     * @param text      The text to calibrate for.
     * @param min       The minimum text size to use.
     * @param max       The maximum text size to use.
     * @param boxWidth  The width of the space in which the text has to fit.
     */
    public void calibrateTextSize(String text, float min, float max, float boxWidth) {
        setTextSize(10);
        setTextSize(Math.max(Math.min((boxWidth/measureText(text))*10, max), min));
    }
}

This simply calculates the correct size rather than running a trial/error test.

You can use it like this:

float availableWidth = ...; // use your text view's width, or any other width.
String text = "Hi there";
TextPaint paint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
paint.setTypeface(...);
paint.calibrateTextSize(text, availableWidth);

Or otherwise, without the extra type:

/**
 * Calibrates this paint's text-size to fit the specified text within the specified width.
 * @param paint     The paint to calibrate.
 * @param text      The text to calibrate for.
 * @param min       The minimum text size to use.
 * @param max       The maximum text size to use.
 * @param boxWidth  The width of the space in which the text has to fit.
 */
public static void calibrateTextSize(Paint paint, String text, float min, float max, float boxWidth) {
    paint.setTextSize(10);
    paint.setTextSize(Math.max(Math.min((boxWidth/paint.measureText(text))*10, max), min));
}

Use like so:

float availableWidth = ...; // use your text view's width, or any other width.
String text = "Hi there";
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setTypeface(...);
calibrateTextSize(paint, text, 0, Float.MAX_VALUE, availableWidth);

Upvotes: 2

selevenguo
selevenguo

Reputation: 149

 public static float getFitTextSize(TextPaint paint, float width, String text) {
     float nowWidth = paint.measureText(text);
     float newSize = (float) width / nowWidth * paint.getTextSize();
     return newSize;
 }

Upvotes: 13

Hamzeh Soboh
Hamzeh Soboh

Reputation: 7710

This should be a simple solution:

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

Upvotes: 36

Natie
Natie

Reputation: 2284

If it is the Size of the space the Text takes your after then the following might help:

Paint paint = new Paint();
Rect bounds = new Rect();

int text_height = 0;
int text_width = 0;

paint.setTypeface(Typeface.DEFAULT);// your preference here
paint.setTextSize(25);// have this the same as your text size

String text = "Some random text";

paint.getTextBounds(text, 0, text.length(), bounds);

text_height =  bounds.height();
text_width =  bounds.width();

Edit (after comment): Use the above in reverse:

int text_height = 50;
int text_width = 200;

int text_check_w = 0;
int text_check_h = 0;

int incr_text_size = 1;
boolean found_desired_size = true;

while (found_desired_size){
    paint.setTextSize(incr_text_size);// have this the same as your text size

    String text = "Some random text";

    paint.getTextBounds(text, 0, text.length(), bounds);

    text_check_h =  bounds.height();
    text_check_w =  bounds.width();
    incr_text_size++;

if (text_height == text_check_h && text_width == text_check_w){
found_desired_size = false;
}
}
return incr_text_size; // this will be desired text size from bounds you already have

//this method may be tweaked a bit, but gives you an idea on what you can do

Upvotes: 29

Related Questions