4

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.

2
  • MoKo-Resource#font Commented Jan 14 at 20:10
  • @Jay: Thanks, but I would prefer to stick with JetBrains' implementation. Commented Jan 14 at 20:18

3 Answers 3

4
+100

Since Compose Multiplatform 1.7.3, The resources are getting packed as Android assets already,

Therefore Typeface.createFromFile(File(Res.getUri('font/path/in/multiplat/commons/'))) should work.

Sign up to request clarification or add additional context in comments.

4 Comments

Hope this helps! Also thanks for years of service and the awesome giant book you gave us almost a decade ago. You've answered plenty of my questions here before 🙂 hope things are well with you. Have a nice year 🙌
Thanks for the kind words! In terms of your answer, the font isn't a file (assets are entries in an APK), so createFromFile() won't work. But createFromAsset() might. I will give that a try later this week. Thanks for pointing this out!
Here is my implementation, using createFromAsset(). Thanks for steering me in the right direction!
nice! twas my honor
4

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.

Comments

1

You can use a Typeface from the font defined in Compose Resources and set it on a TextView in your legacy Views. You can achieve this simply by defining a helper function to convert FontFamily to Typeface, I have attached an example below, take a look:

Add the following library if you haven’t already:

implementation "androidx.compose.ui:ui-text:{latest_version}"

then create a helper function like this

fun getTypefaceFromComposeFont(
    context: Context,
    fontFamily: FontFamily
): Typeface? {
    val resourceLoader = ResourceLoader(context)
    val firstFont = (fontFamily as? FontFamily.Custom)?.fonts?.firstOrNull() as? Font
    return firstFont?.let {
        resourceLoader.load(it)
    }
}

then use it like this with your legacy view system TextViews

val typeface = getTypefaceFromComposeFont(context, MyDesignSystemFonts.MyCustomFont)

textView.typeface = typeface

Since this is not what you want, you can simply add your font resource in the resource directory of your project and then use it in your font file like this

enter image description here

You can keep this font in the res directory of your folder like this

enter image description here

Then you can simply use it whereever you want.

7 Comments

The only ResourceLoader that I am seeing is Font.ResourceLoader, which is an interface and cannot be instantiated (and also is deprecated). Neither the platform nor the Compose edition of FontFamily have FontFamily.Custom.
Checkout my updates in the prevous answer, hope this will help you
That is not what I am asking for. My design system is in Compose Multiplatform. I want to keep the font file in Compose Resources and use it, as needed, for a View.
Both the Compose Multiplatform and View system modules reference this shared directory. I don’t know why you can’t do it for your design system. This is the only solution I have used or done before.
"Both the Compose Multiplatform and View system modules reference this shared directory" -- the Compose Resources plugin uses composeResources/ directory, as shown in the screenshot in my question. Android's build tools use res/. Do you have a reference to documentation or a full sample project that show a Compose Multiplatform project using res/?
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.