Custom and Downloadable Fonts on Android
See how to make great looking apps using the new custom and downloadable fonts capability available in Android Studio 3.0, all in Kotlin. By Ivan Kušt.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Custom and Downloadable Fonts on Android
25 mins
- History: Difficulties in the Past
- Getting Started
- Requirements
- Add the latest support library
- Bundled fonts
- Creating a font family
- Custom fonts in layouts
- Custom fonts programatically
- Downloadable fonts
- How Font Providers work
- Security & certificates
- Downloadable fonts programatically
- Creating a font request
- Retrieving font information
- Downloadable fonts as XML resources
- Pre-declaring fonts in manifest
- Where To Go From Here?
Retrieving font information
After you answer a question on the quiz, it would be cool to display a simple fact about the font in question. So your next task will be to retrieve information about a font family.
Go to loadFontFact()
in QuestionActivity
.
To get information about available fonts use fetchFonts()
from FontsContractCompat
. Like in the previous task, create a FontRequest
first:
val query = "name=$familyName"
val request = FontRequest(
"com.google.android.gms.fonts",
"com.google.android.gms",
query,
R.array.com_google_android_gms_fonts_certs
)
Then pass it to fetchFonts()
:
val result = FontsContractCompat.fetchFonts(this@QuestionActivity, null, request)
This will return the information about the requested font family if there is one available with the given name. You’ll look it up in the fonts
array of the object returned.
requestFont()
, fetchFonts()
is synchronous. It will execute on the same thread that it’s called and return information about available fonts.
There are several properties for each font:
- uri – a URI associated to the font file by the font provider
- ttcIndex – If providing a TTC_INDEX file, the index to point to. Otherwise, 0.
- weight – font weight as an integer
- italic – boolean with value true if the font is italic style
Check that the status code of the result is ok and show the weight of the font to the user:
if (result.statusCode == FontsContractCompat.FontFamilyResult.STATUS_OK) {
with(textView) {
text = getString(R.string.correct_answer_message, familyName, result.fonts[0].weight)
visibility = View.VISIBLE
}
}
The string R.string.correct_answer_message
is already prepared and takes one integer format param that represents the font weight.
Fetching font data is a blocking operation that should execute in the background. Use doAsync
and uiThread
blocks from Kotlin to execute it on a background thread:
doAsync {
val query = "name=$familyName"
val request = FontRequest(
"com.google.android.gms.fonts",
"com.google.android.gms",
query,
R.array.com_google_android_gms_fonts_certs
)
val result = FontsContractCompat.fetchFonts(this@QuestionActivity, null, request)
if (result.statusCode == FontsContractCompat.FontFamilyResult.STATUS_OK) {
uiThread {
with(textView) {
text = getString(R.string.correct_answer_message, familyName, result.fonts[0].weight)
visibility = View.VISIBLE
}
}
}
}
Finally, add error handling and hide the progress indicator. The final code should in loadFontFact()
look like:
progressView.visibility = View.INVISIBLE
doAsync {
val query = "name=$familyName"
val request = FontRequest(
"com.google.android.gms.fonts",
"com.google.android.gms",
query,
R.array.com_google_android_gms_fonts_certs
)
val result = FontsContractCompat.fetchFonts(this@QuestionActivity, null, request)
if (result.statusCode == FontsContractCompat.FontFamilyResult.STATUS_OK) {
uiThread {
progressView.visibility = View.GONE
with(textView) {
text = getString(R.string.correct_answer_message, familyName, result.fonts[0].weight)
visibility = View.VISIBLE
}
}
} else {
uiThread {
showError(result.statusCode)
}
}
}
Build and run your project. After answering a question you’ll see a fun fact about the font.
Downloadable fonts as XML resources
You can also define downloadable fonts as XML resources.
Right click on the res\font folder. Choose New\Font resource file. For the name type acme in the dialog.
Add font related attributes to the <font-family>
element:
<?xml version="1.0" encoding="utf-8"?>
<font-family xmlns:app="http://schemas.android.com/apk/res-auto"
app:fontProviderAuthority="com.google.android.gms.fonts"
app:fontProviderPackage="com.google.android.gms"
app:fontProviderQuery="Acme"
app:fontProviderCerts="@array/com_google_android_gms_fonts_certs">
</font-family>
All this looks familiar. Yes, that’s right – you are setting the attributes that you earlier passed to requestFont()
. Only this time using XML.
Refer to the created font resource like you did with font family and .ttf files. Open res/activity_main.xml layout and set the acme font to tvFontQuiz
textView:
<android.support.v7.widget.AppCompatTextView
android:id="@+id/tvFontQuiz"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/app_name"
android:textColor="@android:color/black"
android:textSize="@dimen/logo_text_size"
android:textStyle="bold"
app:fontFamily="@font/acme"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toBottomOf="@+id/horizontalGuideline" />
Now repeat the process and add the font named Bilbo Swash Caps.
Open res/activity_main.xml and set the bilbo_swash_caps
font on tvTheGreat
textView:
<android.support.v7.widget.AppCompatTextView
android:id="@+id/tvTheGreat"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/the_great"
android:textColor="@color/colorAccent"
android:textSize="@dimen/logo_text_size_small"
app:fontFamily="@font/bilbo_swash_caps"
app:layout_constraintBottom_toTopOf="@+id/horizontalGuideline"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" />
Build and run your project:
You should see the FontQuiz labels in fonts now. When you first run the app you’ll notice that the font takes a little bit to load before showing because it’s not downloaded and cached yet.
Pre-declaring fonts in manifest
As a bonus – you can specify fonts that Android should preload before your app starts! To do this, you must specify them in the manifest.
Click on the res\values folder, press ⌘N (or File\New) and select Values resource file.
Name the file preloaded_fonts in the dialog.
Add an array of fonts with the resources
tag and name it preloaded_fonts
:
<array name="preloaded_fonts" translatable="false">
<item>@font/acme</item>
<item>@font/bilbo_swash_caps</item>
</array>
Open manifests\AndroidManifest.xml and add the following <meta-data>
element inside <application>
tag:
<meta-data android:name="preloaded_fonts" android:resource="@array/preloaded_fonts" />
Build and run your project:
Voila! Your fonts are now preloaded and ready to use once the app starts.
And, you have a nice Font Quiz app – it’s time to have fun! Can you score 5 / 5? :]
Where To Go From Here?
Here is the final project with all the code you’ve developed in this tutorial.
Now you know how to add custom and downloadable fonts in your application.
A great place to find more information is the official documentation from Google on
font resources, Fonts in XML and Downloadable fonts.
If you want to browse fonts that are available from the Google font provider check here.
If you have any questions or tips for other custom and downloadable font users, please join in the forum discussion below!