Android O / Fonts

Android O: Fonts – Part 2

In March 2017 Google announced the first release of the Android O developer preview. In this occasional series, we’ll look at some of the new features being introduced in Android O. In this article we’ll look at something very close to my heart: Better font support.

In the previous article in this series we looked at how to import and use a custom font using Android O’s shiny new font support. However, in most cases what we actually need to use is a typeface rather than a font. Many people confuse the two, but a typeface is actually a family of related fonts which vary by weight or style.Take look at Nunito and you’ll see that there are 14 different variants (or styles); each of the individual styles is a font, but the collection of these fonts is the typeface named Nunito. In this article we’ll explore how typefaces are supported using the new font support.

There’s good news and bad news here. The good news is that, in addition to having font resources, there has also been the addition of font families which group all the distinct styles of a font together in to a typeface. The bad news is that it currently doesn’t work very well.

Let’s begin by looking at how we create a font family. First we need to import the individual font files as resources, just as we did for Pacifico. For this example I’m just pulling in Nunito-Regular.ttf, Nunito-Bold.ttf, Nunito-Italic.ttf, and Nunito-BoldItalic.ttf and renaming them to nunito_regular.ttf, nunito_bold.ttf, nunito_italic.ttf, and nunito_bold_italic.ttf respectively.

Next we create a new font family resource which is an XML file grouping the individual fonts in to a single family:

<?xml version="1.0" encoding="utf-8"?>
<font-family xmlns:android="http://schemas.android.com/apk/res/android">
  <font
    android:font="@font/nunito_regular"
    android:fontStyle="normal"
    android:fontWeight="400" />

  <font
    android:font="@font/nunito_italic"
    android:fontStyle="italic"
    android:fontWeight="400" />

  <font
    android:font="@font/nunito_bold"
    android:fontStyle="normal"
    android:fontWeight="700" />

  <font
    android:font="@font/nunito_bold_italic"
    android:fontStyle="italic"
    android:fontWeight="700" />

</font-family>

So basically this is simply a mapping of a specific fontStyle and fontWeight to the font resource which will be used to render that specific variant. Valid values for fontStyle are normal or italic; and fontWeight conforms to the CSS font-weight specification (which is a good resource to understand how the named styles in Nunito such as Extra-Light, Light, Regular, Semi-Bold, Bold, Extra-Bold, and Black map to these numeric values.

So now we can use this font-family within our layout:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:app="http://schemas.android.com/apk/res-auto"
  xmlns:tools="http://schemas.android.com/tools"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  tools:context="com.stylingandroid.o.fonts.MainActivity">

  <TextView
    android:id="@+id/pacifico"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginTop="16dp"
    android:fontFamily="@font/pacifico"
    android:text="@string/pangram"
    android:textSize="20sp"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintTop_toTopOf="parent" />

  <TextView
    android:id="@+id/nunito_regular"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginTop="8dp"
    android:fontFamily="@font/nunito"
    android:text="@string/pangram"
    android:textSize="20sp"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintTop_toBottomOf="@+id/pacifico" />

</android.support.constraint.ConstraintLayout>

Please note that we use a fontFamily of @font/nunito rather than the more specific @font/nunito_regular resource.

So now, in theory we should be able to specify different variants using the textStyle attribute of the TextView but sadly, this does not appear to be working. I’ve tried it both using the AppCompat library and using native O widgets and the default variant in the font-family is always rendered:

There are a couple of work arounds for this. The first is to not use font-family at all. We have already imported the individual font files as individual resources, so we can simply use these directly:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:app="http://schemas.android.com/apk/res-auto"
  xmlns:tools="http://schemas.android.com/tools"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  tools:context="com.stylingandroid.o.fonts.MainActivity">

  <TextView
    android:id="@+id/pacifico"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginTop="16dp"
    android:fontFamily="@font/pacifico"
    android:text="@string/pangram"
    android:textSize="20sp"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintTop_toTopOf="parent" />

  <TextView
    android:id="@+id/nunito_regular"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginTop="8dp"
    android:fontFamily="@font/nunito_regular"
    android:text="@string/pangram"
    android:textSize="20sp"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintTop_toBottomOf="@+id/pacifico" />

  <TextView
    android:id="@+id/nunito_bold"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginTop="8dp"
    android:fontFamily="@font/nunito_bold"
    android:text="@string/pangram"
    android:textSize="20sp"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintTop_toBottomOf="@+id/nunito_regular" />

  <TextView
    android:id="@+id/nunito_italic"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginTop="8dp"
    android:fontFamily="@font/nunito_italic"
    android:text="@string/pangram"
    android:textSize="20sp"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintTop_toBottomOf="@+id/nunito_bold" />

  <TextView
    android:id="@+id/nunito_bold_italic"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginTop="8dp"
    android:fontFamily="@font/nunito_bold_italic"
    android:text="@string/pangram"
    android:textSize="20sp"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintTop_toBottomOf="@+id/nunito_italic" />

  <TextView
    android:id="@+id/nunito_programmatic"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginTop="8dp"
    android:text="@string/pangram"
    android:textSize="20sp"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintTop_toBottomOf="@+id/nunito_italic" />

</android.support.constraint.ConstraintLayout>

This renders as we would expect (please note that the final TextView does not yet have a font assigned, it’s merely a placeholder for what is coming next – check out the descender of the ‘y’ it’s different from all the others as this is actually being rendered using the default Roboto font):

The other work around is to actually set the typeface programmatically:

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Typeface nunito = getResources().getFont(R.font.nunito);
        TextView text = (TextView)findViewById(R.id.nunito_programmatic);
        text.setTypeface(nunito, Typeface.BOLD_ITALIC);
    }
}

What we’re doing here is first using the getFont() call to load the Nunito font-family which we defined earlier. Next we get a reference to the placeholder TextView that we created previously; Then comes the magic – we call setTypeface on the TextView and specify the BOLD_ITALIC style. This not only applies the Nutino family, but also uses the appropriate style:

The final TextView is now rendered identically to the one immediately above it.

It is quite interesting how this is actually implemented within TextView. It suggests that it should be possible to control this from the layout which as the state of a View is set by calling methods on the underlying Widget instance during inflation. Perhaps there is a bug in the layout inflation, or possibly, being a developer preview, we only have a partial implementation to play with at this stage. Either way, I would hope and expect that we’ll get full control of this in our layouts in due course.

The one question that this does raise is that there are currently only a small set of constants available for these styles: NORMAL, BOLD, ITALIC, and BOLD_ITALIC. These are clearly not sufficient to map all 14 of the style and weight combinations we saw in Nunito. Perhaps there is an as yet unpublished feature to expand on these styles and possibly that may be the reason for the gap in implementation within layouts.

So there are still a number of unanswered questions regarding font family support but, for now, the basic individual font support is there and working well; and I suspect that full support for typefaces / font families will not be too far behind.

The source code for this article is available here.

© 2017, Mark Allison. All rights reserved.

Copyright © 2017 Styling Android. All Rights Reserved.
Information about how to reuse or republish this work may be available at http://blog.stylingandroid.com/license-information.

3 Comments

  1. We have 2 items in nunito.xml

    both of the above has fontStyle set to “italic”. So In my TextView If I write something like this how android decides which font to apply? Will it be “@font/nunito_italic or “@font/nunito_bold_italic”.

  2. Thank you for validating the issue I’ve been investigating for a while now, not having any method to leverage font weights defined in a font family xml definition.

    I had previously set up a font family with font variations of weights from 100 to 900, but in usage, I was trying to reference the font with a combination of android:fontFamily and android:fontWeight from the TextView xml.

    The Fonts in XML documentation (https://developer.android.com/guide/topics/ui/look-and-feel/fonts-in-xml) conveniently doesn’t mention how you would reference any other weights besides a normal 400 weight font variation, and just as you mentioned, the textStyle attribute seems to be ignored as well.

    I ended up creating smaller font family xml definitions consisting of just the normal and italic versions of each font weight variation as a work around, somewhat similar to your first work around approach. I’m just glad I found someone in the same boat. Thanks!

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.