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.

Leave a rating/review
Save for later
Share
You are currently viewing page 3 of 3 of this article. Click here to view the first page.

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.

Note: unlike 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!