Migrating custom fonts from Calligraphy to Android fonts
When we started building Peaks app three years ago the Android framework didn’t provide any straightforward way to work with custom fonts in our XML layout and style files.
The typical solution was to create a custom implementation of TextView adding a new XML attribute for specifying the font and then loading it from the app assets setting it as a Typeface
. And repeat this process for any other view where you needed to use custom fonts…
Thankfully, Christopher Jenkins came up with a more elegant approach that didn’t require creating any custom view. Instead, his library Calligraphy used layout inflation injection to add the custom fonts to the views.
The fairly simple setup (almost three lines of code) convinced us to use the library instead of the custom views approach. And it worked really well during all this time.
Meanwhile, in mid-2017 Android 8 was released and in its release notes there was an exciting new feature: Fonts in XML
[…] Which lets you use fonts as resources. This means, there is no need to bundle fonts as assets. Fonts are compiled in
R
file and are automatically available in the system as a resource. You can then access these fonts with the help of a new resource type, font.
What’s more, the new feature was backward compatible with API versions 14 and higher thanks to the support library.
However, at that time we were pretty full of work and Calligraphy was working fine, so… the migration was lost in the backlog.
Until… some months ago, when we were trying to migrate our app to Android 10 and we were getting a nasty exception:
After inspecting the logcat, we found out that Calligraphy was causing the crash. Luckily, by that time the developers of the library had already fixed the issue, but we were still using Calligraphy 2 and the fix was only available in Calligraphy 3.
As we needed to do a small migration anyway, we thought that it was a good moment to try to drop Calligraphy in favor of the native solution. After all, it always feels good to remove third-party dependencies.
The migration
The migration was easier than expected, it took us roughly 1 hour to finish it. The process was the following:
1. Move font files from assets to font folder
Move your font files (ttf
or otf
) from your assets
folder to the font resources folder res/font
.
📂/app/src/main/assets
┣ my_custom_font_regular.ttf
┣ my_custom_font_italic.ttf
┗ my_custom_font_bold.ttfTo:📂/app/src/main/res/font
┣ my_custom_font_regular.ttf
┣ my_custom_font_italic.ttf
┗ my_custom_font_bold.ttf
2. Create a custom font family
In your res/font
directory, create a new file my_custom_font_family.xml
. A font family is a set of font files along with its style and weight details.
Note that we use the app
namespace as we are using the support library to ensure backwards compatibility. If you don’t need to support API level < 26 you can use the android
namespace instead.
3. Remove Calligraphy dependency
Remove the Calligraphy dependency from your build.gradle
and sync the project.
The compiler will point to all the places in your code where the library was used. For example:
error: cannot find symbol class CalligraphyTypefaceSpan
private CalligraphyTypefaceSpan typefaceSpanBold;
^
symbol: class CalligraphyTypefaceSpan
4. Remove Calligraphy code
Remove all the Calligraphy-related code.
a) Remove initialization code in the#onCreate()
method of your Application
class
b) Remove context wrapper in the #attachBaseContext()
method of your BaseActivity
class
c) Replace TypefaceUtils and CalligraphyTypefaceSpan class
TypefaceUtils.load()
can be replaced byResourcesCompat.getFont()
:
Note: If you don’t need to support API level < 26, you can use resources.getFont()
instead.
Tip: “Replace in Path” from Android Studio (Edit/Find/Replace in Path) may be a handy tool during the migration.
CalligraphyTypefaceSpan
can be replaced byTypefaceSpan
:
5. Update font declarations in layouts and styles
Finally, we have to update font declarations in our XML files.
a) Layouts
Note: you can specify the fontFamily
attribute using the namespace:
android
: supported from API 16.app
: if you need to backport this feature all the way to API 14.
b) Styles
And… migration completed! If you run the app nothing should have changed, but your mind will be more peaceful knowing that you have a third-party dependency less 🙂