How to scale/resize text to fit a TextView?
Solution 1
The AutofitTextView library from MavenCentral handles this nicely. The source hosted on Github(1k+ stars) at https://github.com/grantland/android-autofittextview
Add the following to your app/build.gradle
repositories {
mavenCentral()
}
dependencies {
implementation 'me.grantland:autofittextview:0.2.+'
}
Enable any View extending TextView in code:
AutofitHelper.create(textView);
Enable any View extending TextView in XML:
<me.grantland.widget.AutofitLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true"
/>
</me.grantland.widget.AutofitLayout>
Use the built in Widget in code or XML:
<me.grantland.widget.AutofitTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true"
/>
Solution 2
New since Android O:
https://developer.android.com/preview/features/autosizing-textview.html
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:autoSizeTextType="uniform"
android:autoSizeMinTextSize="12sp"
android:autoSizeMaxTextSize="100sp"
android:autoSizeStepGranularity="2sp"
/>
Solution 3
I have played with this for quite some time, trying to get my font sizes correct on a wide variety of 7" tablets (kindle fire, Nexus7, and some inexpensive ones in China with low-res screens) and devices.
The approach that finally worked for me is as follows. The "32" is an arbitrary factor that basically gives about 70+ characters across a 7" tablet horizontal line, which is a font size I was looking for. Adjust accordingly.
textView.setTextSize(getFontSize(activity));
public static int getFontSize (Activity activity) {
DisplayMetrics dMetrics = new DisplayMetrics();
activity.getWindowManager().getDefaultDisplay().getMetrics(dMetrics);
// lets try to get them back a font size realtive to the pixel width of the screen
final float WIDE = activity.getResources().getDisplayMetrics().widthPixels;
int valueWide = (int)(WIDE / 32.0f / (dMetrics.scaledDensity));
return valueWide;
}
Solution 4
I had the same problem and wrote a class that seems to work for me. Basically, I used a static layout to draw the text in a separate canvas and remeasure until I find a font size that fits. You can see the class posted in the topic below. I hope it helps.
Auto Scale TextView Text to Fit within Bounds
Solution 5
I was able to answer my own question using the following code (see below), but my solution was very specific to the application. For instance, this will probably only look good and/or work for a TextView sized to approx. 1/2 the screen (with also a 40px top margin and 20px side margins... no bottom margin).
The using this approach though, you can create your own similar implementation. The static method basically just looks at the number of characters and determines a scaling factor to apply to the TextView's text size, and then incrementally increases the text size until the overall height (an estimated height -- using the width of the text, the text height, and the width of the TextView) is just below that of the TextView. The parameters necessary to determine the scaling factor (i.e. the if/else if statements) were set by guess-and-check. You'll likely have to play around with the numbers to make it work for your particular application.
This isn't the most elegant solution, though it was easy to code and it works for me. Does anyone have a better approach?
public static void autoScaleTextViewTextToHeight(final TextView tv, String s)
{
float currentWidth=tv.getPaint().measureText(s);
int scalingFactor = 0;
final int characters = s.length();
//scale based on # of characters in the string
if(characters<5)
{
scalingFactor = 1;
}
else if(characters>=5 && characters<10)
{
scalingFactor = 2;
}
else if(characters>=10 && characters<15)
{
scalingFactor = 3;
}
else if(characters>=15 && characters<20)
{
scalingFactor = 3;
}
else if(characters>=20 && characters<25)
{
scalingFactor = 3;
}
else if(characters>=25 && characters<30)
{
scalingFactor = 3;
}
else if(characters>=30 && characters<35)
{
scalingFactor = 3;
}
else if(characters>=35 && characters<40)
{
scalingFactor = 3;
}
else if(characters>=40 && characters<45)
{
scalingFactor = 3;
}
else if(characters>=45 && characters<50)
{
scalingFactor = 3;
}
else if(characters>=50 && characters<55)
{
scalingFactor = 3;
}
else if(characters>=55 && characters<60)
{
scalingFactor = 3;
}
else if(characters>=60 && characters<65)
{
scalingFactor = 3;
}
else if(characters>=65 && characters<70)
{
scalingFactor = 3;
}
else if(characters>=70 && characters<75)
{
scalingFactor = 3;
}
else if(characters>=75)
{
scalingFactor = 5;
}
//System.out.println(((int)Math.ceil(currentWidth)/tv.getWidth()+scalingFactor));
//the +scalingFactor is important... increase this if nec. later
while((((int)Math.ceil(currentWidth)/tv.getWidth()+scalingFactor)*tv.getTextSize())<tv.getHeight())
{
tv.setTextSize(TypedValue.COMPLEX_UNIT_SP, tv.getTextSize()+0.25f);
currentWidth=tv.getPaint().measureText(s);
//System.out.println(((int)Math.ceil(currentWidth)/tv.getWidth()+scalingFactor));
}
tv.setText(s);
}
Thanks.
RyanM
Updated on October 14, 2021Comments
-
RyanM over 2 years
I'm trying to create a method for resizing multi-line text in a
TextView
such that it fits within the bounds (both the X and Y dimensions) of theTextView
.At present, I have something, but all it does is resize the text such that just the first letter/character of the text fills the dimensions of the
TextView
(i.e. only the first letter is viewable, and it's huge). I need it to fit all the lines of the text within the bounds of the TextView.Here is what I have so far:
public static void autoScaleTextViewTextToHeight(TextView tv) { final float initSize = tv.getTextSize(); //get the width of the view's back image (unscaled).... float minViewHeight; if(tv.getBackground()!=null) { minViewHeight = tv.getBackground().getIntrinsicHeight(); } else { minViewHeight = 10f;//some min. } final float maxViewHeight = tv.getHeight() - (tv.getPaddingBottom()+tv.getPaddingTop())-12;// -12 just to be sure final String s = tv.getText().toString(); //System.out.println(""+tv.getPaddingTop()+"/"+tv.getPaddingBottom()); if(minViewHeight >0 && maxViewHeight >2) { Rect currentBounds = new Rect(); tv.getPaint().getTextBounds(s, 0, s.length(), currentBounds); //System.out.println(""+initSize); //System.out.println(""+maxViewHeight); //System.out.println(""+(currentBounds.height())); float resultingSize = 1; while(currentBounds.height() < maxViewHeight) { resultingSize ++; tv.setTextSize(resultingSize); tv.getPaint().getTextBounds(s, 0, s.length(), currentBounds); //System.out.println(""+(currentBounds.height()+tv.getPaddingBottom()+tv.getPaddingTop())); //System.out.println("Resulting: "+resultingSize); } if(currentBounds.height()>=maxViewHeight) { //just to be sure, reduce the value tv.setTextSize(resultingSize-1); } } }
I think the problem is in the use of
tv.getPaint().getTextBounds(...)
. It always returns small numbers for the text bounds... small relative to thetv.getWidth()
andtv.getHeight()
values... even if the text size is far larger than the width or height of theTextView
. -
RyanM about 14 yearsThanks, but I want the text to be displayed on multiple lines. I just need all lines to remain within the bounds of the textview and be as big as possible.
-
gh. over 11 yearsDevelop the working model for your native language (or the first one to be demonstrated) and swap counterparts for multi-language support after successful testing.
-
a54studio about 11 yearsThanks for posting this. This works perfect and I am going to modify it to accept text size going in and then spit the correct one out.
-
Mark JW about 11 yearsNice suggested improvement, yes, you can specify a target size going in and then it can set up the universal size for all devices.
-
Mark about 11 yearsAny reason not to collapse the range of 15 to 75 characters to one statement?
-
Mixaz about 8 yearsThanks, excellent answer, I would upvote twice for it! It took me a while to understand that
TextView
s textSize attribute is used now as a max size limit, so you must specify it as big as possible... -
MK. almost 8 yearsI liked the look of this library, but it looks like it doesn't really support StyleSpans, they are likely to cause text cutoff.
-
Offek almost 7 yearsPerfect solution! Similar functionality to iOS's adjustsFontSizeToFitWidth
-
letroll over 6 yearsor if you use support lib: app:autoSizeTextType="uniform" app:autoSizeMinTextSize="12sp" app:autoSizeMaxTextSize="100sp" app:autoSizeStepGranularity="2sp" with the use of : xmlns:app="schemas.android.com/apk/res-auto"