Using a custom typeface in Android
Asked Answered
M

21

111

I want to use a custom font for my android application which I am creating.
I can individually change the typeface of each object from Code, but I have hundreds of them.

So,

  • Is there a way to do this from the XML? [Setting a custom typeface]
  • Is there a way to do it from code in one place, to say that the whole application and all the components should use the custom typeface instead of the default one?
Michiko answered 4/6, 2010 at 10:28 Comment(4)
#5541558 Look at this post I posted the complete code here regarding this issueAcatalectic
You could use static variables on your main activity to hold references to the embedded fonts. That would cause there to be one persistent set of fonts that won't get picked up by GC.Recede
Factory method. Or a method that takes your view and sets all the font and typeface settings.Sheepshead
New support library 26 now allows you to use fonts in XML. Here is how to it Fonts in XMLRoma
C
80

Is there a way to do this from the XML?

No, sorry. You can only specify the built-in typefaces through XML.

Is there a way to do it from code in one place, to say that the whole application and all the components should use the custom typeface instead of the default one?

Not that I am aware of.

There are a variety of options for these nowadays:

  • Font resources and backports in the Android SDK, if you are using appcompat

  • Third-party libraries for those not using appcompat, though not all will support defining the font in layout resources

Chancy answered 4/6, 2010 at 12:11 Comment(17)
Oh! That is really disappointng. I have been searching the whole internet and was optimistic. Does that mean, I have to, from the code set the typeface for each UI component?Michiko
@Codevalley: Well, in many ways, the better answer is not to fuss with custom fonts. They tend to be large, making your downloads bigger and reducing the number of people who will download and keep using your app.Chancy
@CommonsWare: I don't necessarily agree. Typeface is a integral part of UI Styling like you do with backgrounds images etc. And size is not necessarily big always. For instance, the font in my case is just 19.6KB :)Michiko
Wouldn't it be possible to create a custom theme, with the wanted typeface?Gamosepalous
@Gamosepalous Unfortunately, only way to load custom Typefaces in Android is by specifying that in code. You can't do that from XML.Michiko
@Michiko Have you had the opportunity to try Manish's code snippet?Monophyletic
can anyone suggest me how to use one font through out the whole app.Lawrencelawrencium
I would argue that this is a bizarre and terrible oversight on Google's part, and hope that they remedy it soon.Abscind
@Codevalley, I think we have to face up to the fact that styling was an afterthought for the originators of Android.Traitorous
@Chancy What will be your answer now (after more than a year)? Is their any central place to do that ..Positronium
@Amit: There has been no change on this issue. There are plenty of code snippets around to help simplify applying a typeface to your UI in Java, but there's no way to do it from XML.Chancy
@Chancy Thanks, for me and most of other Android programmers senior guys like you are the authority. If you are saying that it is not possible than I will stop wasting my time on it.... But does that means I will have to create my own .ttf or .otf files for custom fonts ? Any suggested reading from your side on this issue will be more helpful. Thanks!!Positronium
@Amit: "But does that means I will have to create my own .ttf or .otf files for custom fonts ?" -- um, no. You can use existing TTF/OTF fonts, though they may not all work. The issue on this question is how to apply those fonts across an entire app, which still isn't possible, and that is what I was referring to in my comment.Chancy
@CommonsWare, Actually I also wanted to apply fonts across entire app. So that I don't have to do that everywhere and future changes can be done in minutes... I am afraid their is no such way.Positronium
@Positronium I believe the misunderstanding here is that TextView doesn't support it natively. You can fairly simply write a custom component that reads which typeface you wish to use from the layout or style attributes. Here is such an example from one of my conference talks. (Note that there's also a PowerPoint presentation on the repository that has an explanation.)Monophyletic
This might be useful if you want to apply custom fonts for the whole application. github.com/DarryQueen/Flare/blob/master/Flare/src/com/flare/…Weal
This accepted answer is obsolete, and it would be great to update it or delete it.Supercharger
A
109

Yes It is possible.

You have to create a custom view which extends text view.

In attrs.xml in values folder:

<resources>
    <declare-styleable name="MyTextView">
        <attr name="first_name" format="string"/>
        <attr name="last_name" format="string"/>
        <attr name="ttf_name" format="string"/>
    </declare-styleable>
</resources>

In main.xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:lht="http://schemas.android.com/apk/res/com.lht"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
    <TextView  android:layout_width="fill_parent" 
        android:layout_height="wrap_content"
        android:text="Hello"/>
    <com.lht.ui.MyTextView  
        android:id="@+id/MyTextView"
        android:layout_width="fill_parent" 
        android:layout_height="wrap_content"
        android:text="Hello friends"
        lht:ttf_name="ITCBLKAD.TTF"
        />   
</LinearLayout>

In MyTextView.java:

package com.lht.ui;

import android.content.Context;
import android.graphics.Typeface;
import android.util.AttributeSet;
import android.util.Log;
import android.widget.TextView;

public class MyTextView extends TextView {

    Context context;
    String ttfName;

    String TAG = getClass().getName();

    public MyTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.context = context;

        for (int i = 0; i < attrs.getAttributeCount(); i++) {
            Log.i(TAG, attrs.getAttributeName(i));
            /*
             * Read value of custom attributes
             */

            this.ttfName = attrs.getAttributeValue(
                    "http://schemas.android.com/apk/res/com.lht", "ttf_name");
            Log.i(TAG, "firstText " + firstText);
            // Log.i(TAG, "lastText "+ lastText);

            init();
        }

    }

    private void init() {
        Typeface font = Typeface.createFromAsset(context.getAssets(), ttfName);
        setTypeface(font);
    }

    @Override
    public void setTypeface(Typeface tf) {

        // TODO Auto-generated method stub
        super.setTypeface(tf);
    }

}
Acatalectic answered 3/3, 2011 at 19:36 Comment(11)
This will work, but only for TextView. It will require custom subclasses for every widget class that inherits from TextView where the same capability is desired.Chancy
Hi Manish, Thanks for the solution. Though Im facing a problem in using this. I keep getting 'no resource identifier found for attribute ttf_name in package com.lht.android'Molybdenous
This will work, but pre-ICS it will allocate memory for the fonts for each view you instantiate: code.google.com/p/android/issues/detail?id=9904 A way to fix this is to create a globally-accessible static hashmap of all instantiated fonts: code.google.com/p/android/issues/detail?id=9904#c7Pulmonary
Why do we need a loop?, Should it not work with a single call?Suzisuzie
@KenV.H. True, but this can be sorted out using an enum in the attrs.xml styleable instead of a string and then in the TextView subclass link with the static instantiated fonts.Dispread
A nice step by step tutorial kotikan.com/blog/posts/2012/09/android-attributes to use together with this answerThroe
This is of course a valid solution, but if you've already created a custom class, why not just set the typeface right in the constructor? No need to play around with custom attributes.Doehne
@AlaksiejN. in case you need to set different typefaces to different TextViews...Upcast
Can i set the tag from a style xml resource?Wile
Sorry for late reply....Actually I was not following my Post...I dont have any other thing regarding this post....Acatalectic
Now it can be achieved by this way also :- developer.android.com/guide/topics/ui/look-and-feel/…Acatalectic
C
80

Is there a way to do this from the XML?

No, sorry. You can only specify the built-in typefaces through XML.

Is there a way to do it from code in one place, to say that the whole application and all the components should use the custom typeface instead of the default one?

Not that I am aware of.

There are a variety of options for these nowadays:

  • Font resources and backports in the Android SDK, if you are using appcompat

  • Third-party libraries for those not using appcompat, though not all will support defining the font in layout resources

Chancy answered 4/6, 2010 at 12:11 Comment(17)
Oh! That is really disappointng. I have been searching the whole internet and was optimistic. Does that mean, I have to, from the code set the typeface for each UI component?Michiko
@Codevalley: Well, in many ways, the better answer is not to fuss with custom fonts. They tend to be large, making your downloads bigger and reducing the number of people who will download and keep using your app.Chancy
@CommonsWare: I don't necessarily agree. Typeface is a integral part of UI Styling like you do with backgrounds images etc. And size is not necessarily big always. For instance, the font in my case is just 19.6KB :)Michiko
Wouldn't it be possible to create a custom theme, with the wanted typeface?Gamosepalous
@Gamosepalous Unfortunately, only way to load custom Typefaces in Android is by specifying that in code. You can't do that from XML.Michiko
@Michiko Have you had the opportunity to try Manish's code snippet?Monophyletic
can anyone suggest me how to use one font through out the whole app.Lawrencelawrencium
I would argue that this is a bizarre and terrible oversight on Google's part, and hope that they remedy it soon.Abscind
@Codevalley, I think we have to face up to the fact that styling was an afterthought for the originators of Android.Traitorous
@Chancy What will be your answer now (after more than a year)? Is their any central place to do that ..Positronium
@Amit: There has been no change on this issue. There are plenty of code snippets around to help simplify applying a typeface to your UI in Java, but there's no way to do it from XML.Chancy
@Chancy Thanks, for me and most of other Android programmers senior guys like you are the authority. If you are saying that it is not possible than I will stop wasting my time on it.... But does that means I will have to create my own .ttf or .otf files for custom fonts ? Any suggested reading from your side on this issue will be more helpful. Thanks!!Positronium
@Amit: "But does that means I will have to create my own .ttf or .otf files for custom fonts ?" -- um, no. You can use existing TTF/OTF fonts, though they may not all work. The issue on this question is how to apply those fonts across an entire app, which still isn't possible, and that is what I was referring to in my comment.Chancy
@CommonsWare, Actually I also wanted to apply fonts across entire app. So that I don't have to do that everywhere and future changes can be done in minutes... I am afraid their is no such way.Positronium
@Positronium I believe the misunderstanding here is that TextView doesn't support it natively. You can fairly simply write a custom component that reads which typeface you wish to use from the layout or style attributes. Here is such an example from one of my conference talks. (Note that there's also a PowerPoint presentation on the repository that has an explanation.)Monophyletic
This might be useful if you want to apply custom fonts for the whole application. github.com/DarryQueen/Flare/blob/master/Flare/src/com/flare/…Weal
This accepted answer is obsolete, and it would be great to update it or delete it.Supercharger
S
49

I did this in a more "brute force" way that doesn't require changes to the layout xml or Activities.

Tested on Android version 2.1 through 4.4. Run this at app startup, in your Application class:

private void setDefaultFont() {

    try {
        final Typeface bold = Typeface.createFromAsset(getAssets(), DEFAULT_BOLD_FONT_FILENAME);
        final Typeface italic = Typeface.createFromAsset(getAssets(), DEFAULT_ITALIC_FONT_FILENAME);
        final Typeface boldItalic = Typeface.createFromAsset(getAssets(), DEFAULT_BOLD_ITALIC_FONT_FILENAME);
        final Typeface regular = Typeface.createFromAsset(getAssets(),DEFAULT_NORMAL_FONT_FILENAME);

        Field DEFAULT = Typeface.class.getDeclaredField("DEFAULT");
        DEFAULT.setAccessible(true);
        DEFAULT.set(null, regular);

        Field DEFAULT_BOLD = Typeface.class.getDeclaredField("DEFAULT_BOLD");
        DEFAULT_BOLD.setAccessible(true);
        DEFAULT_BOLD.set(null, bold);

        Field sDefaults = Typeface.class.getDeclaredField("sDefaults");
        sDefaults.setAccessible(true);
        sDefaults.set(null, new Typeface[]{
                regular, bold, italic, boldItalic
        });

    } catch (NoSuchFieldException e) {
        logFontError(e);
    } catch (IllegalAccessException e) {
        logFontError(e);
    } catch (Throwable e) {
        //cannot crash app if there is a failure with overriding the default font!
        logFontError(e);
    }
}

For a more complete example, see http://github.com/perchrh/FontOverrideExample

Samantha answered 29/4, 2013 at 9:28 Comment(11)
This is the best solution for me.Wisp
default doesnt work for me. if i use monospace and then set code<style name="AppTheme" parent="AppBaseTheme"> <item name="android:typeface">monospace</item> </style>code it works but not for bold. i added this code in a class that extends Application. is that the correct place? @P-chanJuror
@ChristopherRivera Yes, add it to your app's Application class, make sure it runs on onCreate. Having a look at grepcode.com/file/repository.grepcode.com/java/ext/… I would suggest you override the additional field for monospace as well as the one in my sample code above!Samantha
Great!! that's working on 4.4 too but it's a solution for who wanna changing the default font of ALL THE APPLICATION not just one view like a Button from the xml filePepsinogen
Overriding DEFAULT typeface didn't work for me. As @ChristopherRivera suggested, I overrode MONOSPACE and it worked.Pomander
This solution works except for the action bar. Any ideas to change that easily?Cryptogenic
Okay, I changed for the field SERIF and it worked :)Cryptogenic
@P-chan thanx for your answer but this is working with only android 4.4 , below 4.4 its crashing app. :( please suggestHexamerous
@SantoshDhoundiyal That is expected, you must catch the exception on older versions, as the new field doesn't exist there. Optionally you can check the device's android-version before you attempt to touch the new field for 4.4.Samantha
1. You are missing a closing curly bracket 2. I love you !Wile
This answer references this answer, and provides a cleaner approach imho.Alevin
O
37

Although I am upvoting Manish's answer as the fastest and most targeted method, I have also seen naive solutions which just recursively iterate through a view hierarchy and update all elements' typefaces in turn. Something like this:

public static void applyFonts(final View v, Typeface fontToSet)
{
    try {
        if (v instanceof ViewGroup) {
            ViewGroup vg = (ViewGroup) v;
            for (int i = 0; i < vg.getChildCount(); i++) {
                View child = vg.getChildAt(i);
                applyFonts(child, fontToSet);
            }
        } else if (v instanceof TextView) {
            ((TextView)v).setTypeface(fontToSet);
        }
    } catch (Exception e) {
        e.printStackTrace();
        // ignore
    }
}

You would need to call this function on your views both after inflating layout and in your Activity's onContentChanged() methods.

Overland answered 28/9, 2011 at 8:29 Comment(1)
Pretty much. At the time, it was the only way of doing it within project timeframes. A handy shortcut if needed (:Overland
R
23

I was able to do this in a centralized way, here is the result:

enter image description here

I have following Activity and I extend from it if I need custom fonts:

import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.util.AttributeSet;
import android.view.LayoutInflater.Factory;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;

public class CustomFontActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
    getLayoutInflater().setFactory(new Factory() {

        @Override
        public View onCreateView(String name, Context context,
                AttributeSet attrs) {
            View v = tryInflate(name, context, attrs);
            if (v instanceof TextView) {
                setTypeFace((TextView) v);
            }
            return v;
        }
    });
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
}

private View tryInflate(String name, Context context, AttributeSet attrs) {
    LayoutInflater li = LayoutInflater.from(context);
    View v = null;
    try {
        v = li.createView(name, null, attrs); 
    } catch (Exception e) {
        try {
            v = li.createView("android.widget." + name, null, attrs);
        } catch (Exception e1) {
        }
    }
    return v;
}

private void setTypeFace(TextView tv) {
    tv.setTypeface(FontUtils.getFonts(this, "MTCORSVA.TTF"));
}
}

But if I am using an activity from support package e.g. FragmentActivity then I use this Activity:

import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;

public class CustomFontFragmentActivity extends FragmentActivity {

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

// we can't setLayout Factory as its already set by FragmentActivity so we
// use this approach
@Override
public View onCreateView(String name, Context context, AttributeSet attrs) {
    View v = super.onCreateView(name, context, attrs);
    if (v == null) {
        v = tryInflate(name, context, attrs);
        if (v instanceof TextView) {
            setTypeFace((TextView) v);
        }
    }
    return v;
}

@TargetApi(Build.VERSION_CODES.HONEYCOMB)
@Override
public View onCreateView(View parent, String name, Context context,
        AttributeSet attrs) {
    View v = super.onCreateView(parent, name, context, attrs);
    if (v == null) {
        v = tryInflate(name, context, attrs);
        if (v instanceof TextView) {
            setTypeFace((TextView) v);
        }
    }
    return v;
}

private View tryInflate(String name, Context context, AttributeSet attrs) {
    LayoutInflater li = LayoutInflater.from(context);
    View v = null;
    try {
        v = li.createView(name, null, attrs);
    } catch (Exception e) {
        try {
            v = li.createView("android.widget." + name, null, attrs);
        } catch (Exception e1) {
        }
    }
    return v;
}

private void setTypeFace(TextView tv) {
    tv.setTypeface(FontUtils.getFonts(this, "MTCORSVA.TTF"));
}
}

I haven't tested this code with Fragments yet, but hopefully it will work.

My FontUtils is simple which also solves the pre-ICS issue mentioned here https://code.google.com/p/android/issues/detail?id=9904:

import java.util.HashMap;
import java.util.Map;

import android.content.Context;
import android.graphics.Typeface;

public class FontUtils {

private static Map<String, Typeface> TYPEFACE = new HashMap<String, Typeface>();

public static Typeface getFonts(Context context, String name) { 
    Typeface typeface = TYPEFACE.get(name);
    if (typeface == null) {
        typeface = Typeface.createFromAsset(context.getAssets(), "fonts/"
                + name);
        TYPEFACE.put(name, typeface);
    }
    return typeface;
}
}
Robb answered 28/11, 2013 at 13:56 Comment(1)
This should get more votes. It works and it has a screenshot for demonstrationThorite
S
10

Hey i also need 2 different fonts in my app for different widgeds! I use this way:

In my Application class i create an static method:

public static Typeface getTypeface(Context context, String typeface) {
    if (mFont == null) {
        mFont = Typeface.createFromAsset(context.getAssets(), typeface);
    }
    return mFont;
}

The String typeface represents the xyz.ttf in the asset folder. (i created an Constants Class) Now you can use this everywhere in your app:

mTextView = (TextView) findViewById(R.id.text_view);
mTextView.setTypeface(MyApplication.getTypeface(this, Constants.TYPEFACE_XY));

The only problem is, you need this for every widget where you want to use the Font! But i think this is the best way.

Sulfathiazole answered 21/9, 2011 at 10:1 Comment(0)
P
4

Using pospi's suggestion and working with the 'tag' property like Richard did, i created a custom class that loads my custom fonts and applies them to the views according to their tags.

So basicly, instead of setting the TypeFace in the attribute android:fontFamily you are using the android:tag attritube and set it to one of the defined enums.

public class Fonts {
    private AssetManager mngr;

    public Fonts(Context context) {
        mngr = context.getAssets();
    }
    private enum AssetTypefaces {
        RobotoLight,
        RobotoThin,
        RobotoCondensedBold,
        RobotoCondensedLight,
        RobotoCondensedRegular
    }

    private Typeface getTypeface(AssetTypefaces font) {
        Typeface tf = null;
        switch (font) {
            case RobotoLight:
                tf = Typeface.createFromAsset(mngr,"fonts/Roboto-Light.ttf");
                break;
            case RobotoThin:
                tf = Typeface.createFromAsset(mngr,"fonts/Roboto-Thin.ttf");
                break;
            case RobotoCondensedBold:
                tf = Typeface.createFromAsset(mngr,"fonts/RobotoCondensed-Bold.ttf");
                break;
            case RobotoCondensedLight:
                tf = Typeface.createFromAsset(mngr,"fonts/RobotoCondensed-Light.ttf");
                break;
            case RobotoCondensedRegular:
                tf = Typeface.createFromAsset(mngr,"fonts/RobotoCondensed-Regular.ttf");
                break;
            default:
                tf = Typeface.DEFAULT;
                break;
        }
        return tf;
    }
    public void setupLayoutTypefaces(View v) {
        try {
            if (v instanceof ViewGroup) {
                ViewGroup vg = (ViewGroup) v;
                for (int i = 0; i < vg.getChildCount(); i++) {
                    View child = vg.getChildAt(i);
                    setupLayoutTypefaces(child);
                }
            } else if (v instanceof TextView) {
                if (v.getTag().toString().equals(AssetTypefaces.RobotoLight.toString())){
                    ((TextView)v).setTypeface(getTypeface(AssetTypefaces.RobotoLight));
                }else if (v.getTag().toString().equals(AssetTypefaces.RobotoCondensedRegular.toString())) {
                    ((TextView)v).setTypeface(getTypeface(AssetTypefaces.RobotoCondensedRegular));
                }else if (v.getTag().toString().equals(AssetTypefaces.RobotoCondensedBold.toString())) {
                    ((TextView)v).setTypeface(getTypeface(AssetTypefaces.RobotoCondensedBold));
                }else if (v.getTag().toString().equals(AssetTypefaces.RobotoCondensedLight.toString())) {
                    ((TextView)v).setTypeface(getTypeface(AssetTypefaces.RobotoCondensedLight));
                }else if (v.getTag().toString().equals(AssetTypefaces.RobotoThin.toString())) {
                    ((TextView)v).setTypeface(getTypeface(AssetTypefaces.RobotoThin));
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            // ignore
        }
    }
}

In your Activity or Fragment you just call

Fonts fonts = new Fonts(getActivity());
fonts.setupLayoutTypefaces(mainLayout);
Pareu answered 12/7, 2013 at 12:44 Comment(0)
E
4

I found a nice solution on the blog of Lisa Wray. With the new data binding it is possible to set the font in your XML files.

@BindingAdapter({"bind:font"})
public static void setFont(TextView textView, String fontName){
    textView.setTypeface(Typeface.createFromAsset(textView.getContext().getAssets(), "fonts/" + fontName));
}

In XML:

<TextView
app:font="@{`Source-Sans-Pro-Regular.ttf`}"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"/>
Extensible answered 7/9, 2015 at 5:31 Comment(1)
Can you suggest an example using the Data Binding ? That would be much appreciated.Glycine
D
3

I think there can be a handier way to do it. The following class will set a custom type face for all your the components of your application (with a setting per class).

/**
 * Base Activity of our app hierarchy.
 * @author SNI
 */
public class BaseActivity extends Activity {

    private static final String FONT_LOG_CAT_TAG = "FONT";
    private static final boolean ENABLE_FONT_LOGGING = false;

    private Typeface helloTypeface;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        helloTypeface = Typeface.createFromAsset(getAssets(), "fonts/<your type face in assets/fonts folder>.ttf");
    }

    @Override
    public View onCreateView(String name, Context context, AttributeSet attrs) {
        View view = super.onCreateView(name, context, attrs);
        return setCustomTypeFaceIfNeeded(name, attrs, view);
    }

    @Override
    public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
        View view = super.onCreateView(parent, name, context, attrs);
        return setCustomTypeFaceIfNeeded(name, attrs, view);
    }

    protected View setCustomTypeFaceIfNeeded(String name, AttributeSet attrs, View view) {
        View result = null;
        if ("TextView".equals(name)) {
            result = new TextView(this, attrs);
            ((TextView) result).setTypeface(helloTypeface);
        }

        if ("EditText".equals(name)) {
            result = new EditText(this, attrs);
            ((EditText) result).setTypeface(helloTypeface);
        }

        if ("Button".equals(name)) {
            result = new Button(this, attrs);
            ((Button) result).setTypeface(helloTypeface);
        }

        if (result == null) {
            return view;
        } else {
            if (ENABLE_FONT_LOGGING) {
                Log.v(FONT_LOG_CAT_TAG, "A type face was set on " + result.getId());
            }
            return result;
        }
    }

}
Disclimax answered 28/1, 2013 at 8:24 Comment(0)
J
2

The default implementations of LayoutInflater do not support specifying the font typeface from xml. I have however seen it done in xml by providing a custom factory for the LayoutInflater that will parse such attributes from the xml tag.

The basic structure would like this.

public class TypefaceInflaterFactory implements LayoutInflater.Factory {

    @Override
    public View onCreateView(String name, Context context, AttributeSet attrs) {
        // CUSTOM CODE TO CREATE VIEW WITH TYPEFACE HERE
        // RETURNING NULL HERE WILL TELL THE INFLATER TO USE THE
        // DEFAULT MECHANISMS FOR INFLATING THE VIEW FROM THE XML
    }

}

public class BaseActivity extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        LayoutInflater.from(this).setFactory(new TypefaceInflaterFactory());
    }
}

This article provides a more in depth explanation of these mechanisms and how the author attempts to provide xml layout support for typefaces in this way. The code for the author's implementation can be found here.

Jit answered 4/4, 2013 at 19:59 Comment(0)
P
1

Setting a custom font to a regular ProgressDialog/AlertDialog:

font=Typeface.createFromAsset(getAssets(),"DroidSans.ttf");

ProgressDialog dialog = ProgressDialog.show(this, "titleText", "messageText", true);
((TextView)dialog.findViewById(Resources.getSystem().getIdentifier("message", "id", "android"))).setTypeface(font);
((TextView)dialog.findViewById(Resources.getSystem().getIdentifier("alertTitle", "id", "android"))).setTypeface(font);
Preferable answered 26/1, 2012 at 12:36 Comment(0)
G
1

Yes it is possible by overriding the default typeface. I followed this solution and it worked like a charm for all TextViews and ActionBar text too with a single change.

public class MyApp extends Application {

  @Override
  public void onCreate() {
    TypefaceUtil.overrideFont(getApplicationContext(), "SERIF", "fonts/Roboto-Regular.ttf"); // font from assets: "assets/fonts/Roboto-Regular.ttf
  }
}

styles.xml

<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
    <!-- Customize your theme here. -->
    <item name="colorPrimary">@color/pantone</item>
    <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
    <item name="colorAccent">@color/colorAccent</item>
    <item name="android:windowTranslucentStatus" tools:targetApi="kitkat">true</item>
    <item name="android:windowDisablePreview">true</item>
    <item name="android:typeface">serif</item>
</style>

Instead of themes.xml as mentioned in the above link, I mentioned the default font to override in my styles.xml in my default app theme tag. The default typefaces that can be overwritten are serif, sans, monospace and normal.

TypefaceUtil.java

public class TypefaceUtil {

    /**
     * Using reflection to override default typeface
     * NOTICE: DO NOT FORGET TO SET TYPEFACE FOR APP THEME AS DEFAULT TYPEFACE WHICH WILL BE OVERRIDDEN
     * @param context to work with assets
     * @param defaultFontNameToOverride for example "monospace"
     * @param customFontFileNameInAssets file name of the font from assets
     */
    public static void overrideFont(Context context, String defaultFontNameToOverride, String customFontFileNameInAssets) {
        try {
            final Typeface customFontTypeface = Typeface.createFromAsset(context.getAssets(), customFontFileNameInAssets);

            final Field defaultFontTypefaceField = Typeface.class.getDeclaredField(defaultFontNameToOverride);
            defaultFontTypefaceField.setAccessible(true);
            defaultFontTypefaceField.set(null, customFontTypeface);
        } catch (Exception e) {
            Log.e("Can not set custom font " + customFontFileNameInAssets + " instead of " + defaultFontNameToOverride);
        }
    }
}

Initially, I didnt know the typefaces to be overwritten are fixed and set of defined values but eventually it helped me understand how Android deals with fonts and typefaces and their default values, which is a different point ofcourse.

Geniegenii answered 18/7, 2017 at 13:15 Comment(0)
D
1

I use DataBinding for this (with Kotlin).


Set up BindingAdapter:

BindingAdapter.kt

import android.graphics.Typeface
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.databinding.BindingAdapter
import java.util.*


object BindingAdapters {


    @JvmStatic
    @BindingAdapter("typeface", "typefaceStyle")
    fun setTypeface(v: TextView, tf: Typeface?, style: Int?) {
        v.setTypeface(tf ?: Typeface.DEFAULT, style ?: Typeface.NORMAL)
    }
}

Usage:

fragment_custom_view.xml

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

    <data>

        <import type="android.graphics.Typeface" />

        <variable
            name="typeface"
            type="android.graphics.Typeface" />

    </data>


    <TextView
        android:id="@+id/reference"
        style="@style/TextAppearance.MaterialComponents.Body1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="I'm formatted text"
        app:typeface="@{typeface}"
        app:typefaceStyle="@{Typeface.ITALIC}" />

</layout>

MyFragment.kt

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        binding = FragmentCustomView.bind(view)
        binding.typeface = // some code to get user selected typeface

}

Now, if the user selects a new typeface you can just update the binding value and all your TextViews that you've set app:typeface will get updated.

Dunford answered 18/9, 2020 at 0:34 Comment(0)
C
0

I don't know if it changes the whole app, but I have managed to change some components that couldn't otherwise be changed by doing this:

Typeface tf = Typeface.createFromAsset(getAssets(), "fonts/Lucida Sans Unicode.ttf");
Typeface.class.getField("DEFAULT").setAccessible(true);
Typeface.class.getField("DEFAULT_BOLD").setAccessible(true);
Typeface.class.getField("DEFAULT").set(null, tf);
Typeface.class.getField("DEFAULT_BOLD").set(null, tf);
Conjunctive answered 17/2, 2011 at 11:52 Comment(1)
Unfortunately this doesn't work any more: java.lang.IllegalAccessException: field is marked 'final' at java.lang.reflect.Field.setField(Native Method) at java.lang.reflect.Field.set(Field.java:556)Iey
C
0

I like pospi's suggestion. Why not go all-out any use the 'tag' property of a view (which you can specify in XML - 'android:tag') to specify any additional styling that you can't do in XML. I like JSON so I'd use a JSON string to specify a key/value set. This class does the work - just call Style.setContentView(this, [resource id]) in your activity.

public class Style {

  /**
   * Style a single view.
   */
  public static void apply(View v) {
    if (v.getTag() != null) {
      try {
        JSONObject json = new JSONObject((String)v.getTag());
        if (json.has("typeface") && v instanceof TextView) {
          ((TextView)v).setTypeface(Typeface.createFromAsset(v.getContext().getAssets(),
                                                             json.getString("typeface")));
        }
      }
      catch (JSONException e) {
        // Some views have a tag without it being explicitly set!
      }
    }
  }

  /**
   * Style the passed view hierarchy.
   */
  public static View applyTree(View v) {
    apply(v);
    if (v instanceof ViewGroup) {
      ViewGroup g = (ViewGroup)v;
      for (int i = 0; i < g.getChildCount(); i++) {
        applyTree(g.getChildAt(i));
      }
    }
    return v;
  }

  /**
   * Inflate, style, and set the content view for the passed activity.
   */
  public static void setContentView(Activity activity, int resource) {
    activity.setContentView(applyTree(activity.getLayoutInflater().inflate(resource, null)));
  }
}

Obviously you'd want to handle more than just the typeface to make using JSON worthwhile.

A benefit of the 'tag' property is that you can set it on a base style which you use as a theme and thus have it apply to all of your views automatically. EDIT: Doing this results in a crash during inflation on Android 4.0.3. You can still use a style and apply it to text views individually.

One thing you'll see in the code - some views have a tag without one being explicitly set - bizarrely it's the string 'Αποκοπή' - which is 'cut' in greek, according to google translate! What the hell...?

Conjunctive answered 20/6, 2012 at 19:18 Comment(0)
C
0

@majinboo's answer is revised for performance and memory management. Any more than one font need related Activity can use this Font class by giving the constructor itself as a parameter.

@Override
public void onCreate(Bundle savedInstanceState)
{
    Font font = new Font(this);
}

Revised Fonts class is as below:

public class Fonts
{
    private HashMap<AssetTypefaces, Typeface> hashMapFonts;

    private enum AssetTypefaces
    {
        RobotoLight,
        RobotoThin,
        RobotoCondensedBold,
        RobotoCondensedLight,
        RobotoCondensedRegular
    }

    public Fonts(Context context)
    {
        AssetManager mngr = context.getAssets();

        hashMapFonts = new HashMap<AssetTypefaces, Typeface>();
        hashMapFonts.put(AssetTypefaces.RobotoLight, Typeface.createFromAsset(mngr, "fonts/Roboto-Light.ttf"));
        hashMapFonts.put(AssetTypefaces.RobotoThin, Typeface.createFromAsset(mngr, "fonts/Roboto-Thin.ttf"));
        hashMapFonts.put(AssetTypefaces.RobotoCondensedBold, Typeface.createFromAsset(mngr, "fonts/RobotoCondensed-Bold.ttf"));
        hashMapFonts.put(AssetTypefaces.RobotoCondensedLight, Typeface.createFromAsset(mngr, "fonts/RobotoCondensed-Light.ttf"));
        hashMapFonts.put(AssetTypefaces.RobotoCondensedRegular, Typeface.createFromAsset(mngr, "fonts/RobotoCondensed-Regular.ttf"));
    }

    private Typeface getTypeface(String fontName)
    {
        try
        {
            AssetTypefaces typeface = AssetTypefaces.valueOf(fontName);
            return hashMapFonts.get(typeface);
        }
        catch (IllegalArgumentException e)
        {
            // e.printStackTrace();
            return Typeface.DEFAULT;
        }
    }

    public void setupLayoutTypefaces(View v)
    {
        try
        {
            if (v instanceof ViewGroup)
            {
                ViewGroup vg = (ViewGroup) v;
                for (int i = 0; i < vg.getChildCount(); i++)
                {
                    View child = vg.getChildAt(i);
                    setupLayoutTypefaces(child);
                }
            }
            else if (v instanceof TextView)
            {
                ((TextView) v).setTypeface(getTypeface(v.getTag().toString()));
            }
        }
        catch (Exception e)
        {
            e.printStackTrace();
            // ignore
        }
    }
}
Cultigen answered 19/10, 2014 at 17:11 Comment(0)
A
0

Working for Xamarin.Android:

Class:

public class FontsOverride
{
    public static void SetDefaultFont(Context context, string staticTypefaceFieldName, string fontAssetName)
    {
        Typeface regular = Typeface.CreateFromAsset(context.Assets, fontAssetName);
        ReplaceFont(staticTypefaceFieldName, regular);
    }

    protected static void ReplaceFont(string staticTypefaceFieldName, Typeface newTypeface)
    {
        try
        {
            Field staticField = ((Java.Lang.Object)(newTypeface)).Class.GetDeclaredField(staticTypefaceFieldName);
            staticField.Accessible = true;
            staticField.Set(null, newTypeface);
        }
        catch (Exception e)
        {
            Console.WriteLine(e.Message);
        }
    }
}

Application Implementation:

namespace SomeAndroidApplication
{
    [Application]
    public class App : Application
    {
        public App()
        {

        }

        public App(IntPtr handle, JniHandleOwnership transfer)
            : base(handle, transfer)
        {

        }

        public override void OnCreate()
        {
            base.OnCreate();

            FontsOverride.SetDefaultFont(this, "MONOSPACE", "fonts/Roboto-Light.ttf");
        }
    }
}

Style:

<style name="Theme.Storehouse" parent="Theme.Sherlock">
    <item name="android:typeface">monospace</item>
</style>
Anurag answered 10/3, 2015 at 3:27 Comment(0)
G
0

It looks like using custom fonts has been made easy with Android O, you can basically use xml to achieve this. I have attached a link to Android official documentation for reference, and hopefully this will help people who still need this solution. Working with custom fonts in Android

Goodrum answered 28/6, 2017 at 20:45 Comment(0)
E
0

It may be useful to know that starting from Android 8.0 (API level 26) you can use a custom font in XML.

Simply put, you can do it in the following way.

  1. Put the font in the folder res/font.

  2. Either use it in the attribute of a widget

<Button android:fontFamily="@font/myfont"/>

or put it in res/values/styles.xml

<style name="MyButton" parent="android:Widget.Button">
    <item name="android:fontFamily">@font/myfont</item>
</style>

and use it as a style

<Button style="@style/MyButton"/>
End answered 9/2, 2019 at 8:34 Comment(0)
G
0

Use "fontPath" attribute directly in xml file.

for use in style.xml

<item name="fontPath">fonts/ProximaNovaSemibold.ttf</item>

for use in direct layout file

fontPath="fonts/ProximaNovaBold.ttf"

(Note : No need to use app/android attribute in prefix)

Grimbald answered 19/10, 2019 at 6:44 Comment(0)
L
-6

Absolutely possible. Many ways to do it. The fastest way, create condition with try - catch method.. try your certain font style condition, catch the error, and define the other font style.

Lasky answered 12/10, 2012 at 9:4 Comment(1)
can you provide a demo to prove you answer?Ophir

© 2022 - 2024 — McMap. All rights reserved.