I'm looking for an optimal way to resize wrapping text in a TextView
so that it will fit within its getHeight and getWidth bounds. I'm not simply looking for a way to wrap the text- I want to make sure it both wraps and is small enough to fit entirely on the screen.
I've seen a few cases on StackOverflow where auto resizing was needed, but they are either very special cases with hack solutions, have no solution, or involve re-drawing the TextView
recursively until it is small enough (which is memory intense and forces the user to watch the text shrink step-by-step with every recursion).
But I'm sure somebody out there has found a good solution that doesn't involve what I'm doing: writing several heavy routines that parse and measure the text, resize the text, and repeat until a suitably small size has been found.
What routines does TextView
use to wrap the text? Couldn't those be somehow used to predict whether text will be small enough?
tl;dr: is there a best-practice way to auto-resize a TextView
to fit, wrapped, in its getHeight and getWidth bounds?
My implementation is a bit more complex, but comes with the following goodies:
[revised on 2012-11-21]
Providing this version of top answer rewritten on C# for those who codes on Xamarin.Android. Worked for me well.
I combined some of the above suggestions to make one that scales up and down, with bisection method. It also scales within the width.
I started with Chase's solution, but had to adapt two things before it was working as expected on my device (Galaxy Nexus, Android 4.1):
using a copy of TextPaint for measuring layout The documentation for TextView.getPaint() states that it should be used read-only, so I made a copy in both places where we use the paint object for measuring:
adding a unit to setting the text size
With these two modifications the solution is working perfectly for me, thanks Chase! I don't know whether it is due to Android 4.x that the original solution was not working. In case you want to see it in action or test whether it really works on your device, you can have a look at my flashcard app Flashcards ToGo where I use this solution to scale the text of a flashcard. The text can have arbitrary length, and the flashcards are displayed in different activities, sometimes smaller sometimes bigger, plus in landscape + portrait mode, and I haven't found any corner case where the solution would not work properly...
I found the following to work nicely for me. It doesn't loop and accounts for both height and width. Note that it is important to specify the PX unit when calling setTextSize on the view.
Here is the routine I use, passing in the getPaint() from the view. A 10 character string with a 'wide' character is used to estimate the width independent from the actual string.
Here's an enumeration of what else I've found for anyone still searching:
1) Here's a solution that recursively re-paints the textview until it fits. This means literally watching your text shrink into place, but at least it fits when it's done. The code will need some tweaking to implement, but it's mostly there.
2) You can try hacking together a custom solution like this, or dunni's class in this, which is what I did using the getPaint().measureText(str) to search for the right size, but it got a lot messier since I need it to wrap only on whitespace...
3) You can keep searching- I've tried more alternatives than I can count. Ted's advice on StaticLayout hasn't paid off for me but maybe there's something there; I tried using the StaticLayout.getEllipsis(line) to determine if text was going off screen, to no effect. See my (presently un-answered) post about that here.