Custom Fonts on Android — Adding Support for Extra Bold, Extra Light and More

It has been a while since our last post in this custom fonts on Android series. We decided to extend it for two more blog posts due to the popularity and the excellent question from Alex Morozov: how do you add support for non-standard typefaces, like extra bold or extra light?

Before we dive deep into the solution, please make sure you understood the previous posts:

Custom Fonts on Android Series Overview

Adding Support For Non-Standard Typefaces

We're assuming you're up to date with our approach from the previous blog posts and have seen the previous class setup. You'll see that the code for the CustomFontTextView didn't change at all:

public class CustomFontTextView extends TextView {

    public CustomFontTextView(Context context) {
        super(context);

        CustomFontUtils.applyCustomFont(this, context, null);
    }

    public CustomFontTextView(Context context, AttributeSet attrs) {
        super(context, attrs);

        CustomFontUtils.applyCustomFont(this, context, attrs);
    }

    public CustomFontTextView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);

        CustomFontUtils.applyCustomFont(this, context, attrs);
    }
}

The first half of the changed implementation is in the XML declaration. In the previous blog posts, we've used the standard android:textStyle="bold|italic|normal" tag to select the typeface. Unfortunately, this doesn't work anymore when you require custom typefaces.

Thus, open up the attrs.xml and add or adjust the styleable for CustomFontTextView:

<declare-styleable name="CustomFontTextView">  
    <attr name="font" format="string"/>
    <attr name="textStyle" format="integer"/>
</declare-styleable>  

Next, we've created a new /res/values/custom_font_constants.xml file, that contains all the constants which we'll use for our new custom typefaces:

<resources>  
    <integer name="font_style_extra_light">10</integer>
    <integer name="font_style_extra_bold">11</integer>
</resources>  

If you need more custom typefaces, you should add the constants for them right away. You also can change the integers to your preferences, just make sure you've them ready in a minute.

The second half of the implementation is adapting the code for our CustomFontUtils class. First up, the applyCustomFont() font method:

public static void applyCustomFont(TextView customFontTextView, Context context, AttributeSet attrs) {  
    TypedArray attributeArray = context.obtainStyledAttributes(
            attrs,
            R.styleable.CustomFontTextView);

    String fontName = attributeArray.getString(R.styleable.CustomFontTextView_font);

    // check if a special textStyle was used (e.g. extra bold)
    int textStyle = attributeArray.getInt(R.styleable.CustomFontTextView_textStyle, 0);

    // if nothing extra was used, fall back to regular android:textStyle parameter
    if (textStyle == 0) {
        textStyle = attrs.getAttributeIntValue(ANDROID_SCHEMA, "textStyle", Typeface.NORMAL);
    }

    Typeface customFont = selectTypeface(context, fontName, textStyle);
    customFontTextView.setTypeface(customFont);

    attributeArray.recycle();
}

You'll notice we're now checking if the XML element has a custom text style defined: int textStyle = attributeArray.getInt(R.styleable.CustomFontTextView_textStyle, 0); If not, we're defaulting to the value 0. If it's not set, we'll check if the XML tag has a text style defined using the standard android: notation: attrs.getAttributeIntValue(ANDROID_SCHEMA, "textStyle", Typeface.NORMAL);. If this one is not set either, we'll display the regular typeface. We pass the typeface result of our text style checks to the selectTypeface() method.

private static Typeface selectTypeface(Context context, String fontName, int textStyle) {  
    if (fontName.contentEquals(context.getString(R.string.font_name_fontawesome))) {
        return FontCache.getTypeface("fontawesome.ttf", context);
    }
    else if (fontName.contentEquals(context.getString(R.string.font_name_source_sans_pro))) {
        /*
        information about the TextView textStyle:
        http://developer.android.com/reference/android/R.styleable.html#TextView_textStyle
        */
        switch (textStyle) {
            case Typeface.BOLD: // bold
                return FontCache.getTypeface("SourceSansPro-Bold.ttf", context);

            case Typeface.ITALIC: // italic
                return FontCache.getTypeface("SourceSansPro-Italic.ttf", context);

            case Typeface.BOLD_ITALIC: // bold italic
                return FontCache.getTypeface("SourceSansPro-BoldItalic.ttf", context);

            case 10: // extra light, equals @integer/font_style_extra_light
                return FontCache.getTypeface("SourceSansPro-ExtraLight.ttf", context);

            case 11: // extra bold, equals @integer/font_style_extra_bold
                return FontCache.getTypeface("SourceSansPro-Black.ttf", context);

            case Typeface.NORMAL: // regular
            default:
                return FontCache.getTypeface("SourceSansPro-Regular.ttf", context);
        }
    }
    else {
        // no matching font found
        // return null so Android just uses the standard font (Roboto)
        return null;
    }
}

On the first look, this method looks long, but it isn't too bad if you consider that it supports multiple fonts and multiple text styles. The second case, which is SourceSansPro, is especially interesting. We've extended the switch statement with two additional cases, which return our new extra light and extra bold fonts:

case 10: // extra light, equals @integer/font_style_extra_light  
    return FontCache.getTypeface("SourceSansPro-ExtraLight.ttf", context);

case 11: // extra bold, equals @integer/font_style_extra_bold  
    return FontCache.getTypeface("SourceSansPro-Black.ttf", context);

Unfortunately, we've to use magic numbers, since switch cases cannot reference to our defined integer resource variables.

Last, but not least, let's look at an example on how we could use them in an XML layout:

<!-- android:textStyle="bold|italic" is replaced by custom app:textStyle: -->  
<io.futurestud.tutorials.customfont.CustomFontTextView  
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="https://futurestud.io/ (SSP Extra Light)"
    app:font="@string/font_name_source_sans_pro"
    app:textStyle="@integer/font_style_extra_light"/>

<!-- android:textStyle="bold|italic" is replaced by custom app:textStyle: -->  
<io.futurestud.tutorials.customfont.CustomFontTextView  
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="https://futurestud.io/ (SSP Extra Bold)"
    app:font="@string/font_name_source_sans_pro"
    app:textStyle="@integer/font_style_extra_bold"/>

As you can see, we've to use app:textStyle to set the new custom typefaces. Also, just like we had to with the custom fonts, you'll need to add xmlns:app="http://schemas.android.com/apk/res-auto" to the root XML element.

Outlook

We hope this guide helps you to add extra pretty fonts to your Android app. Once again, you'll find the full example project on Github, if you don't want to copy-and-paste the snippets from here. As always, let us know if you've any questions in the comments!

Some of the feedback we've received on this topic was mentioning that it's not necessary to implement all of this on your own, since there are libraries, which do all of this for you. We generally prefer to use tested and well-rounded libraries instead of our own implementations as well, but thought in this case it's valuable to understand the techniques behind the scenes.

Nevertheless, as requested, we'll add one more blog post giving you an insight on our preferred custom font libraries.

Explore the Library

Find interesting tutorials and solutions for your problems.