How to use specified weights for fonts in XML

Using the Fonts in XML feature you can specify various font weights for a font family. For example:

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

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

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

    <font android:font="@font/archivo_narrow_medium" android:fontWeight="500" android:fontStyle="normal"
        app:font="@font/archivo_narrow_medium" app:fontWeight="500" app:fontStyle="normal"/>

    <font android:font="@font/archivo_narrow_medium_italic" android:fontWeight="500" android:fontStyle="italic"
        app:font="@font/archivo_narrow_medium_italic" app:fontWeight="500" app:fontStyle="italic"/>

    <font android:font="@font/archivo_narrow_semibold" android:fontWeight="600" android:fontStyle="normal"
        app:font="@font/archivo_narrow_semibold" app:fontWeight="600" app:fontStyle="normal"/>

    <font android:font="@font/archivo_narrow_semibold_italic" android:fontWeight="600" android:fontStyle="italic"
        app:font="@font/archivo_narrow_semibold_italic" app:fontWeight="600" app:fontStyle="italic"/>

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

    <font android:font="@font/archivo_narrow_bold_italic" android:fontWeight="700" android:fontStyle="italic"
        app:font="@font/archivo_narrow_bold_italic" app:fontWeight="700" app:fontStyle="italic"/>

</font-family>

But I cannot figure out how to actually make use of each of these weights; either in an XML (layout/style) file, or in Java code. Their is no fontWeight attribute available for TextView, and the Typeface object created from ResourcesCompat.getFont(context, R.font.archivo_narrow) has no mention of font weights.

I realize that I can just specify the specific font resource (i.e. R.font.archivo_narrow_semibold), but then what is the point of having a fontWeight attribute in the font-family?


Update

A new static create(Typeface family, int weight, boolean italic) method was added in API Level 28, along with a getWeight() instance method. This finally makes it possible to make use of the fontWeight attribute in Java code; though only for API Level 28 and above, I haven't found any analogs in the support library.

This is useful—and shows that the fontWeight attribute didn't serve any purpose in the past—but I would really like to be able to use the weight in XML styling.


Its looks like android following web standards for font management and sizing for android app.

The “font-weight” property is used to define the weight of a font, such as regular or bold.

But for all other weights a numerical range from 100 to 900 is used. One of the challenges with web fonts is that most web browsers do not properly support font weights other than normal & bold. The following chart describes the possible mappings of weights to the numeric definitions:

100    Extra Light or Ultra Light
200    Light or Thin
300    Book or Demi
400    Normal or Regular
500    Medium
600    Semibold, Demibold
700    Bold
800    Black, Extra Bold or Heavy
900    Extra Black, Fat, Poster or Ultra Black

You can read more about font weight here


cc_montserrat_bold.xml

<?xml version="1.0" encoding="utf-8"?>
<font-family xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <font
        android:font="@font/montserrat_bold"
        android:fontStyle="normal"
        android:fontWeight="700"
        app:font="@font/montserrat_bold"
        app:fontStyle="normal"
        app:fontWeight="700" />
    <font
        android:font="@font/montserrat_bolditalic"
        android:fontStyle="italic"
        android:fontWeight="700"
        app:font="@font/montserrat_bolditalic"
        app:fontStyle="italic"
        app:fontWeight="700" />

</font-family>

cc_montserrat_regular.xml

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

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


</font-family>

Kotlin Usage:

val textView = dialog.findViewById<TextView>(android.R.id.message) as TextView
val typeface = ResourcesCompat.getFont(context,R.font.cc_montserrat_regular)
        textView.typeface = typeface

Android Project Screenshot:

enter image description here


As pointed out by @FlorianWalther, the TextView.textFontWeight attribute is exactly what I was looking for. This attribute was apparently added in API level 28, but was not documented until recently.

Make sure all of the weights are in the same XML file (as in my question), then simply use the attribute along with the fontFamily attribute.

<TextView android:id="@+id/weightedTextView"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:fontFamily="@font/archivo_narrow"
    android:textFontWeight="700"/>

As far as I am aware this attribute is only available in API Level 28 an above, I will update if I find its counterpart in the support library.


Florian's right in the comments about < API 28, and just to be explicit about it (since I spent ages trying to puzzle out how this works):

Android seems to ignore everything wthin a font family except font weights 700 and 400, which it uses for bold and non-bold textStyles respectively.

That's it. You can have regular 400/700, and italic 400/700. None of the other definitions seem to get used, and it's only with the newer APIs you can actually do anything with them. If I'm missing something let me know, but that seems like all you can control on lower APIs - bold or not.


The system does seem to mess around looking for an alternative weight, so if you specify bold but you don't have anything defined with weight 700 it will pull another weight (even an italic variant), but you can't actually say "use Medium 500 for this style" - you have to create a separate font family and explicitly use it, and at that point you might as well just specify the actual font instead?


For completeness's sake, I have done the multiple font family thing (a separate one for each weight I'm using - regular, medium etc.) and applied them in the styles generated by the Material Design type scale generator. The type scale uses different weights, like light for Headline1 and medium for Subtitle2 but obviously Android isn't using them (and they're not specified in the style hierarchy either).

So you can fix that by adding the font family reference into the generated styles:

<style name="TextAppearance.MdcTypographyStyles.Headline1" parent="TextAppearance.MaterialComponents.Headline1">
    <item name="fontFamily">@font/my_font_weight_light</item>
    <item name="android:fontFamily">@font/my_font_weight_light</item>
    <item name="android:textSize">123sp</item>
    <item name="android:letterSpacing">-0.0122</item>
</style>

where my_font_weight_light.xml is a font family that only includes the light variant of the font

<font-family xmlns:app="http://schemas.android.com/apk/res-auto">
    <font
        app:font="@font/my_font_light_italic"
        app:fontStyle="italic"
        app:fontWeight="400" />
    <font
        app:font="@font/my_font_light"
        app:fontStyle="normal"
        app:fontWeight="400" />
</font-family>

The weight is wrong, 400 is regular, but I figure if Android is only using 400 for non-bold text anyway, there's less chance of it doing something weird if I just give it the value it expects, so it doesn't have to go hunting for an alternative (and maybe picking the italic). The "weight" is being defined by which font family I'm applying to the style anyway. I didn't bother adding bold since the styles use fixed weights and I'm not using bold anyway.

(edit - Jimit Patel mentions in the comments that this didn't work for them in one case, and using a bold font required specifying a weight of 700 to get it consistent across different APIs. So maybe the correct weight is important - or at least picking whichever of 400 or 700 is closest to it. I can't devote time to testing all this so I'm just putting out there!)


The next fun thing is, if you're applying a font family to your whole app through your theme by setting the fontFamily attributes, that will override any fontFamily settings you apply through textAppearance because theme styles take precedence over textAppearance. So your "weighted" styles will lose their "weight", because fontFamily will get set back to the normal version with the regular 400 value present.

To get around this, you have to apply your style as a style, not a textAppearance, i.e:

style="@style/TextAppearance.MdcTypographyStyles.Headline1"

fun!!