Search code examples
androidcompose-multiplatform

Is There a Way to Use a Font from Compose Resources with a View?


I am writing a suite of apps. Some will be multiplatform, using Compose Multiplatform for the UI. One will be only deployed on Android.

I defined the design system using Compose Multiplatform and Compose Resources. This includes a custom font, in commonMain/composeResources/font/ of my design system module:

Android Studio project view, showing custom font resource

This works well, even from a regular Android project. However, that Android project also uses a third-party library, where I need to use the same custom font. That library uses the legacy View system, so I have a second copy of my custom font in res/font/ of the module that needs it.

Everything runs and looks correct, but the result is that I have two copies of this font. The font is over 800KB, and ideally I would only have one copy.

Is there a way that I can use a font defined in Compose Resources with a View?

Right now, I am using the Android font resource in a style, via <item name="android:fontFamily">@font/inter_variable</item>. Probably I can switch that to configuring the font in Kotlin. So, for example, if there is a way I can get a Typeface for the Compose Resources font, I may be able to use that with setTypeface() on a TextView.


Solution

  • Using Nima's answer as inspiration, I wound up with:

    @OptIn(ExperimentalResourceApi::class)
    public fun createTypefaceFromComposeResource(context: Context, fontFileName: String): Typeface? = Typeface.createFromAsset(
        context.assets,
        Uri.parse(Res.getUri("font/$fontFileName")).path?.replace("/android_asset/", "")
    )
    

    Usage would be something like:

    myTextView.typeface = createTypefaceFromComposeResource(context, "myAwesomeFont.ttf")
    

    Res.getUri() returns a string representation of an Android asset Uri: file:///android_asset/path/to/the/resource. Right now I'm playing it safe, parsing the Uri and using path to get rid of the scheme. Unfortunately, the android_asset segment is considered to be part of the path, which is why I remove it using replace(). If you wanted, you could skip the Uri.parse() and use replace() for the whole initial segment.