Custom Fonts in Android — Google Fonts, Font Resources and App-Wide Theming with Kotlin

Fonts are one of the fastest ways to make an app feel like it has a real identity. That distinctive typeface in your favourite app? It took the developer about 10 minutes to set up — and it's even faster now than it used to be.

In 2014 the only way to use a custom font in Android was to copy a .ttf file into assets/fonts/ and load it in Java with Typeface.createFromAsset(). In 2026 you have three much better options — XML Font Resources, Downloadable Google Fonts with zero asset files, and app-wide font theming that applies your font everywhere in one line. This guide covers all three, plus the legacy approach for when you need to support older APIs.

Custom fonts in Android project structure

Three ways to add custom fonts in Android — pick the one that fits your project

Three Approaches — Which One Should You Use?

Approach Setup Best For
Google Fonts (Downloadable) Zero asset files — font downloads at runtime Google Fonts library — most popular choice
Font Resource (XML) Font file bundled in res/font/ Custom/branded fonts not on Google Fonts
Typeface (Legacy) Font file in assets/fonts/, loaded in code API below 26, or legacy codebases

Method 1 — Google Fonts (Recommended, Zero Asset Files)

This is the cleanest approach in 2026. Android Studio has Google Fonts built in — you pick the font, Android handles the download. No .ttf files in your project, no assets directory.

Using Android Studio's built-in font picker

  1. Open any layout XML file
  2. Click on a TextView
  3. In the Attributes panel find fontFamily
  4. Click the dropdown → More Fonts
  5. Search for your font (e.g. "Roboto", "Poppins", "Inter")
  6. Select Add font to project → choose Downloadable
  7. Click OK — Android Studio generates all the required XML automatically

Android Studio generates three things automatically:

// 1. res/font/poppins.xml — font family definition
<?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="Poppins"
    app:fontProviderCerts="@array/com_google_android_gms_fonts_certs">
</font-family>
// 2. res/values/font_certs.xml — security certificates (auto-generated)
// 3. AndroidManifest.xml — preloaded fonts declaration (auto-generated)

Apply the font in XML

<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Hello with Poppins"
    android:fontFamily="@font/poppins"
    android:textSize="18sp" />
✅ Advantage: Downloadable fonts are cached by the OS across apps. If another app already downloaded Poppins, your app uses the cached version instantly — no extra download, no APK size increase.

Method 2 — Font Resource in res/font/ (For Custom/Branded Fonts)

When your brand uses a custom typeface that isn't on Google Fonts — a purchased font, a company-specific typeface — bundle it directly in your app using the Font Resource system introduced in API 26.

Step 1: Add font files to res/font/

// Create the directory: res/font/
// Add your font files with lowercase names — no spaces, no hyphens
// res/font/montserrat_regular.ttf
// res/font/montserrat_bold.ttf
// res/font/montserrat_italic.ttf

Step 2: Create a font family XML (optional but recommended)

A font family XML lets you define all weights and styles in one place. Android automatically picks the right weight for bold/italic text:

<!-- res/font/montserrat.xml -->
<?xml version="1.0" encoding="utf-8"?>
<font-family xmlns:android="http://schemas.android.com/apk/res/android">

    <font
        android:fontStyle="normal"
        android:fontWeight="400"
        android:font="@font/montserrat_regular" />

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

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

</font-family>

Step 3: Apply in XML

<!-- Apply the font family -->
<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Branded heading"
    android:fontFamily="@font/montserrat"
    android:textStyle="bold"
    android:textSize="24sp" />

<!-- Apply a specific weight directly -->
<TextView
    android:fontFamily="@font/montserrat_regular"
    android:text="Regular body text" />

Apply in code with ViewBinding (Kotlin)

import androidx.core.content.res.ResourcesCompat

// Load font resource in code
val typeface = ResourcesCompat.getFont(context, R.font.montserrat)
binding.tvTitle.typeface = typeface

// Or with a specific style
val boldTypeface = ResourcesCompat.getFont(context, R.font.montserrat_bold)
binding.tvHeading.typeface = boldTypeface

Apply Font App-Wide via Theme (Most Efficient)

This is the best thing most tutorials skip entirely. Instead of setting fontFamily on every single TextView, set it once in your app theme and it applies everywhere automatically:

<!-- res/values/themes.xml -->
<resources>
    <style name="Theme.MyApp" parent="Theme.Material3.DayNight">

        <!-- Apply custom font to ALL text in the app -->
        <item name="fontFamily">@font/poppins</item>

        <!-- Or use Material3 type attributes for finer control -->
        <item name="textAppearanceHeadlineLarge">@style/TextAppearance.MyApp.HeadlineLarge</item>
        <item name="textAppearanceBodyMedium">@style/TextAppearance.MyApp.BodyMedium</item>

    </style>

    <!-- Custom text appearances with your font -->
    <style name="TextAppearance.MyApp.HeadlineLarge"
        parent="TextAppearance.Material3.HeadlineLarge">
        <item name="fontFamily">@font/montserrat_bold</item>
    </style>

    <style name="TextAppearance.MyApp.BodyMedium"
        parent="TextAppearance.Material3.BodyMedium">
        <item name="fontFamily">@font/montserrat_regular</item>
    </style>
</resources>
<!-- AndroidManifest.xml — apply theme to whole app -->
<application
    android:theme="@style/Theme.MyApp"
    ... />
💡 This is the right way for branded apps. Set the font once in the theme — every TextView, Button, Chip, and text-based component in your entire app picks it up automatically. No per-view font setting needed.

Legacy Method — Typeface from Assets (API below 26)

If you need to support devices below API 26 and can't use the Font Resource system, the original assets/fonts/ approach still works. Use Kotlin and ViewBinding instead of the old Java pattern:

Step 1: Add font to assets

// Create: src/main/assets/fonts/
// Add your font: src/main/assets/fonts/custom_font.ttf

Step 2: Load with Typeface in Kotlin

import android.graphics.Typeface

class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        // Load font from assets
        val typeface = Typeface.createFromAsset(assets, "fonts/custom_font.ttf")

        // Apply to views
        binding.tvTitle.typeface = typeface
        binding.tvSubtitle.typeface = typeface

        // Apply bold style programmatically
        binding.tvHeading.setTypeface(typeface, Typeface.BOLD)
    }
}

Reusable extension function for the assets approach

// ViewExtensions.kt
fun TextView.setCustomFont(context: Context, fontPath: String) {
    typeface = Typeface.createFromAsset(context.assets, fontPath)
}

// Usage
binding.tvTitle.setCustomFont(this, "fonts/custom_font.ttf")
binding.tvBody.setCustomFont(this, "fonts/custom_font.ttf")
⚠️ Performance note: Typeface.createFromAsset() reads from disk every time it's called. For lists or repeated use, cache the Typeface in a companion object or ViewModel to avoid repeated I/O:
companion object {
    private var cachedTypeface: Typeface? = null

    fun getTypeface(context: Context): Typeface {
        if (cachedTypeface == null) {
            cachedTypeface = Typeface.createFromAsset(
                context.assets, "fonts/custom_font.ttf"
            )
        }
        return cachedTypeface!!
    }
}

Best Practices

  • Use Google Fonts for standard typefaces — if your font is on Google Fonts, always use the downloadable approach. No APK size increase, OS-level caching, zero maintenance.
  • Set fonts in the theme, not per-view — for branded apps, define your typography in themes.xml using Material3 text appearance attributes. One change updates every text element in the app simultaneously.
  • Use lowercase filenames with underscores for font filesres/font/ files follow the same naming rules as drawables. No uppercase, no hyphens. poppins_bold.ttf ✅, Poppins-Bold.ttf
  • Define font families for multiple weights — create a font family XML in res/font/ that includes all weights. Android automatically selects the right weight when you use textStyle="bold".
  • Cache Typeface when using the legacy approachcreateFromAsset() reads from disk. In RecyclerView or any repeated context, cache the Typeface instance and reuse it.

Frequently Asked Questions

How do I add a custom font in Android with Kotlin?
Place your .ttf or .otf file in res/font/ and reference it with android:fontFamily="@font/your_font" in XML. For Google Fonts, use Android Studio's font picker which generates all required files automatically. For app-wide fonts, add fontFamily to your theme in themes.xml.

What is the difference between Google Fonts and Font Resources?
Google Fonts (downloadable) fetch at runtime from Google's servers with OS-level caching — no font files in your APK. Font Resources bundle the font inside res/font/ in your APK — works offline but increases APK size. Use Google Fonts for standard typefaces, Font Resources for custom branded fonts.

How do I apply a custom font to all text in the app?
Add <item name="fontFamily">@font/your_font</item> to your app theme in themes.xml and apply the theme in AndroidManifest.xml. This applies your font to every text-based component in the entire app automatically.

Can I still use Typeface.createFromAsset()?
Yes it still works — but it's the legacy approach. Use Font Resources or Google Fonts for new projects. If you do use createFromAsset(), cache the Typeface instance to avoid repeated disk reads, especially inside RecyclerView adapters.

📚 Continue Learning
📝 Summary
  • Google Fonts — zero asset files, OS-cached, use for any font on Google Fonts
  • Font Resource (res/font/) — bundle custom/branded fonts, set in XML with @font/name
  • App-wide font via theme — add fontFamily to themes.xml, applies everywhere automatically
  • Font family XML — define all weights together, Android picks the right one automatically
  • Legacy Typeface — still works, cache the instance, avoid for new projects
  • Font filenames must be lowercase with underscores — no uppercase, no hyphens
  • Use ResourcesCompat.getFont() to load font resources in Kotlin code

Pragnesh Ghoda

A forward-thinking developer offering more than 8 years of experience building, integrating, and supporting android applications for mobile and tablet devices on the Android platform. Talks about #kotlin and #android

Post a Comment

Please let us know about any concerns or query.

Previous Post Next Post

Contact Form