84

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.

Community
  • 1
  • 1
Bryan
  • 14,756
  • 10
  • 70
  • 125

11 Answers11

79

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    Thin (Hairline)
200    Extra Light (Ultra Light)
300    Light
400    Normal (Regular)
500    Medium
600    Semi Bold (Demi Bold)
700    Bold
800    Extra Bold (Ultra Bold)
900    Black (Heavy)
950    Extra Black (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

mtrakal
  • 6,121
  • 2
  • 25
  • 35
Rajesh Dalsaniya
  • 1,977
  • 1
  • 13
  • 12
  • 27
    Yes, but how do I *use* the various font-weights? – Bryan Oct 13 '17 at 14:42
  • Don’t weight to identify fonts individually? Can you give me example what confusing you ? – Rajesh Dalsaniya Oct 15 '17 at 17:46
  • 11
    I can specify the `font-weight` attribute within the `` tag, but I cannot find a way to *reference* that `font-weight` attribute anywhere else in my code (i.e. I can *set* the weight, I just can't *use* the weight). There is a [`textStyle`](https://developer.android.com/reference/android/widget/TextView.html#attr_android:textStyle) attribute, but you can only specify `bold`, `italic` or `bold|italic`. There is no `semibold` for example. – Bryan Oct 16 '17 at 14:05
  • @Bryan I think font weighs is for internal reference only you don’t need to define in code. As example of you want to use font in programming you can get it by R.font.fontname_type – Rajesh Dalsaniya Oct 16 '17 at 14:09
  • @Bryan I am using font in my project this way. 1. I defined xml as you did above then in code `val typeface = ResourcesCompat.getFont(context,R.font.cc_montserrat_regular) textView.typeface = typeface` – Rajesh Dalsaniya Oct 16 '17 at 14:12
  • 3
    Then why have the `font-weight` attribute at all? What does it do internally? It seems useless if I have no way to reference it. – Bryan Oct 16 '17 at 14:13
  • @Bryan Check updated answer. How I am using fonts in my project. – Rajesh Dalsaniya Oct 16 '17 at 14:21
  • @Bryan you need to create separate XML for each font type bold regular extra bold etc – Rajesh Dalsaniya Oct 16 '17 at 14:30
  • 8
    That just seems like overkill. A `font-family` usually encompasses all of the variants, not just the `normal` and `italic` variants of a specific weight. And it still doesn't explain why there is a `font-weight` attribute that I *cannot make use of*. – Bryan Oct 16 '17 at 14:50
  • 15
    @Bryan, I've wasted hours trying to find an answer to this very simple question. Not sure why everyone keeps suggesting ridiculous work-arounds. Did you ever find a solution? – Theo Dec 19 '17 at 18:08
  • @Theo Nope, looks like using separate font resources is the only solution so far. I've been keeping an eye out for any changes to the API though, I'll update if I find anything. – Bryan Dec 19 '17 at 20:30
  • @Theo Anyone have checked this Android font document? https://developer.android.com/guide/topics/ui/look-and-feel/fonts-in-xml.html – Rajesh Dalsaniya Dec 20 '17 at 02:58
  • 2
    @RajeshDalsaniya I referenced that document in my original question. Again, it shows how to specify the `font-weight` attribute, but to [*use*](https://developer.android.com/guide/topics/ui/look-and-feel/fonts-in-xml.html#fonts-in-code) the font you need to obtain a [`Typeface`](https://developer.android.com/reference/android/graphics/Typeface.html) instance; which doesn't have any reference to a `font-weight` attribute. – Bryan Dec 20 '17 at 13:55
  • From what I understand, below API 28, this only distinguishes between regular and bold. – Florian Walther Apr 14 '19 at 14:51
  • 5
    Crazy that there is still no good compatible solution for this, crazy that this terrible rephrasing of the question posted as an answer got more than 50 votes – Nick Cardoso Jan 19 '21 at 11:35
31

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.

Bryan
  • 14,756
  • 10
  • 70
  • 125
21

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!!

cactustictacs
  • 17,935
  • 2
  • 14
  • 25
  • Nicely articulated. I have one more observation to add. If I am using `fontWeight="400"` on **bold TTF font file**, it behaves weird on different Android API versions and if I use `fontWeight="700"` then it works exactly as expected. – Jimit Patel Dec 04 '20 at 13:10
  • Is that with ``textStyle`` (when you actually use the font, like when you're setting up a ``TextView``) set to ``normal`` or ``bold``? If it's ``bold`` then I'd kinda expect that, since that basically says "look for weight 700" and if you don't have one defined in your font it might get weird. This hacky approach is basically creating a new font for each weight, and treating them all as ``regular`` for ``textStyle`` - so if you want bold, you need to use whatever font has the medium/semibold/bold variant – cactustictacs Dec 04 '20 at 14:26
  • If it's still weird even with ``regular`` or ``normal`` or whatever the ``textStyle`` value is, let me know and I'll add a note in the answer! – cactustictacs Dec 04 '20 at 14:28
  • 1
    Nowhere special attributes were used for making it bold. I am using the bold font in order to have everything according to the brand, but this was caught while testing for the same `TextView` over different devices. You can test it for API 21 to 29, for me, 21, 22, and 29 worked fine rest all were giving extra bold. Check for 2 or 3 different TTFs for getting more accurate observations. – Jimit Patel Dec 11 '20 at 10:16
  • 1
    Hrm ok, I haven't seen that myself (mostly looking at API 24, 29 and 30) but maybe I just didn't notice, or it could be font-dependent too, or the support library version... I'll just add a note telling people to check it works and try different weights if it doesn't, my **400** idea was just a guess anyway. Thanks! – cactustictacs Dec 11 '20 at 12:39
  • From testing on API 27: Using *only* android:fontFamily to set a default font works, and styles and attributes setting fontFamily for the view element overrides it: You can get e.g. a semibold font if you add it to the app as per current guidelines, set the specific font, and don't set the fontWeight for the style or view element. – Henrik Erlandsson Oct 26 '22 at 09:17
4

Since the textFontWeight attribute is API level 28 and higher a workaround for supporting older devices is to create a separate fontFamily for each font-weight and reference the fontFamily from the View.

res/layout/your_layout.xml

<TextView
    ...
    android:fontFamily="@font/fontFamily_your_font_demibold 
/>

res/font/fontFamily_your_font_demibold.xml

<font-family xmlns:app="http://schemas.android.com/apk/res-auto">

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

</font-family>

res/font/fontFamily_your_font_bold.xml

<font-family xmlns:app="http://schemas.android.com/apk/res-auto">

    <font
        app:font="@font/your_font_bold"
        app:fontStyle="normal"
        app:fontWeight="800" />

</font-family>
Marius Kohmann
  • 713
  • 1
  • 8
  • 11
  • 4
    fontfamily is useless in this case and using directly the font in the textview will be better – Magnas Jan 15 '20 at 10:19
3

I struggled with several weights of a font: bold, semibold, medium, regular. For me it finally worked when I created a .xml file for each weight, with normal and italic, and then referencing them in the layout or style with both android:fontFamily: and fontFamily:. The latter was important, cause otherwise it would only work in very high API levels.

    <font-family xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">


    <font android:font="@font/internal_inter_bold" 
        android:fontStyle="normal"
        android:fontWeight="700"
        app:font="@font/internal_inter_bold"
        app:fontStyle="normal"
        app:fontWeight="700" />
    <font android:font="@font/internal_inter_bold_italic"
        android:fontStyle="italic"
        android:fontWeight="700"
        app:font="@font/internal_inter_bold_italic"
        app:fontStyle="italic"
        app:fontWeight="700" />
     </font-family>



     <style name="numbers_large">
        <item name="android:textSize">32sp</item>
        <item name="android:fontFamily">@font/inter_bold</item>
        <item name="fontFamily">@font/inter_bold</item>
    </style>

This works even in Android 5.0, didn't test it for lower ones.

So basically create a font.xml file for each font weight, use both app: and android: prefixes, and then reference them with both prefixes.

Peterdk
  • 15,625
  • 20
  • 101
  • 140
1

Edit: this isn't a solution the the request :/

I've found the Typeface.Builder class which I think is what you want. Unfortunately it is only available from API level 26 and up. Perhaps a compat library will appear soon.

https://developer.android.com/reference/android/graphics/Typeface.Builder.html

 Typeface.Builder buidler = new Typeface.Builder("your_font_file.ttf");
 builder.setFontVariationSettings("'wght' 700, 'slnt' 20, 'ital' 1");
 builder.setWeight(700);  // Tell the system that this is a bold font.
 builder.setItalic(true);  // Tell the system that this is an italic style font.
 Typeface typeface = builder.build();
kassim
  • 3,880
  • 3
  • 26
  • 27
  • No, unfortunately you can only set the weight through `Typeface.Builder`; there is still no way to reference that weight through the `Typeface` that is created. – Bryan Feb 23 '18 at 17:21
  • 1
    Yeah, sorry, I found a weight input and got excited without fully understanding. - I found your StackOverflow trying to figure out the same thing myself. After trawling through source code all I can find is that if you refer to a font family it will try its hardest to get the closest weight to bold (700) if you specify that as the text style, otherwise it will find the closest to 400. There's no way to request what weight you want. Hopefully they put it in soon, I have semi-bold used as my title weight in most designs and it would be really useful to have the weight input. – kassim Feb 27 '18 at 11:28
1

here how you can user different font's weights in android studio

  1. Open app/src/main/res/layout/you_awesome_layout.xml
  2. Select the Design tab
  3. In the Component Tree panel, open you TextView and select View all attributes at the bottom of the right menu
  4. In the Attributes panel, open the fontFamily dropdown and select More Fonts…
  5. Select family YouFontFamilyName
  6. Select style Regular/Bold/Semibold/Black/WhateverStyleYouHaveThere

Voila, I think this is the only way to achieve what you want

Wackaloon
  • 2,285
  • 1
  • 16
  • 33
  • I cannot get more than two styles to appear in my custom fonts (for example only `Bold` and `Bold Italic` show up). For the available Google fonts (under the `Downloadable` header) there can be more styles; for example Advent Pro has Thin, ExtraLight, Light, Regular, Medium, SemiBold, and Bold. Any idea as to how I can mimic this functionality with my custom fonts? – Bryan Sep 27 '18 at 19:08
0

If you target an API level below 28 and you do not want to create 2 XMLs, you can use this extension:

@BindingAdapter("textFontWeight")
fun TextView.textFontWeight(int: Int) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
        typeface = Typeface.create(typeface, int, false)
    }
}

And use it like this:

    <TextView
    ...
    app:textFontWeight="@{700}"/>
Iman Dolatkia
  • 186
  • 1
  • 7
0

The functionality was backported to AndroidX Core 1.9.0-beta01 released August 10, 2022. So, usable below API 28 now.

Backported Typeface#create(Typeface, int, boolean) which allows creating typeface with specific weight from a font family (I342dc)

https://developer.android.com/reference/androidx/core/graphics/TypefaceCompat#create(android.content.Context,android.graphics.Typeface,int,boolean)

Typeface family = ResourcesCompat.getFont(context, R.font.archivo_narrow);
int weight = 600;
boolean italic = false;
Typeface typeface = TypefaceCompat.create(context, family, weight, italic);
textView.setTypeface(typeface);
TalkLittle
  • 8,866
  • 6
  • 54
  • 51
-1

Step 1: Find or Download your font, let's say cavier_dream

Step 2: Right-click on the res folder and create the font resource folder inside the res folder

Step 3: Right-click on font folder and create a font resource file, let's say app_font.xml

Step 4: Open the app_font.xml file and modify as follows

<?xml version="1.0" encoding="utf-8"?>

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

Usage:

For global usage across the application go to the styles.xml and inside the theme, create a new item as follow

 <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
    <!-- Customize your theme here. -->
    <item name="colorPrimary">@color/colorPrimary</item>
    <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
    <item name="colorAccent">@color/colorAccent</item>
    <item name="android:fontFamily">@font/app_font</item>
</style>

And for local usage simply use the fontFamily attribute and set the app_font as value.

Happy coding:)

Minion
  • 964
  • 14
  • 16
  • This actually works, but i think that it could be misleading as you have to set a fontFamily when you actually want to set the fontStyle/fontWeight. – Marius Kohmann Nov 29 '19 at 14:26
-2

Please see the official Android reference for Font Weight Values:

android:fontWeight

Integer. The weight of the font. This attribute is used when the font is loaded into the font stack and overrides any weight information in the font's header tables. The attribute value must be a positive number, a multiple of 100, and between 100 and 900, inclusive. If you do not specify the attribute, the app uses the value from the font's header tables. The most common values are 400 for regular weight and 700 for bold weight.

https://developer.android.com/guide/topics/resources/font-resource

Jacob N.
  • 9
  • 2