Inevitably, you’ll get the question “hey, can we change this font?” when doing application development. The answer depends on what fonts come with the platform, whether you can add other fonts, and how to apply them to the widget or whatever needs the font change.
Android is no different. It comes with some fonts plus a means for adding new fonts. Though, as with any new environment, there are a few idiosyncrasies to deal with.
Understanding this chapter requires that you have read the core chapters, particularly the one on files.
Android natively knows three fonts, by the shorthand names of “sans”, “serif”, and “monospace”. For Android 1.x, 2.x, and 3.x, these fonts are actually the Droid series of fonts, created for the Open Handset Alliance by Ascender. A new font set, Roboto, is used in Android 4.x and beyond, though the look of the font changed somewhat in Android 5.0.
For those fonts, you can just reference them in your layout XML, if you
choose, such as the following layout from the
Fonts/FontSampler
sample project:
<?xml version="1.0" encoding="utf-8"?>
<TableLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:stretchColumns="1">
<TableRow>
<TextView
android:text="sans:"
android:layout_marginRight="4dip"
android:textSize="20sp"
/>
<TextView
android:id="@+id/sans"
android:text="Hello, world!"
android:typeface="sans"
android:textSize="20sp"
/>
</TableRow>
<TableRow>
<TextView
android:text="serif:"
android:layout_marginRight="4dip"
android:textSize="20sp"
/>
<TextView
android:id="@+id/serif"
android:text="Hello, world!"
android:typeface="serif"
android:textSize="20sp"
/>
</TableRow>
<TableRow>
<TextView
android:text="monospace:"
android:layout_marginRight="4dip"
android:textSize="20sp"
/>
<TextView
android:id="@+id/monospace"
android:text="Hello, world!"
android:typeface="monospace"
android:textSize="20sp"
/>
</TableRow>
<TableRow>
<TextView
android:text="Custom:"
android:layout_marginRight="4dip"
android:textSize="20sp"
/>
<TextView
android:id="@+id/custom"
android:text="Hello, world!"
android:textSize="20sp"
/>
</TableRow>
<TableRow android:id="@+id/filerow">
<TextView
android:text="Custom from File:"
android:layout_marginRight="4dip"
android:textSize="20sp"
/>
<TextView
android:id="@+id/file"
android:text="Hello, world!"
android:textSize="20sp"
/>
</TableRow>
</TableLayout>
This layout builds a table showing short samples of five fonts. Notice
how the first three have the android:typeface
attribute, whose value
is one of the three built-in font faces (e.g., “sans”).
The three built-in fonts are very nice. However, it may be that a designer, or a manager, or a customer wants a different font than one of those three. Or perhaps you want to use a font for specialized purposes, such as a “dingbats” font instead of a series of PNG graphics.
The easiest way to accomplish this is to package the desired font(s)
with your application. To do this, simply create an assets/
folder in
the project root, and put your TrueType (TTF) fonts in the assets. You
might, for example, create assets/fonts/
and put your TTF files in
there. Note that Android has some support for OpenType (OTF) fonts, as well.
Then, you need to tell your widgets to use that font. Unfortunately, you can no longer use layout XML for this, since the XML does not know about any fonts you may have tucked away as an application asset. Instead, you need to make the change in Java code:
package com.commonsware.android.fonts;
import android.app.Activity;
import android.graphics.Typeface;
import android.os.Bundle;
import android.os.Environment;
import android.view.View;
import android.widget.TextView;
import java.io.File;
public class FontSampler extends Activity {
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
TextView tv=(TextView)findViewById(R.id.custom);
Typeface face=Typeface.createFromAsset(getAssets(),
"fonts/HandmadeTypewriter.ttf");
tv.setTypeface(face);
File font=new File(Environment.getExternalStorageDirectory(),
"MgOpenCosmeticaBold.ttf");
if (font.exists()) {
tv=(TextView)findViewById(R.id.file);
face=Typeface.createFromFile(font);
tv.setTypeface(face);
}
else {
findViewById(R.id.filerow).setVisibility(View.GONE);
}
}
}
Here we grab the TextView
for our “custom” sample, then create a
Typeface
object via the static createFromAsset()
builder method.
This takes the application’s AssetManager
(from getAssets()
) and
a path within your assets/
directory to the font you want.
Then, it is just a matter of telling the TextView
to setTypeface()
,
providing the Typeface
you just created. In this case, we are using
the
Handmade Typewriter font.
You can also load a font out of a local file and use it. The benefit is
that you can customize your fonts after your application has been
distributed. On the other hand, you have to somehow arrange to get the
font onto the device. But just as you can get a Typeface
via
createFromAsset()
, you can get a Typeface
via createFromFile()
.
In our FontSampler
, we look in the root of “external storage”
(typically the SD card) for the MgOpenCosmeticaBold TrueType font file,
and if it is found, we use it for the fifth row of the table.
Otherwise, we hide that row.
The results?
Figure 566: The FontSampler application
Note that Android does not seem to like all TrueType fonts. When
Android dislikes a custom font, rather than raise an Exception
, it
seems to substitute Droid Sans (“sans”) quietly. So, if you try to
use a different font and it does not seem to be working, it may be that
the font in question is incompatible with Android, for whatever reason.
One common complaint with font handling in Android is that you have to apply a custom font on a per-widget basis in Java code.
This gets old quickly.
It is not too bad with just a single TextView
. But for a whole
activity, or a whole application, changing all of the relevant
TextView
widgets (and descendents, like Button
) gets to be a bit
tedious.
While there are “traverse the widget hierarchy and fix up the fonts” code snippets available, you are probably better served using a third-party library, like Christoper Jenkins’ Calligraphy, which lets you define custom fonts in layout XML files or style resources.
TrueType fonts can be rather pudgy, particularly if they support an extensive subset of the available Unicode characters. The Handmade Typewriter font used above runs over 70KB; the DejaVu free fonts can run upwards of 500KB apiece. Even compressed, these add bulk to your application, so be careful not to go overboard with custom fonts, lest your application take up too much room on your users’ phones.
Conversely, bear in mind that fonts may not have all of the glyphs that you need. As an example, let us talk about the ellipsis.
Android’s TextView
class has the built-in ability to “ellipsize”
text, truncating it and adding an ellipsis if the text is longer than
the available space. You can use this via the android:ellipsize
attribute, for example. This works fairly well, at least for
single-line text.
The ellipsis that Android uses is not three periods. Rather it uses an
actual ellipsis character, where the three dots are contained in a
single glyph. Hence, any font that you use in a TextView
where you also use the
“ellipsizing” feature will need the ellipsis glyph.
Beyond that, though, Android pads out the string that gets rendered
on-screen, such that the length (in characters) is the same before and
after “ellipsizing”. To make this work, Android replaces one character
with the ellipsis, and replaces all other removed characters with the
Unicode character ‘ZERO WIDTH NO-BREAK SPACE’ (U+FEFF
). This means
the “extra” characters after the ellipsis do not take up any visible
space on screen, yet they can be part of the string.
However, this means any custom fonts you use for TextView
widgets
that you use with android:ellipsize
must also support this special
Unicode character. Not all fonts do, and you will get artifacts in the
on-screen representation of your shortened strings if your font lacks
this character (e.g., rogue X’s appear at the end of the line).
And, of course, Android’s international deployment means your font must handle any language your users might be looking to enter, perhaps through a language-specific input method editor.
Hence, while using custom fonts in Android is very possible, there are many potential problems, and so you must weigh carefully the benefits of the custom fonts versus their potential costs.