Making the most of TextView auto-sizing on Android
Dynamically adjusting the size of text on Android with a simple API
TextView
auto-sizing was introduced to the framework with Android 8.0 Oreo (API 26). It offers a simple yet powerful API to solve a particular problem: scaling of text size to fit text bounds.
When is this needed? 🤔
For Android Development, using a fixed textSize
, layout_width="match_parent”
and layout_height="wrap_content”
(perhaps inside a scrollable parent) is fairly common practice and suitable for most TextView
use cases.
However, consider the case where the width and/or height of the text bounds are fixed and the text needs to adapt to the available space. Examples include a newspaper-style layout, a font selector that needs to show each different font typeface/name on a single-line, etc. The text can’t scroll and we can’t just add a "read more" button. In these scenarios, TextView
auto-sizing is precisely what we need.
The basics 🔤
The TextView
auto-sizing API is fairly concise. It can be implemented in XML layouts or programmatically. There are three distinct ways of enabling auto-sizing on a TextView
, with increasing levels of specificity: Default, Granular and Preset.
At the time of this writing, the chances of anyone having a minSdk
of 26 are quite slim. Thankfully, all of the auto-sizing functionality is available in the AndroidX core package (formerly Support Library). The differences to the framework API are minimal:
- Use the
app
namespace for XML attributes - Use the functions in
TextViewCompat
instead those onTextView
directly
Note: All of the examples in this post will use the AndroidX implementation.
Before we get going, there are two important points to keep in mind:
- Auto-sizing (as the name would suggest) only adjusts the text size. Other properties (eg.
letterSpacing
,lineHeight
, etc.) are not changed. They do, of course, affect the text layout bounds and thus impact the automatic size chosen for the text. - It is advised to not use a
layout_width
orlayout_height
of"wrap_content"
when usingTextView
auto-sizing, as this may lead to unexpected results. Using a fixed dimension or"match_parent"
is fine (or a“0dp”
match_constraint
if you are usingConstraintLayout
) .
Default auto-sizing 1️⃣
This is the simplest way of enabling TextView
auto-sizing. Given the bounds and attributes of a TextView
, the text size is adjusted in an attempt to perfectly fit the horizontal and vertical axes.
Note: The granularity dimensions for default auto-sizing are minTextSize = 12sp, maxTextSize = 112sp, and granularity = 1px (see Granular auto-sizing below).
In XML:
<TextView
app:autoSizeTextType="uniform" /> <!-- enabled -->
<TextView
app:autoSizeTextType="none" /> <!-- disabled -->
Programmatically:
TextViewCompat.setAutoSizeTextTypeWithDefaults(textView, autoSizeTextType)
where autoSizeTextType
can be:
TextViewCompat.AUTO_SIZE_TEXT_TYPE_UNIFORM
(enabled)TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE
(disabled)
Granular auto-sizing 2️⃣
This allows you to define the values used in uniform auto-sizing: the minimum and maximum text sizes as well a dimension for the size of each "step". A "step" is the increase/decrease in size of the text layout bounds. The text size scales uniformly between the minimum and maximum text size after each "step".
In XML:
<TextView
app:autoSizeTextType="uniform"
app:autoSizeMinTextSize="16sp"
app:autoSizeMaxTextSize="100sp"
app:autoSizeStepGranularity="1sp" />
Programmatically:
TextViewCompat.setAutoSizeTextTypeUniformWithConfiguration(16, 100, 1, unit)
where unit
is the TypedValue
dimension unit of all of the configuration values (eg. TypedValue.COMPLEX_UNIT_SP
).
Preset auto-sizing 3️⃣
This allows you to specify all the possible values used for auto-sizing. The most appropriate text size will be picked from these values to fit the text bounds.
In XML:
Add the preset sizes to res/values/arrays.xml:
<resources>
<array name="auto_size_text_sizes">
<item>16sp</item>
<item>20sp</item>
<item>48sp</item>
<item>100sp</item>
</array>
</resources>
In your layout:
<TextView
app:autoSizeTextType="uniform"
app:autoSizePresetSizes="@array/auto_size_text_sizes" />
Programmatically:
TextViewCompat.setAutoSizeTextTypeUniformWithPresetSizes(textView, arrayOf(16, 20, 48, 100), unit)
where unit
is the TypedValue
dimension unit of the preset size values in the array.
Pro-tips and gotchas 🤓
Mixing value types:
You may have noticed that the programmatic versions of granular and preset auto-sizing could be limiting: the TypedValue
unit in these functions applies to all of the supplied auto-sizing values. If you want to mix types (eg. PX and SP) then you need to do so in XML.
Auto-sizing to a single line:
You may be required to restrict auto-sized text to a single line. You can set the lines
or maxLines
TextView
layout attributes to "1"
(or use the programmatic equivalent). You may also need to adjust the granular autoSizeMinTextSize
, as single-line text will be clipped if the minimum text size is reached but the width still exceeds that of the layout bounds.
Performance:
For performance optimization, one might assume that using preset auto-sizing is the best option. In reality, granular text sizes are precomputed given the minimum, maximum and step values and the difference is negligible.
I hope this post has provided some insight into TextView
auto-sizing and how best to make use of it. If you have any questions, thoughts or suggestions then I’d love to hear from you!
Find me on Twitter @ricknout