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?
Here's yet another solution, just for kicks. It's probably not very efficient, but it does cope with both height and width of the text, and with marked-up text.
You can use the
android.text.StaticLayout
class for this. That's whatTextView
uses internally.AppcompatTextView now supports auto sizing starting from Support Library 26.0. TextView in Android O also works same way. More info can be found here. A simple demo app can be found here.
I started with Chase's AutoResizeTextView class, and made a minor change so it would fit both vertically and horizontally.
I also discovered a bug which causes a Null Pointer Exception in the Layout Editor (in Eclipse) under some rather obscure conditions.
Change 1: Fit the text both vertically and horizontally
Chase's original version reduces the text size until it fits vertically, but allows the text to be wider than the target. In my case, I needed the text to fit a specified width.
This change makes it resize until the text fits both vertically and horizontally.
In
resizeText(
int,
int)
change from:to:
Then, at the end of the file, append the
getTextWidth()
routine; it's just a slightly modifiedgetTextHeight()
. It probably would be more efficient to combine them to one routine which returns both height and width.Change 2: Fix a EmptyStackException in the Eclipse Android Layout Editor
Under rather obscure and very precise conditions, the Layout Editor will fail to display the graphical display of the layout; it will throw an "EmptyStackException: null" exception in com.android.ide.eclipse.adt.
The conditions required are:
- create an AutoResizeTextView widget
- create a style for that widget
- specify the text item in the style; not in the widget definition
as in:
res/layout/main.xml:
res/values/myStyles.xml:
With these files, selecting the Graphical Layout tab when editing
main.xml
will display:instead of the graphical view of the layout.
To keep an already too-long story shorter, I tracked this down to the following lines (again in
resizeText
):The problem is that under the specific conditions, mTextSize is never initialized; it has the value 0.
With the above,
targetTextSize
is set to zero (as a result of Math.min).That zero is passed to
getTextHeight()
(andgetTextWidth()
) as thetextSize
argument. When it gets tolayout.draw(sTextResizeCanvas);
we get the exception.
It's more efficient to test if
(mTextSize == 0)
at the beginning ofresizeText()
rather than testing ingetTextHeight()
andgetTextWidth()
; testing earlier saves all the intervening work.With these updates, the file (as in my crash-demo test app) is now:
A big thank you to Chase for posting the initial code. I enjoyed reading through it to see how it worked, and I'm pleased to be able to add to it.
My need was to resize text in order to perfectly fit view bounds. Chase's solution only reduces text size, this one enlarges also the text if there is enough space.
To make all fast & precise i used a bisection method instead of an iterative while, as you can see in
resizeText()
method. That's why you have also aMAX_TEXT_SIZE
option. I also included onoelle's tips.Tested on Android 4.4
If anyone needs it, here is the same code snippet but for Xamarin.Android.