96

I have added a custom font file to my assets/fonts folder. How do I use it from my XML?

I can use it from code as follows:

TextView text = (TextView) findViewById(R.id.textview03);
Typeface tf = Typeface.createFromAsset(getAssets(), "fonts/Molot.otf");
text.setTypeface(tf);

Can't I do it from XML using an android:typeface="/fonts/Molot.otf" attribute?

Daniel Nugent
  • 43,104
  • 15
  • 109
  • 137
Nilanchala
  • 5,891
  • 8
  • 42
  • 72
  • I have searched this a lot and there's no way that you can do it from xml. – C.d. Feb 17 '12 at 11:07
  • 2
    try checking out this post http://stackoverflow.com/questions/2376250/custom-fonts-and-xml-layouts-android – dor506 Feb 17 '12 at 11:08
  • Take a look at [this answer](http://stackoverflow.com/a/41275113/4729523)! It allows you use multiple fonts and using XML. – Rafa0809 Dec 22 '16 at 12:04
  • As other said bellow, you can use [Calligraphy](https://github.com/chrisjenx/Calligraphy) to achieve this. – mbonnin Mar 10 '17 at 11:04
  • Check this article http://www.gadgetsaint.com/tips/set-custom-font-for-textview-and-edittext-in-android-using-fontfamily – ASP Oct 29 '19 at 11:23

11 Answers11

45

Short answer: No. Android doesn't have built-in support for applying custom fonts to text widgets through XML.

However, there's a workaround that's not terribly difficult to implement.

First

You'll need to define your own stylable. In your /res/values folder, open/create the attrs.xml file and add a declare-styleable object like so:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="FontText">
        <attr name="typefaceAsset" format="string"/>
    </declare-styleable>
</resources>

Second

Assuming you want to use this widget often, you should set up a simple cache for the loaded Typeface objects, since loading them from memory on the fly can take time. Something like:

public class FontManager {
    private static FontManager instance;

    private AssetManager mgr;

    private Map<String, Typeface> fonts;

    private FontManager(AssetManager _mgr) {
        mgr = _mgr;
        fonts = new HashMap<String, Typeface>();
    }

    public static void init(AssetManager mgr) {
        instance = new FontManager(mgr);
    }

    public static FontManager getInstance() {
        if (instance == null) {
            // App.getContext() is just one way to get a Context here
            // getContext() is just a method in an Application subclass
            // that returns the application context
            AssetManager assetManager = App.getContext().getAssets();
            init(assetManager);
        }
        return instance;
    }

    public Typeface getFont(String asset) {
        if (fonts.containsKey(asset))
            return fonts.get(asset);

        Typeface font = null;

        try {
            font = Typeface.createFromAsset(mgr, asset);
            fonts.put(asset, font);
        } catch (Exception e) {

        }

        if (font == null) {
            try {
                String fixedAsset = fixAssetFilename(asset);
                font = Typeface.createFromAsset(mgr, fixedAsset);
                fonts.put(asset, font);
                fonts.put(fixedAsset, font);
            } catch (Exception e) {

            }
        }

        return font;
    }

    private String fixAssetFilename(String asset) {
        // Empty font filename?
        // Just return it. We can't help.
        if (TextUtils.isEmpty(asset))
            return asset;

        // Make sure that the font ends in '.ttf' or '.ttc'
        if ((!asset.endsWith(".ttf")) && (!asset.endsWith(".ttc")))
            asset = String.format("%s.ttf", asset);

        return asset;
    }
}

This one will allow you to use .ttc file extensions, but it's untested.

Third

Create a new class that subclasses TextView. This particular example takes into account the defined XML typeface (bold, italic, etc.) and apply it to the font (assuming you're using a .ttc file).

/**
 * TextView subclass which allows the user to define a truetype font file to use as the view's typeface.
 */
public class FontText extends TextView {
    public FontText(Context context) {
        this(context, null);
    }

    public FontText(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public FontText(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);

        if (isInEditMode())
            return;

        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.FontText);

        if (ta != null) {
            String fontAsset = ta.getString(R.styleable.FontText_typefaceAsset);

            if (!TextUtils.isEmpty(fontAsset)) {
                Typeface tf = FontManager.getInstance().getFont(fontAsset);
                int style = Typeface.NORMAL;
                float size = getTextSize();

                if (getTypeface() != null)
                    style = getTypeface().getStyle();

                if (tf != null)
                    setTypeface(tf, style);
                else
                    Log.d("FontText", String.format("Could not create a font from asset: %s", fontAsset));
            }
        }
    }
}

Finally

Replace the instances of TextView in your XML with the fully qualified class name. Declare your custom namespace just like you would the Android namespace. Note that the "typefaceAsset" should point to a .ttf or .ttc file contained in your /assets directory.

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:custom="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.example.FontText
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="This is a custom font text"
        custom:typefaceAsset="fonts/AvenirNext-Regular.ttf"/>
</RelativeLayout>
Daniel Nugent
  • 43,104
  • 15
  • 109
  • 137
themarshal
  • 1,056
  • 1
  • 11
  • 20
  • Great answer! One question though: Why did you return when isInEditMode? – Avi Shukron Apr 11 '15 at 11:25
  • 04-11 18:18:32.685 3506-3506/com.example.demo E/AndroidRuntime﹕ FATAL EXCEPTION: main Process: com.example.demo, PID: 3506 android.view.InflateException: Binary XML file line #2: Error inflating class com.example.demo.libraries.LatoTextView at android.view.LayoutInflater.createView(LayoutInflater.java:620) at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:696) at android.view.LayoutInflater.inflate(LayoutInflater.java:469) at android.view.LayoutInflater.inflate(LayoutInflater.java:397) at ... – e-info128 Apr 11 '15 at 22:21
  • @AvrahamShukron - That's there because I kept getting an error in Android Studio while in preview mode (presumably because it doesn't know how to handle applying a custom typeface. – themarshal Apr 12 '15 at 16:29
  • Added xmlns:custom="http://schemas.android.com/tools" in RelativeLayout and the error was gone. Thanks man – Naveed Ahmad Apr 20 '16 at 11:47
  • 1
    Hello @themarshal: instead of xmlns:custom="http://schemas.android.com/tools" you should use: xmlns:custom="http://schemas.android.com/apk/res-auto" in order to use styleable attributes. – Abhinav Saxena May 23 '16 at 11:47
  • There are libraries that you can abstract all these codes. Add the compile and use CustomTextView in your layout xml. Like this: https://github.com/febaisi/CustomTextView – febaisi Jun 27 '16 at 12:57
  • Why if (isInEditMode()) return;? Does this mean AndroidStudio will not know how to handle this in preview? – Slobodan Antonijević Apr 20 '17 at 11:47
  • With custom fonts which was added in API 26 and backported via a support library it is not possible. – Siyamed Jan 26 '18 at 18:00
28

Here is example code that does this. I have the font defined in a static final variable and the font file is in the assets directory.

public class TextViewWithFont extends TextView {

    public TextViewWithFont(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.setTypeface(MainActivity.typeface);
    }

    public TextViewWithFont(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        this.setTypeface(MainActivity.typeface);
    }

    public TextViewWithFont(Context context) {
        super(context);
        this.setTypeface(MainActivity.typeface);
    }

}
Nikhil Agrawal
  • 26,128
  • 21
  • 90
  • 126
Stephan Wiesner
  • 337
  • 3
  • 4
  • 5
    The OP specifically states that he knows how to set the font programatically (he even provides an example). The question is how to set the font in xml? – SMBiggs Jun 18 '16 at 23:48
13

Create your customed TextView belong to the font you want to use. In this class, I use a static mTypeface field to cache the Typeface (for better performance)

public class HeliVnTextView extends TextView {

/*
 * Caches typefaces based on their file path and name, so that they don't have to be created every time when they are referenced.
 */
private static Typeface mTypeface;

public HeliVnTextView(final Context context) {
    this(context, null);
}

public HeliVnTextView(final Context context, final AttributeSet attrs) {
    this(context, attrs, 0);
}

public HeliVnTextView(final Context context, final AttributeSet attrs, final int defStyle) {
    super(context, attrs, defStyle);

     if (mTypeface == null) {
         mTypeface = Typeface.createFromAsset(context.getAssets(), "HelveticaiDesignVnLt.ttf");
     }
     setTypeface(mTypeface);
}

}

In xml file:

<java.example.HeliVnTextView
        android:id="@+id/textView1"
        android:layout_width="0dp"
        ... />

In java class:

HeliVnTextView title = new HeliVnTextView(getActivity());
title.setText(issue.getName());
haotang
  • 5,520
  • 35
  • 46
11

Activity implements LayoutInflater.Factory2 that provides callbacks on each created View. It's possible to style the TextView with custom font Family attribute, load the typefaces on demand and call setTypeface on instantiated text views automatically.

Unfortunately due to the architectural relationship of Inflater instances relative to Activities and Windows the simplest approach to use custom fonts in android is to cache loaded fonts on the Application level.

The sample code base is here:

https://github.com/leok7v/android-textview-custom-fonts

  <style name="Baroque" parent="@android:style/TextAppearance.Medium">
    <item name="android:layout_width">fill_parent</item>
    <item name="android:layout_height">wrap_content</item>
    <item name="android:textColor">#F2BAD0</item>
    <item name="android:textSize">14pt</item>
    <item name="fontFamily">baroque_script</item>
  </style>

  <?xml version="1.0" encoding="utf-8"?>
  <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
          xmlns:custom="http://schemas.android.com/apk/res/custom.fonts"
          android:orientation="vertical"
          android:layout_width="fill_parent"
          android:layout_height="fill_parent"
  >
  <TextView
    style="@style/Baroque"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:text="@string/sample_text"
  />

results in

enter image description here

Jins Lukose
  • 699
  • 6
  • 19
Leo
  • 790
  • 8
  • 10
  • Did not touch that code for 2 years. But it should. I don't see anything in theory preventing it. – Leo Feb 21 '16 at 21:55
7

Not a good idea to use custom fonts in xml due to this fact that is, you have to do it programmatically to avoid the memory leak!

Janak Nirmal
  • 22,706
  • 18
  • 63
  • 99
type-a1pha
  • 1,891
  • 13
  • 19
7

UPDATE: https://github.com/chrisjenx/Calligraphy appears to be a superior solution to this.


Maybe you can use reflection to inject/hack your font into the static list of available fonts when your application is created? I am interested in feedback from others if this is a really, really bad idea or if this is a great solution — it seems it is going to be one of those extremes...

I was able to inject my custom typeface into the list of system typefaces with my own font family name, then specifying that custom font family name ("brush-script") as the value of android:FontFamily on a standard TextView worked on my LG G4 running Android 6.0.

public class MyApplication extends android.app.Application
{
    @Override
    public void onCreate()
    {
        super.onCreate();

        Typeface font = Typeface.createFromAsset(this.getResources().getAssets(),"fonts/brush-script.ttf");
        injectTypeface("brush-script", font);
    }

    private boolean injectTypeface(String fontFamily, Typeface typeface)
    {
        try
        {
            Field field = Typeface.class.getDeclaredField("sSystemFontMap");
            field.setAccessible(true);
            Object fieldValue = field.get(null);
            Map<String, Typeface> map = (Map<String, Typeface>) fieldValue;
            map.put(fontFamily, typeface);
            return true;
        }
        catch (Exception e)
        {
            Log.e("Font-Injection", "Failed to inject typeface.", e);
        }
        return false;
    }
}

In my layout

<TextView
    android:id="@+id/name"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Fancy Text"
    android:fontFamily="brush-script"/>
Ross Bradbury
  • 605
  • 5
  • 13
  • Doesn't work for me, can you give more detail how to use it? Does the `MyApplication` class needs to be called? – Aesthetic Mar 28 '16 at 08:53
  • @Yawz you would need to call the injectTypeface method at least once in your application. If it logs an exception I would be interested in the details. I only tested this on my LG G4 running Android 6.0 (I was doing some of my own research at the time) and I expect it doesn't work on all versions of Android. – Ross Bradbury Sep 01 '16 at 12:51
  • @RossBradbury it not work on some devices.. most of the devices working correctly but some device not accept this fontfamily.. my tested device - lenova a7000 model – Ranjithkumar Nov 04 '16 at 10:16
  • UPDATE: https://github.com/chrisjenx/Calligraphy is a superior solution. – Ross Bradbury Nov 30 '16 at 17:50
6

Create a fonts folder in assets and add all your required font's there.

public class CustomTextView extends TextView {
    private static final String TAG = "TextView";

    public CustomTextView(Context context) {
        super(context);
    }

    public CustomTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
        setCustomFont(context, attrs);
    }

    public CustomTextView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        setCustomFont(context, attrs);
    }

    private void setCustomFont(Context ctx, AttributeSet attrs) {
        TypedArray a = ctx.obtainStyledAttributes(attrs, R.styleable.CustomTextView);
        String customFont = a.getString(R.styleable.CustomTextView_customFont);
        setCustomFont(ctx, customFont);
        a.recycle();
    }

    public boolean setCustomFont(Context ctx, String fontName) {
        Typeface typeface = null;
        try {
            if(fontName == null){
                fontName = Constants.DEFAULT_FONT_NAME;
            }
            typeface = Typeface.createFromAsset(ctx.getAssets(), "fonts/" + fontName);
        } catch (Exception e) {
            Log.e(TAG, "Unable to load typeface: "+e.getMessage());
            return false;
        }
        setTypeface(typeface);
        return true;
    }
}

and add a declarable in attrs.xml

<declare-styleable name="CustomTextView">
      <attr name="customFont" format="string"/>
</declare-styleable>

and then add your customFont like

app:customFont="arial.ttf"
Zar E Ahmer
  • 33,936
  • 20
  • 234
  • 300
  • you can also customize above class to work with style as done in here .https://github.com/leok7v/android-textview-custom-fonts/blob/master/res/values/styles.xml – Zar E Ahmer May 30 '17 at 11:29
6

I know this is an old question, but i've found a much easier solution.

First declare your TextView in xml as usual. Put your font (TTF or TTC) in the asset folder

app\src\main\assets\

Then just set the typeface for your text view in your onCreate method.

@Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_name);    

    TextView textView = findViewById(R.id.my_textView);
    Typeface typeface = Typeface.createFromAsset(getAssets(), "fontName.ttf");
    textView.setTypeface(typeface);
}

Done.

Oiproks
  • 764
  • 1
  • 9
  • 34
2

The best solution is to use (finally) introduced by Google a native custom font feature in XML. But you have to target API 26. It supports API 16+

https://developer.android.com/guide/topics/ui/look-and-feel/fonts-in-xml

Kirill Karmazin
  • 6,256
  • 2
  • 54
  • 42
0

instead of xmlns:custom="schemas.android.com/tools"; you should use: xmlns:custom="schemas.android.com/apk/res-auto"; in order to use styleable attributes. I made this change and it is working now.

Abhinav Saxena
  • 1,990
  • 2
  • 24
  • 55
0

the latest update now that you can set the font in XML without any other classes added like:

android:fontFamily="@font_folder/font_file"
Sattar
  • 2,453
  • 2
  • 33
  • 47