Best practices for using text in Android

10 Likes comments off



Hello, everyone.

I’m Florina Muntenescu.

I’m an engineer in the
Developer Relations team.

I am Siyamed Sinir.

I am engineer in
UI Toolkit team.


My name’s Seigo Nonaka.

I’m also engineer
in UI Toolkit team.

Last year, we

told you the basics of
best practices with text.

And this year, we want
to build on top of that.

So if you haven’t seen that
talk yet, go check it out.

We’ll wait.


This year, we looked
again at topics

that you have most problems
with, the ones that you feel

are most challenging.

So in this talk, we’re going
to cover a variety of topics.

Some are wider, more
generally applicable,

and some are more
niche advanced topics.

And the solutions,
as you’ll see,

are either new APIs in queue
or some specific guidance.

So let’s get started.


SEIGO NONAKA: OK, let’s get
started from performance.

The performance is a top
topic in the user experience,

and text is one of the most
heavily used components

in UI Toolkit team.

Despite from the usage, the
most of the internal things

are black box and
how to optimize

for many of developers.

We have been keep improving
the TextView performance

and it shows some utility
tips in less Google I/O.

This year, first let me announce
that the hyphenation is now

turned off by default in Android
Q and AppCompat version 1.1.

As we talked last
year, the hyphenation

has a huge impact to the
TextView performance.

Actually, the 70% of the
text work is for hyphenation.

So we turned off
with by default.

If you need hyphenation,
please manually turn

it own in your application.


To enable the
hyphenations, please

set hyphenation frequency
attribute to normal

in style.xml or layout.xml.

Also, you can call fit
hyphenation frequency method

in your code.

OK, next.

We released the precomputed
text in last Google I/O.

This is a tool for doing text
work in background thread.

This does 90% of
text work beforehand,

selecting fonts
or measuring text.


This is a [INAUDIBLE]
object and it should be

created in background thread.

And we back ported
this craft to jet pack

and available from API 21.


Then this year, we
added new utility APIs

for easy integration with
RecyclerView prefetch.


Before looking into the
details, let’s see the overview

of the RecyclerView prefetch.

This figure shows the work
growth of UI thread and render


UI thread does some work
and pass the rendering work

to render thread.

The eye icon shows
the timing of VSYNC.

You see here shows a good state,
nothing broke the VSYNC event.

But if some work takes
time and couldn’t

finish until the
next VSYNC event,

it end up with [INAUDIBLE].

In this example, the
lid works takes time

and blocks the next VSYNC event.


This typically happens
when the new item

is appeared by scrolling.

The idea of prefetch is
doing this view item process

in much earlier time when
the UI thread is idle.


If everything goes well, the
workload of the UI thread

have distributed, and we
can prevent UI [INAUDIBLE]..


This is just the overview.

So if you need the more details,
please check the articles

on the Android developer
medium publication.


This is how prefetch
works, but this prefetch

had three limitations.

The prefetch still
works on the UI thread.

If prefetch takes longer
time, that prefetch

still blocks the VSYNC event.

This typically
happens when you set

long, long text to TextView.


One obvious reason
for this problem

is moving some work
to background thread.

Actually, you can move some
text work to background thread

by using PrecomputedText.

However, in general, moving
some work to background set

is not a trivial work.

So we introduce new utility APIs
for make this happen easily.

The API is getTextFuture
in PrecomputedTextCompat

and setTextFuture in

Let’s see how it works.


When you write the application
with the RecyclerView,

you may set text in
onBindViewHolder callback

like this.

Then, if we move that text
work to background set,

the code looks like this.

Let’s see it line by line.

First, let’s call getTextFuture.

The getTextFuture
schedule text work

on the past
execution and returns

the future of the result.
Once you get the future,

you can set it to
the AppCompatTextView

by calling setTextFuture.

Basically, that’s it.

By coding these two APIs,
you can move the text work

to background set.


This is pretty simple, but
please note that you cannot

change the style after
coding getTextFuture,

because getTextFuture
schedules the text work with

the parameters when it’s called.

So if you change the
style after that,

the async work
result won’t match up

with the parameter
of the TextView.

Then it end up
with the exception.


So if you want to
change the style,

please do it before
calling getTextFuture.


If you need more details
or what is under the hood,

please check the article on
the Android developer medium


Thank you.


Apart from performance,

another thing that
everyone needs to work with

is text styling.

But with so many ways of styling
text, which one should you use?

And what’s the precedence
order for each of them?

So one of the easiest
ways to style text

is by just using the
TextView attributes.

But the problem with this
is that this is tedious

and error prone.

If you, for example,
need to update

the color of the
TextViews, you need

to see absolutely every
TextView in your application,

and then do that update

The solution for this
is working with styles.

They bring you consistency,
reusability, and easy updates.


And this actually brings us
to the first precedence order.

So you see here, we have both
the text size in the style

and also in the text attribute.

So which text size will
the text actually have?

Well, the view attributes
overrides the one

from the style.

So what you’ll see here
is a text that’s 16 sp.


Views have a default style.

So for example, for
the button that’s

a subclass of the
TextView, you’ll

see that we’re also using
a theme attribute, that’s

the button style.

Well, in the
Material theme, this

pointed to a certain style
which styles different elements,

including attributes of
the text of the button.

By setting your
default styles, it

means that styling becomes an
opt out rather than opt in.

By default, all buttons,
independent If you’re

adding them to new screens
or you’re refactoring,

will have a default style.

So you’ll only
need to modify them

if you specifically
want to change

the style for the button.

So by default, but
this default style

is going to be overridden by
any style that sits directly

on the view.

The button properties, as some
eagle eye might have seen here,

are set using the
text appearance.

So text appearance actually
functions very similarly

to a style.

Under the hood, the TextView
checks whether the view

  Hilt - Android Dependency Injection

has a text appearance.

It loads it.

It applies it to the view.

And then, it adds all
the other attributes,

like the text style, also.

So this means that, as
the precedence order,

the text appearance
is then overridden

by the default style,
and then the style,

and then the view attributes.

But there’s something you should
know about text appearance.

It’s character building.

It only supports
character-level styling.

It doesn’t support
paragraph-level styling,

like line height
or break strategy.

These can be customized here.

In Android Q, we added a new
attribute in text appearance–

the font variation settings.

And the same attribute,
starting from Q,

can be used in TextView
and in AppCompatTextView.

Some of the text
appearance attributes

are also supported in
text appearance span.

Before Q, the text
appearance span

didn’t read some of
the text attributes,

like typeface or shadow.

So starting from Q, the
text appearance span

is updated to read and apply
these attributes, as well.

Also new in Q,
speaking of spans,

we’ve added implementations
to two new spans–

line height span and
line background span.

When applying the span
on top of text styling,

keep in mind that the
properties of the span

set the properties that
are set by the span

will override any other
properties that you

might be setting–

VI style, or default
style, or view.

But what if you want to change
some attributes of the TextView

at the app level?

So let’s say that you want
to change the entire font

in your application.

Well, for this, you
would use themes.

So for example here, we’re
setting the font family

attribute to set the theme.

The theme is part of
the view styling system,

and it overrides anything
that’s supplied in the text

appearance, but it will override
any more specific attributes.

So for the text styling, we
looked at the precedence order

to help you better understand
why or why not style is




Hi again.


Thank you.

So we have covered tips
related to text styling

and also performance.

And we will continue
with fonts, which

is an important
part of text styling

and also your application’s
brand identity.

Over the years, you
have mentioned to us

about the shortcomings of
font support on Android.

And in the last
few years, we have

added features such as
downloadable fonts and fonts

in XML.

This year, we have improved the
typeface and font-related APIs

based on the further
feedback we got.


Let’s go over a
real-life use case,

and let’s assume
that we have an app.

And in one of our
buttons, we want

to use an icon in
between the text.

And we want this icon to be
rendered using the icon font.

And for the remaining
of the text,

we want to use another
custom font, let’s say, Lato.

When we want to do this,
we are kind of in trouble,

because the button will
accept only a single typeface,

and a typeface can be created
using a single font or font


Actually, in the current API,
we can use a typeface span,

give the icon font into it,
and cover the icon character.

And then for the
whole button, we

can choose to use the Lato font.

However, things will get
much more complicated

if we were to try to
support multiple languages

with their own custom fonts.

Using spans here is definitely
not a scalable solution,

because you will have a lot
of languages to support,

and it’s pretty hard
to accurately identify

different languages
for a given string.

And on top of that,
over the years

I have heard continuously,
parsing emoji, for example,

is pretty hard.

And if you have another
custom emoji font,

that would be a pain.

Therefore, in Android Q, we
added custom fallback builder

to the typeface class.

It enables you to create a
typeface using multiple fonts

or font families.

Then we check our
multi-language use case.

Here we create a builder, and
for each of the languages,

we create a font family and add
the related fonts into them.

And this will also show the
icon font use case, as well,

where this time
again, we put the icon

font as a separate font family
into the final typeface.


Then you have multiple
styles for a font family,

you can add them into the
same font family object.

And whenever you set the text,
we have to use bold style

or mark a portion of the
text to be bold using spans.

The system will choose
the correct bold font

from the given configuration.

When creating a typeface
using this builder,

you can add up to
64 font families.

However, what you cannot do is
you cannot put the fonts that

belong conceptually to different
font families into the same

font family object.

The reason is the system
expects the fonts that

are in the same font family
to have the same code

coverage, which
means that they have

to have one-to-one mapping
between the characters

that they support.

And the other thing that
you cannot do is to put two

of the same styled fonts
into the same font family.

For example, here, if I
were to put Lato bold twice

into the same font family,
I would get an exception.

And then you define a
custom fallback builder.

Here is how the system works.

It does a sequential search
over the string, and also

over the fonts
that are provided.

For example, in
here, it will start

with the Japanese
character, and it

will check if Lato font
supports this character.

If not, it will go to the
second one, see Kosugi,

and at that point, it will try
to cover as many characters as

possible using the same font.

And then it will do the
same thing over again.

And if the string
contains language

that is not supported
with the given font,

the system will use
the system fallback use

case, which is the current
case for Android and typeface.

As a control over
system fallback,

the new API has a function
named setSystemFallback,

where you define what
kind of generic family

should be preferred while
choosing a font from the system


Since Lato is a sans
serif font, here I

tell the system that
if a character is not

supported by Lato,
the font that I edit,

I want system to prefer
a sans-serif font.


Just like the current
typeface builders,

while creating a font, you can
set the weight, slant, and also

the variable font
settings on a font object.

And finally, in terms
of font matching,

let’s assume that you added
two different weighted forms

into the same font
family, 400 and 700.

And by using text font
weight attribute on TextView,

you requested to render
this text using a 600 font.

In such mismatches,
the system will

use the closest match or style.

For this case, it will
choose to use 700.

Text font weight was
recently added, I think in P,

to TextView.

And it’s also currently being
read by text appearance span.


Thank you, Siyamed.



  Storage access with Android 11

As he explained, we expanded
the font experience in Android Q

for Java applications.

On the other hand, there’s
another strong demand

from NDK application
developers about system fonts.

So we decided to add new
APIs for existing system

fonts from native code.

This would be a great tool for
NDK application filtered text

on the screen, like a games,
document viewer, or browsers.


First, what is a system font?

Android supports
over 100 languages,

and each languages may
require different font files.

For example, to draw Hindi,
you may need Devanagari font.

In case of pixels
three, to support

this large number
of languages, it

has over 270 font
files are installed.

To draw text, NDK application,
like document viewer,

needs to know which system
font can render the given text.

This is not a trivial question.

So we added two
new APIs for this.


The API is font matcher API.


This is a interface
of the system

internal font selection engine.

This font selection engine
is also used by the TextView,

so you can get the same
result as in the TextView.


OK, let’s see how this works.

Let’s think with this example.

The core API is
AFontMatcher match function.

This takes a search string
and returns two outputs–

font and length.


Please note that this API
is simplified for explaining

its behaviors.

The real API is written in
C and takes more parameters.

Anyway, this function
would return,

in this example, font
NotoSansCJK-Regular and length


This length 3 means
first three characters

can be rendered
with a return font.

In this case, NotoSansCJK.

OK, now we know the font for
the first three characters.

What about remaining?


Since we know the
first three characters,

let’s advance the search
range, in this case,

moving starting offset by 3.

Then, for the same
function again.


On the second time,
this API returns

font Roboto-Regular
and length 8.

Again, this 8 means the
next eight characters–

in this example, it
includes a white space–

can render the
Roboto-Regular front.

Then, advance search range,
then call function again.


On the third time, now, really,
it is the end of the string.

So we get all fonts for
the given characters.


As you’ve seen it, by keep
coding these functions,

you can get font files
for all characters.


This is how this API works.

But please note that this API
never returns null pointers.


If no font can render
the given font,

this API still returns
a byte font object.

This font of that should be used
for drawing missing with symbol

called Tofu.


Similarly, even if it has
no font exactly matches

with the given
parameters, this function

still returns a font which
should have a closest style.



This is font matching API.

This API gives an
answer to the question,

what font should be
used for the text?


On the other hand, there’s
another frequently asked


What fonts are
installed in the device?


To answer these
questions, we also

add a new API called
font enumeration API.


This font enumeration
API gives user

access to the
system-installed font files.

Let’s see how to use this.

The Java API is really simple.

You can just call system
fonts, get the available fonts.

This gives you the set of
system-installed font files,

and you can get the clear
path of the system fonts.


In case of NDK, it starts
from creating iterator object.

By keep calling next
function to iterator,

you can access all
system-installed font files.

But please note that
the returning order

doesn’t mean anything here.

For example, even
if font A comes

before font B, that doesn’t
mean font A has higher

priority than font
B. This next function

returns in random order.


Finally, there’s
another note here.

This font enumeration
API is not so fast,

so we recommend you
to cache the result.

Since the system
font configuration

is read only and modified
only by the system update,

so you can keep the
result for a longer time

until the next system update.

Thank you.



the most used subclasses

of TextView is EditText.

And pretty much everything
that applies to the TextView

also applies to EditText.

But on top of this,
there are also

other things that
you need to handle,

things like layout, styling,
keyboard configuration, input

internationalization, and more.

Unfortunately, we
don’t have time

to go over all of these today.

But we will cover some of them.

So first of all, when
working with EditText,

you actually interact with
three different processes.

The first one is
your application.

The next one is
the soft keyboards,

named Input Method Service.

And then the third one
is the system process

that orchestrates those two.

And because this is an
inter-process communication,

you might see in some
cases a slight delay

between key events
entered in the UI

and them showing up in the app.

So visually, EditText has
the following components–

the hints, the text, the
background, the selection,

the cursor, and the cursor
and selection handles.

The background, cursor, and
handles, the colors for these

are updated when you
set the color secondary

in your application theme.

They are actually
just drawables that

use colorControlNormal
and colorControlActivated

in their definitions.

So actually, you can also modify
those theme overlay attributes

to change their color.

Each of these
drawables themselves

can also be updated
using related attributes.

But probably the one
you want to set the most

is editTextBackground.

Finally, textColorHint,

and textHighlightColor
are also used

to update the color
of the other three.

Apart from these attributes,
just like TextView, EditText

has its own default style, which
is the editTextStyle attribute.

And this can be ported
to your own custom style.

So use this attribute to
change any EditText view

attributes, such as text
size, or text style.




all know, EditText

has support for
compound drawables.

And sometimes we see
those compound drawables

are being used as action
buttons on the EditText,

such as a clear action.

However, using
drawables as buttons

comes with a lot of
constraints, and it definitely

negatively impacts the
final user experience.

Things like, when you
add an action button,

you will probably want to
show the ripples on touch,

maybe animate the icons,
have more than one icon

on each side.

And then you will also have
the accessibility requirements.

All of these
features are already

supported by the existing
[INAUDIBLE] layout system.

And therefore, we will
suggest to use them instead.

Apart from being
convenient at first,

I believe that there
might be two reasons why

people would like to
choose to go that way

and use the drawables.

One of them is having the
same EditText look and feel

over the whole group,
and the other one

is being able to
contract and expand

the editable area based on
the visibility of the button.

You should definitely
use text input layout,

and that will cover
a lot of use cases.

However, if you have

  How to move from Android to iPhone

requirements that are not
supported by text input layout,

you should create your
own view group and layout.

And in order to address the
issue of having the same look

and feel, you can set
the EditText background

to null, and use the exact
same background that is already

in the system for
the view group.

The only thing we have
to do is, in order

to respond to the state
changes for the drawable, such

as pressed and focused,
you have to add

the addStatesFromChildren
attribute to the parent layout.


Another common issue in terms
of EditText that we are aware of

is how the keyboard plays with
a possible input validation


Depending on the location
of the input field,

the error can remain
behind the keyboard,

and you really want
the users to be

able to see what went
wrong, let’s say,

when they were going
through a sign up process.

This is currently the case
also for the text input layout,

and there is an open
ticket about it.

But until we get to address this
issue in the material library,

here is what we can do in order
to make those errors visible.


There is already
a system in place

that keeps the
editable area visible

after the keyboard shows up.

And all we want to do is to
tell the system, instead of

the edit text area, which
is the green box in here,

use the yellow box.

And in order to do that, we
have to extend from EditText

and override three functions.

I have added the sample code
to the URL given in the slide.

But I will go through
the functions briefly.

Get focused rectangle is used
to find the next focusable

widget, updates a rectangle.

Get globally visible rectangle
defines the visible array

of a view.

And finally, request
rectangle on screen

is used by EditText to
request from the system

to make a certain area visible,
in terms of the edit text where

the cursor is.


Get focused rectangle
implementation is pretty

straightforward and simple.

You let the edit text to
fill in the rectangle,

and you just update the
bottom of the final rectangle

to be the parent’s button.

And you do the same thing for
the global visible rectangle.

Finally, for request
rectangle on screen,

again we let the edit text
to request its own rectangle,

and then we request
another rectangle,

which is at the bottom
of our parent layout.

The number 10 here is
just a magic number

to define a rectangle at
the bottom of the parent.


Going back to the EditText
configuration and how

it works with keyboard–

as you all know, when you
set the input type attribute

to text password, the
characters on the screen

will be invisible.

However, this
information is also

being shared to the keyboard in
order to change its behavior.

Now, the keyboard itself
is another application

that can be installed
from Play Store.

Therefore, they can
define their own behavior

based on the signals they get.

Since we cannot rely
on their behavior,

you still have to validate
the input on our side.

However, using the correct
input types and IME options

is important to reduce the
friction for the end user

as much as possible.

And when you set the input
type to number password,

you have a visible
change on the keyboard,

where they show the number pad.


If you have a custom
component, let’s say,

and you toggle the
visibility of the password,

you still want to signal
the keyboard that this

is a password field,
and they should not

learn about what the user
types, because you don’t want

the keyboard itself, which
is not under your control,

to show back the user, here is
your password, I know about it.

Because it gets a little
creepy for the users.


And the cases where you don’t
want the keyboard to learn

is not limited to
the password fields.

Things like an incognito
mode, such as in Chrome,

or a promo code in
your checkout screen

are really things that you
don’t want the keyboard

to learn about.

For those cases, you can add
the no personalized learning

flag to the IME options.

One more IME option that
is important in my opinion

is flag force ASCII.

A lot of times, we have seen
that developers are expecting

when they set the password, when
they set the input type to text

password, they
expect the keyboard

to show an ASCII keyboard.

However, text password is
definitely not a strong signal

for them to switch
to ASCII keyboard.

So if you have an ASCII-only
field, such as username

and password, maybe,
you might want

to add this flag to IME options.


One last case that can
happen for applications

is the control over the
application language,

text field language, and
the keyboard language.

If you have an
application such as

a language-learning application
or a translation application,

there might be a case
where the main language

of the application is
different than the language

of the edit text,
which can be different

than the language
of the keyboard,

because the user can set
any language that they want.

If you want to
signal to keyboard

to use a specific language
and switch to that language,

you can use set IME hint locales
function on the EditText.


Since we mentioned that
keyboard behavior depends

on the keyboard
implementation, and they

can define their
own behavior, there

might be times
where you might want

to learn about which keyboard
is active at any given time.

You can read the active
keyboard from the settings,

and this will give you the
class name and the package

name of the keyboard.


so many fantastic things

with text in this talk.

But let’s see what are
some of the key things we

should remember.

First, in terms of
performance, keep in mind

that the hyphenation is
now turned off by default

in Android Q and AppCompat 1.1.

So if you need it, enable it.

And also, you should
use PrecomputedText

with RecyclerView prefetch.

For text styling, keep in
mind the precedence order.

If you’re trying
to style something

and you don’t see
the results, it’s

likely been overridden
by something

with a higher precedence.

If you need to support
multiple fonts in your app,

use the custom
fallback builder API.

To find the font used by
the system, use the font

matcher API.

And then, to find all
the installed fonts,

use the font enumeration API.

In terms of EditText,
use the theme attributes

and the Android’s edit text
style to style your edit texts.

Make sure you don’t use the
EditText drawables as actions,

so you can enable extensibility
and maintainability.

And also, use IME flags
to configure soft keyboard

for a better user experience.

So these were some top tips
to make your text terrific.

Thank you.




You might like

About the Author: admin

You cannot copy content of this page