Is it possible to set a custom font for entire of application?
Asked Answered
P

25

275

I need to use certain font for my entire application. I have .ttf file for the same. Is it possible to set this as default font, at application start up and then use it elsewhere in the application? When set, how do I use it in my layout XMLs?

Pallette answered 26/4, 2010 at 7:58 Comment(3)
in android studio 3.0 you can easily set custom fonts : developer.android.com/guide/topics/ui/look-and-feel/…Astrakhan
@MHSFisher Fonts in XML is a feature for Android 8.0 (API level 26)Discriminator
@EmiRaz please read the doc developer.android.com/guide/topics/ui/look-and-feel/…. this feature added in support library 26, but support from API 16.Astrakhan
F
454

Yes with reflection. This works (based on this answer):

(Note: this is a workaround due to lack of support for custom fonts, so if you want to change this situation please do star to up-vote the android issue here). Note: Do not leave "me too" comments on that issue, everyone who has stared it gets an email when you do that. So just "star" it please.

import java.lang.reflect.Field;
import android.content.Context;
import android.graphics.Typeface;

public final class FontsOverride {

    public static void setDefaultFont(Context context,
            String staticTypefaceFieldName, String fontAssetName) {
        final Typeface regular = Typeface.createFromAsset(context.getAssets(),
                fontAssetName);
        replaceFont(staticTypefaceFieldName, regular);
    }

    protected static void replaceFont(String staticTypefaceFieldName,
            final Typeface newTypeface) {
        try {
            final Field staticField = Typeface.class
                    .getDeclaredField(staticTypefaceFieldName);
            staticField.setAccessible(true);
            staticField.set(null, newTypeface);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}

You then need to overload the few default fonts, for example in an application class:

public final class Application extends android.app.Application {
    @Override
    public void onCreate() {
        super.onCreate();
        FontsOverride.setDefaultFont(this, "DEFAULT", "MyFontAsset.ttf");
        FontsOverride.setDefaultFont(this, "MONOSPACE", "MyFontAsset2.ttf");
        FontsOverride.setDefaultFont(this, "SERIF", "MyFontAsset3.ttf");
        FontsOverride.setDefaultFont(this, "SANS_SERIF", "MyFontAsset4.ttf");
    }
}

Or course if you are using the same font file, you can improve on this to load it just once.

However I tend to just override one, say "MONOSPACE", then set up a style to force that font typeface application wide:

<resources>
    <style name="AppBaseTheme" parent="android:Theme.Light">
    </style>

    <!-- Application theme. -->
    <style name="AppTheme" parent="AppBaseTheme">
        <item name="android:typeface">monospace</item>
    </style>
</resources>

API 21 Android 5.0

I've investigated the reports in the comments that it doesn't work and it appears to be incompatible with the theme android:Theme.Material.Light.

If that theme is not important to you, use an older theme, e.g.:

<style name="AppTheme" parent="android:Theme.Holo.Light.DarkActionBar">
    <item name="android:typeface">monospace</item>
</style>
Fremont answered 26/4, 2010 at 7:58 Comment(47)
This should be the accepted answer. It's an A-class hack but it just works.Delagarza
Very clever solution! Runs the risk of the Typeface names changing or being moved to a new classes, but my guess is they've been what and where they are since Android 1.5 so it's a reasonably-safe hack. You could cache the fields by name for better performance (I'm thinking in the case where an app has themes and allows fonts to be changed out with live/ instant feedback).Valerle
how would you override monospace bold or italic?Catfish
@ChristopherRivera There is "DEFAULT_BOLD" but also a private field : private static Typeface[] sDefaults; You could try accessing this by reflection and setting sDefaults[Typeface.ITALIC] to yourTypeface. See this.Fremont
Just in case you need a sample for Xamarin: IntPtr cls = JNIEnv.FindClass("android/graphics/Typeface"); IntPtr fld = JNIEnv.GetStaticFieldID(cls, staticTypefaceFieldName, "Landroid/graphics/Typeface;"); JNIEnv.SetStaticField(cls, fld, newTypeface.Handle); Although all these fields are available now as Typeface static properties.Opsonize
This should be made much more convenient in AndroidIrrefrangible
This solution works except for action bar. Any ideas to change that easily?Aplomb
Okay, I changed for the field SERIF and it worked :)Aplomb
But how do i get it to change the font for spinners and the action bar?Pubescent
@Pubescent Someone above had success by setting the field SERIFFremont
I tried that but its still the same. im putting the FontsOverride.setDefaultFont in the mainactivity.java file. is it suppose to be somewhere else for the actionbar?Pubescent
@Pubescent not 100% sure, I think that would be ok. Needs to be first line in on create. But I usually put in an application class.Fremont
my mainactivity extends ActionBarActivity the FontsOverride.setDefaultFont is the only thing in my onCreate since im using fragments.Pubescent
@Pubescent Sorry I don't know then. Like I said I put in application class, plus that way the font only gets loaded once.Fremont
Cool, thanks for solution, but In case of DEFAULT it is not working. I even tried setting typeface="normal" but no luckSwiercz
Same here - used to work but on Lollipop (Vanilla) device it doesn't work anymore...Luetic
Hi weston, I tried this solution as suggested in resources tag. I have put mycustomfonts.ttf file on assets/fonts folder. If i put monospace it works fine. But if put mycustom fonts, it is not working. Application gives me error. Any suggestion? Thanks.Diazine
@JayPandya Ask a new question. It's not practical to do this in comments. But note this reportedly doesn't work in lollypop.Fremont
@Fremont Never mind. I have achieved it. There was a mistake in my understanding. You have done a great job. Can you just tell me one thing? Why it is not working for DEFAULT Typeface? I have changed Typeface to MONOSPACE as your suggestion and then apply the loginc, it worked. But not working for DEFAULTDiazine
@Fremont in which class do i write this code. FontsOverride.setDefaultFont(this, "DEFAULT", "MyFontAsset.ttf");Microfarad
@SagarDevanga I usually put that in an application class.Fremont
Native typeface cannot be made error. Fixed, by adding fonts path. "FontsOverride.setDefaultFont(this, "DEFAULT", "fonts/MyFontAsset.ttf"); "Edger
Did i find solution how to change fonts on api 21?Khichabia
@Tooto, I don't know, did you? My solution for API 21 is in the answer.Fremont
@Fremont nope. I cant find any other solutions. But ur vaviant is not an option to meKhichabia
@Khichabia sorry I've not had time to investigate and my solution works for my needs right now.Fremont
I can confirm that on lollipop devices it also does not work with Theme.AppCompat.Light derived themes. Granted it does work 4.x devices.Hexahedron
for me is not working in Android 5, tested in 4.4 and in others before it and works like a charm. Someone knows how to fix it to work in Lolipop?Nephology
@Nephology read the bit about Android 5. If that's not suitable, you might be better off asking another question, because not many people will see your comment.Fremont
+1000 - I used AppCompatTheme and it is compatible with it... THANK YOUGodber
I find it really annoying to have to swap out the default TextView everywhere to use custom fonts or use a reflection based solution like this. Combined with the limiting set of fonts available in android, it makes developing good looking apps a lot of hard work. I've added a feature request to the android issue tracker. If you'd like to see this feature, you can star the issue here: code.google.com/p/android/issues/…Waldrop
why native typeface cannot be made exeption?Ottavia
@Ottavia not sure what you are askingFremont
@Waldrop thanks, I've put that at the top of the question to attract more votes to the issue.Fremont
how can we revert back to default. I am doing something like this: pastebin.com/qJqbEBqEOverscore
Note that you may run into some memory corruption bugs as a result of this -- but it would only show up as a native crash in a JNI library. From my testing, I found that the problem is that the old Typeface instance that you are replacing here ends up getting GC, which calls the finalizer. When it's finalized, the native pointer to the Typeface gets cleaned up as well, but the native Typeface may still be in use, resulting in a corrupted pointer somewhere. I was able to fix that by keeping the references to the old/replaced Typeface instances in a static collection.Nigel
is there a way to do this without getting a copy of DroidSans.ttf (the font i want to use) so we don't have to deal with copyright license? shouldn't DroidSans.ttf be available directly with the Android sdk? This is the default android font.Sidney
How to change the BOLD Text??Handsome
@ChristopherRivera I tried using DEFAULT_BOLD but it does't change the bold text font can you help me?Handsome
HURRAH! android O just released with custom font support. My ticket is finally closed. See here for details: developer.android.com/preview/features/working-with-fonts.htmlWaldrop
(also added my own answer covering this method here: https://mcmap.net/q/56656/-is-it-possible-to-set-a-custom-font-for-entire-of-application)Waldrop
that's great, so many upvotes, but where do I put my *.ttf file?Profile
@MarekCzaplicki in the assets folder. I hope I see one more upvote :pFremont
yeah, you got it. I was too lazy back then to check the implementation of your class. However it doesn't seem to work for me. I have this in AndroidManifest/application android:theme="@style/AppTheme" this in Application: FontsOverride.setDefaultFont(this, "MONOSPACE", "fonts/Roboto-Regular.ttf"); and this in styles.xml <item name="android:typeface">monospace</item>Profile
@MarekCzaplicki Android 5 or greater? Read that bit of the answer if so. And are you sure you're going to actually notice if yours works as Roboto is already an built in font. In fact that's very odd, what you're trying to achieve?Fremont
oh, so the default font is Roboto? :D I'm so confusedProfile
@MarekCzaplicki what are you trying to achieve? why don't you ask a new question. Comments are not the best place for this.Fremont
E
65

There is a great library for custom fonts in android:Calligraphy

here is a sample how to use it.

in Gradle you need to put this line into your app's build.gradle file:

dependencies {
    compile 'uk.co.chrisjenx:calligraphy:2.2.0'
}

and then make a class that extends Application and write this code:

public class App extends Application {
    @Override
    public void onCreate() {
        super.onCreate();

        CalligraphyConfig.initDefault(new CalligraphyConfig.Builder()
                        .setDefaultFontPath("your font path")
                        .setFontAttrId(R.attr.fontPath)
                        .build()
        );
    }
} 

and in the activity class put this method before onCreate:

@Override
protected void attachBaseContext(Context newBase) {
    super.attachBaseContext(CalligraphyContextWrapper.wrap(newBase));
}

and the last thing your manifest file should look like this:

<application
   .
   .
   .
   android:name=".App">

and it will change the whole activity to your font! it's simple and clean!

Eruptive answered 2/12, 2015 at 18:8 Comment(9)
This is the perfect solution.Stupefy
True, its very perfect solution, this way i do not have to write different classes to update fonts for menu items , radio button or checkbox fotns. It applies for all !! thanks :)Diophantus
Definitely not a perfect solution: Bold / italic / bold-italic styles are overwritten. There is currently no easy way to set those families.Bogoch
If I do that How can I switch from Bold to Regular?Antihero
To get bold, italic or underline feature, I've used <b></b>, <u></u> and <i></i> in the strings.xml file and so far it works.Mulkey
This is the best solution till nowMorea
works like a charm. Just add an activity as my base activity and that's allTremolant
This is outdated. The new Calligraphy is here: github.com/InflationX/CalligraphyStream
I had an issue with changing the font of bottom navigation and couldn't fix it by calligraphy. Does anyone has solved this issue? If yes, how, please?Discriminator
V
47

While this would not work for an entire application, it would work for an Activity and could be re-used for any other Activity. I've updated my code thanks to @FR073N to support other Views. I'm not sure about issues with Buttons, RadioGroups, etc. because those classes all extend TextView so they should work just fine. I added a boolean conditional for using reflection because it seems very hackish and might notably compromise performance.

Note: as pointed out, this will not work for dynamic content! For that, it's possible to call this method with say an onCreateView or getView method, but requires additional effort.

/**
 * Recursively sets a {@link Typeface} to all
 * {@link TextView}s in a {@link ViewGroup}.
 */
public static final void setAppFont(ViewGroup mContainer, Typeface mFont, boolean reflect)
{
    if (mContainer == null || mFont == null) return;

    final int mCount = mContainer.getChildCount();

    // Loop through all of the children.
    for (int i = 0; i < mCount; ++i)
    {
        final View mChild = mContainer.getChildAt(i);
        if (mChild instanceof TextView)
        {
            // Set the font if it is a TextView.
            ((TextView) mChild).setTypeface(mFont);
        }
        else if (mChild instanceof ViewGroup)
        {
            // Recursively attempt another ViewGroup.
            setAppFont((ViewGroup) mChild, mFont);
        }
        else if (reflect)
        {
            try {
                Method mSetTypeface = mChild.getClass().getMethod("setTypeface", Typeface.class);
                mSetTypeface.invoke(mChild, mFont); 
            } catch (Exception e) { /* Do something... */ }
        }
    }
}

Then to use it you would do something like this:

final Typeface mFont = Typeface.createFromAsset(getAssets(),
"fonts/MyFont.ttf"); 
final ViewGroup mContainer = (ViewGroup) findViewById(
android.R.id.content).getRootView();
HomeActivity.setAppFont(mContainer, mFont);

Hope that helps.

Valerle answered 13/1, 2012 at 17:38 Comment(14)
I think the best way would be to override textview. This method could get somewhat redundant if you have an application with a lot of views.Ediva
It could... but according to the Android specs you want to avoid layouts deeper than 4 levels and 100 views wide (even that's quite a bit). Recursion can be bad in terms of performance, but even if your layout was 20 levels that's not even significant.Valerle
I would agree that it doesn't have a noticeable impact on perfomance and I am not trying to say your answer is wrong. I just don't like cycling through every view in the layout if I don't need to. Also, by extending TextView, if you want to modify the appearance in another way you would only have to change the code in one spot.Ediva
if someone does use your solution they should put your code in an abstract class that extends Activity.Ediva
I use the extend TextView myself and agrees that its a good solution, but if I hadn't already done so its such a pain to edit all of your XML layouts and code to point to your custom TextView. The abstract Activity works, or just leaving the method static and referencing it is what I do in some situations.Valerle
Just wanted to add that above code just covers TextViews but ListViews, Alerts, Toasts, Map Markers etc. will still use the system font.Algia
Also, this will not work on dynamically created content, like lists. In such a case, you need to manually set the typeface for each dynamically created view (that shows text).Canine
What if we had used fragment !Beiderbecke
@NiravDangi What about Fragments? All Views get added to a single hierarchy, so this creates a problem if you're attaching & deattaching views, but you could still call this method onWindowAttach or within an onCreateView if necessary. Maybe create a BaseFragment class that does this for you!Valerle
@Valerle I am calling from onCreateView & getting error at findViewById( android.R.id.content).getRootView();Beiderbecke
@NiravDangi I should have clarified, call this function from onCreateView but referencing your layout! Of course their is no root content because onCreateView has to return before it's added to the Window.Valerle
Thank you for the code, but font doesn't change on ListView and Buttons, either on the Menu.Homesick
guys this function takes 3 parameters, when you call it recursively you passed 2 parameters ??? which is the correct one ? what is reflect??Godber
I have problem on setAppFont((ViewGroup) mChild, mFont); It says "Create method setAppFont or remove 3rd parameter form setAppFont"Suavity
L
34

In summary:

Option#1: Use reflection to apply font (combining weston & Roger Huang's answer):

import java.lang.reflect.Field;
import android.content.Context;
import android.graphics.Typeface;

public final class FontsOverride { 

    public static void setDefaultFont(Context context,
            String staticTypefaceFieldName, String fontAssetName) {
        final Typeface regular = Typeface.createFromAsset(context.getAssets(),
                fontAssetName);
        replaceFont(staticTypefaceFieldName, regular);
    } 

    protected static void replaceFont(String staticTypefaceFieldName,final Typeface newTypeface) {
        if (isVersionGreaterOrEqualToLollipop()) {
            Map<String, Typeface> newMap = new HashMap<String, Typeface>();
            newMap.put("sans-serif", newTypeface);
            try {
                final Field staticField = Typeface.class.getDeclaredField("sSystemFontMap");
                staticField.setAccessible(true);
                staticField.set(null, newMap);
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        } else {
            try {
                final Field staticField = Typeface.class.getDeclaredField(staticTypefaceFieldName);
                staticField.setAccessible(true);
                staticField.set(null, newTypeface);
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } 
        }
    }

} 

Usage in Application class:

public final class Application extends android.app.Application {
    @Override 
    public void onCreate() { 
        super.onCreate(); 
        FontsOverride.setDefaultFont(this, "DEFAULT", "MyFontAsset.ttf");
        FontsOverride.setDefaultFont(this, "MONOSPACE", "MyFontAsset2.ttf");
        FontsOverride.setDefaultFont(this, "SERIF", "MyFontAsset3.ttf");
        FontsOverride.setDefaultFont(this, "SANS_SERIF", "MyFontAsset4.ttf");
    } 
} 

set up a style to force that font typeface application wide (based on lovefish):

Pre-Lollipop:

<resources>
    <style name="AppBaseTheme" parent="Theme.AppCompat.Light">
    </style>

   <!-- Application theme. -->
   <style name="AppTheme" parent="AppBaseTheme">
       <item name="android:typeface">monospace</item>
   </style>
</resources>

Lollipop (API 21):

<resources>
    <style name="AppBaseTheme" parent="Theme.AppCompat.Light">
    </style>

   <!-- Application theme. -->
   <style name="AppTheme" parent="AppBaseTheme">
       <item name="android:textAppearance">@style/CustomTextAppearance</item>
   </style>

   <style name="CustomTextAppearance">
       <item name="android:typeface">monospace</item>
   </style>
</resources>

Option2: Subclass each and every View where you need to customize font, ie. ListView, EditTextView, Button, etc. (Palani's answer):

public class CustomFontView extends TextView {

public CustomFontView(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
    init(); 
} 

public CustomFontView(Context context, AttributeSet attrs) {
    super(context, attrs);
    init(); 
} 

public CustomFontView(Context context) {
    super(context);
    init(); 
} 

private void init() { 
    if (!isInEditMode()) {
        Typeface tf = Typeface.createFromAsset(getContext().getAssets(), "Futura.ttf");
        setTypeface(tf);
    } 
} 

Option 3: Implement a View Crawler that traverses through the view hierarchy of your current screen:

Variation#1 (Tom's answer):

public static final void setAppFont(ViewGroup mContainer, Typeface mFont, boolean reflect)
{ 
    if (mContainer == null || mFont == null) return;

    final int mCount = mContainer.getChildCount();

    // Loop through all of the children. 
    for (int i = 0; i < mCount; ++i)
    { 
        final View mChild = mContainer.getChildAt(i);
        if (mChild instanceof TextView)
        { 
            // Set the font if it is a TextView. 
            ((TextView) mChild).setTypeface(mFont);
        } 
        else if (mChild instanceof ViewGroup)
        { 
            // Recursively attempt another ViewGroup. 
            setAppFont((ViewGroup) mChild, mFont);
        } 
        else if (reflect)
        { 
            try { 
                Method mSetTypeface = mChild.getClass().getMethod("setTypeface", Typeface.class);
                mSetTypeface.invoke(mChild, mFont); 
            } catch (Exception e) { /* Do something... */ }
        } 
    } 
} 

Usage :

final ViewGroup mContainer = (ViewGroup) findViewById(
android.R.id.content).getRootView();
HomeActivity.setAppFont(mContainer, Typeface.createFromAsset(getAssets(),
"fonts/MyFont.ttf"));

Variation#2: https://coderwall.com/p/qxxmaa/android-use-a-custom-font-everywhere.

Option #4: Use 3rd Party Lib called Calligraphy.

Personally, I would recommend Option#4, as it saves a lot of headaches.

Luminary answered 24/3, 2016 at 17:21 Comment(2)
In Option#1, what do you mean by "sans-serif" in newMap.put(, and monospace in styles ?! I want to use a custom font named barana.ttf.Lacylad
in option 1, the first code snippet does not work with API level 22 , Android 5.1.1 . But the other code section does. What should be the exact if condition so ?Murphree
W
28

I would like to improve weston's answer for API 21 Android 5.0.

Cause

Under API 21, most of the text styles include fontFamily setting, like:

<style name="TextAppearance.Material">
     <item name="fontFamily">@string/font_family_body_1_material</item>
</style>

Which applys the default Roboto Regular font:

<string name="font_family_body_1_material">sans-serif</string>

The original answer fails to apply monospace font, because android:fontFamily has greater priority to android:typeface attribute (reference). Using Theme.Holo.* is a valid workaround, because there is no android:fontFamily settings inside.

Solution

Since Android 5.0 put system typeface in static variable Typeface.sSystemFontMap (reference), we can use the same reflection technique to replace it:

protected static void replaceFont(String staticTypefaceFieldName,
        final Typeface newTypeface) {
    if (isVersionGreaterOrEqualToLollipop()) {
        Map<String, Typeface> newMap = new HashMap<String, Typeface>();
        newMap.put("sans-serif", newTypeface);
        try {
            final Field staticField = Typeface.class
                    .getDeclaredField("sSystemFontMap");
            staticField.setAccessible(true);
            staticField.set(null, newMap);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    } else {
        try {
            final Field staticField = Typeface.class
                    .getDeclaredField(staticTypefaceFieldName);
            staticField.setAccessible(true);
            staticField.set(null, newTypeface);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}
Wanitawanneeickel answered 6/3, 2015 at 14:21 Comment(6)
nice workaround for L. however, I'm wondering why this doesn't seem to work for my Buttons and Tool Bar titles. I'm overriding the default font in MainApplication as follows: FontsOverride.setDefaultFont(this, "DEFAULT", "MyFontAsset.ttf");. I'm using Nexus 5, v5.1.1Nutbrown
Hey , Thank you This was very helpful, just one thing: this not work at Android 6 (API 22). Then should the code be changed to this: Build.VERSION.SDK_INT == 21 - That is just in the API 21 I test at Nexus with API 22Southwestwards
Still I'm not able to set custom font using this method in Nexus 9Issiah
Any one having problem not working for 5.1+. Don't override typeface in your values-v21 styles.Boundary
@MuhammadBabar Thanks your solution made my dayMagnuson
The first code snippet does not work with API level 22 , Android 5.1.1 . But the other code section does. What should be the exact if condition so ?Murphree
G
15

its very simple... 1.Download and put ur custom font in assets..then write one separate class for text view as follows: here i used futura font

public class CusFntTextView extends TextView {

public CusFntTextView(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
    init();
}

public CusFntTextView(Context context, AttributeSet attrs) {
    super(context, attrs);
    init();
}

public CusFntTextView(Context context) {
    super(context);
    init();
}

private void init() {
    if (!isInEditMode()) {
        Typeface tf = Typeface.createFromAsset(getContext().getAssets(), "Futura.ttf");
        setTypeface(tf);
    }
}

}

and do the following in xml :

 <com.packagename.CusFntTextView
        android:id="@+id/tvtitle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"         
        android:text="Hi Android"           
        android:textAppearance="?android:attr/textAppearanceLarge"
      />
Gerik answered 8/11, 2013 at 6:54 Comment(2)
Yes, that works :) But, what if I want to apply the same fonts for other remaining views/ widgets? Do I need to write separate class for all other views (including dialogs / toasts / actionbars) too?Bartel
Yes, for this solution you need to write separate classes for each of your view.Parochialism
C
9

I would also suggest extending TextView and other controls, but it would be better I consider to set up font in constructs.

public FontTextView(Context context) {
    super(context);
    init();
}

public FontTextView(Context context, AttributeSet attrs) {
    super(context, attrs);
    init();
}

public FontTextView(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
    init();
}

protected void init() {
    setTypeface(Typeface.createFromAsset(getContext().getAssets(), AppConst.FONT));
}
Cacique answered 9/11, 2012 at 9:41 Comment(2)
Be careful when doing this on platforms before 4.0 - this will leak a lot of resources due to a bug in Android: code.google.com/p/android/issues/detail?id=9904Atheism
how can we revert back to default. I am doing something like this: if(configShared.getString("storeId", AppCredentials.DEFAULT_STORE_ID).equals("2")){ final Field staticField = Typeface.class.getDeclaredField(staticTypefaceFieldName); staticField.setAccessible(true); staticField.set(null, newTypeface); } else{ final Field staticField = Typeface.class.getDeclaredField(staticTypefaceFieldName); staticField.setAccessible(true); staticField.set(null, null); }Overscore
M
8

I would like to improve weston's and Roger Huang's answers for over API 21 Android lollipop with theme "Theme.AppCompat".

Below Android 4.4

<resources>
    <style name="AppBaseTheme" parent="Theme.AppCompat.Light">
    </style>

   <!-- Application theme. -->
   <style name="AppTheme" parent="AppBaseTheme">
       <item name="android:typeface">monospace</item>
   </style>
</resources>

Over(equal) API 5.0

<resources>
    <style name="AppBaseTheme" parent="Theme.AppCompat.Light">
    </style>

   <!-- Application theme. -->
   <style name="AppTheme" parent="AppBaseTheme">
       <item name="android:textAppearance">@style/CustomTextAppearance</item>
   </style>

   <style name="CustomTextAppearance">
       <item name="android:typeface">monospace</item>
   </style>
</resources>

And the FontsOverride util file is same as what in weston's answer. I have tested in these phones:

Nexus 5(android 5.1 Primary Android System)

ZTE V5(android 5.1 CM12.1)

XIAOMI note(android 4.4 MIUI6)

HUAWEI C8850(android 2.3.5 UNKNOWN)

Metabolic answered 28/5, 2015 at 4:54 Comment(0)
M
8

A brilliant solution can be found here: https://coderwall.com/p/qxxmaa/android-use-a-custom-font-everywhere.

Simply extend activities from BaseActivity and write those methods. Also you should better cache fonts as described here: https://mcmap.net/q/54995/-memory-leaks-with-custom-font-for-set-custom-font.


After some research I wrote code that works at Samsung Galaxy Tab A (Android 5.0). Used code of weston and Roger Huang as well as https://mcmap.net/q/57128/-how-to-set-default-font-family-for-entire-android-app. Also tested on Lenovo TAB 2 A10-70L, where it doesn't work. I inserted a font 'Comic Sans' here in order to see a difference.

import android.content.Context;
import android.graphics.Typeface;
import android.os.Build;
import android.util.Log;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

public class FontsOverride {
    private static final int BOLD = 1;
    private static final int BOLD_ITALIC = 2;
    private static final int ITALIC = 3;
    private static final int LIGHT = 4;
    private static final int CONDENSED = 5;
    private static final int THIN = 6;
    private static final int MEDIUM = 7;
    private static final int REGULAR = 8;

    private Context context;

    public FontsOverride(Context context) {
        this.context = context;
    }

    public void loadFonts() {
        Map<String, Typeface> fontsMap = new HashMap<>();
        fontsMap.put("sans-serif", getTypeface("comic.ttf", REGULAR));
        fontsMap.put("sans-serif-bold", getTypeface("comic.ttf", BOLD));
        fontsMap.put("sans-serif-italic", getTypeface("comic.ttf", ITALIC));
        fontsMap.put("sans-serif-light", getTypeface("comic.ttf", LIGHT));
        fontsMap.put("sans-serif-condensed", getTypeface("comic.ttf", CONDENSED));
        fontsMap.put("sans-serif-thin", getTypeface("comic.ttf", THIN));
        fontsMap.put("sans-serif-medium", getTypeface("comic.ttf", MEDIUM));
        overrideFonts(fontsMap);
    }

    private void overrideFonts(Map<String, Typeface> typefaces) {
        if (Build.VERSION.SDK_INT == 21) {
            try {
                final Field field = Typeface.class.getDeclaredField("sSystemFontMap");
                field.setAccessible(true);
                Map<String, Typeface> oldFonts = (Map<String, Typeface>) field.get(null);
                if (oldFonts != null) {
                    oldFonts.putAll(typefaces);
                } else {
                    oldFonts = typefaces;
                }
                field.set(null, oldFonts);
                field.setAccessible(false);
            } catch (Exception e) {
                Log.e("TypefaceUtil", "Cannot set custom fonts");
            }
        } else {
            try {
                for (Map.Entry<String, Typeface> entry : typefaces.entrySet()) {
                    final Field staticField = Typeface.class.getDeclaredField(entry.getKey());
                    staticField.setAccessible(true);
                    staticField.set(null, entry.getValue());
                }
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }

    private Typeface getTypeface(String fontFileName, int fontType) {
        final Typeface tf = Typeface.createFromAsset(context.getAssets(), "fonts/" + fontFileName);
        return Typeface.create(tf, fontType);
    }
}

To run the code in entire application you should write in some class like Application the following:

    new FontsOverride(this).loadFonts();

Create a folder 'fonts' inside 'assets' and put needed fonts there. A simple instruction may be found here: https://mcmap.net/q/57129/-how-to-use-roboto-font-in-android-project-duplicate.

The Lenovo device also incorrectly gets a value of a typeface. In most times it returns Typeface.NORMAL, sometimes null. Even if a TextView is bold (in xml-file layout). See here: TextView isBold always returns NORMAL. This way a text on a screen is always in a regural font, not bold or italic. So I think it's a bug of a producer.

Mutation answered 17/11, 2015 at 15:16 Comment(1)
I studied android source code, your answer is the best best one. It can replace thin, medium, light or anything else. Thank you so much!Frill
P
6

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>
Parsons answered 10/3, 2015 at 3:33 Comment(0)
W
6

As of Android O this is now possible to define directly from the XML and my bug is now closed!

See here for details

TL;DR:

First you must add your fonts to the project

Second you add a font family, like so:

<?xml version="1.0" encoding="utf-8"?>
<font-family xmlns:android="http://schemas.android.com/apk/res/android">
    <font
        android:fontStyle="normal"
        android:fontWeight="400"
        android:font="@font/lobster_regular" />
    <font
        android:fontStyle="italic"
        android:fontWeight="400"
        android:font="@font/lobster_italic" />
</font-family>

Finally, you can use the font in a layout or style:

<TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:fontFamily="@font/lobster"/>

<style name="customfontstyle" parent="@android:style/TextAppearance.Small">
    <item name="android:fontFamily">@font/lobster</item>
</style>

Enjoy!

Waldrop answered 11/4, 2017 at 7:36 Comment(2)
Thanks. We should use Android Studio 2.4 to able to add font folder.Guncotton
actually I just need to apply it to my style and it'll be applied through the application. but some (programmatically added controls will need to be applied through java code)Plaintiff
H
4

You can set custom fonts for every layout one by one ,with just one function call from every layout by passing its root View.First ,create a singelton approach for accessing font object like this

 public class Font {
    private static Font font;
    public Typeface ROBO_LIGHT;

    private Font() {

    }

    public static Font getInstance(Context context) {
        if (font == null) {
            font = new Font();
            font.init(context);
        }
        return font;

    }

    public void init(Context context) {

        ROBO_LIGHT = Typeface.createFromAsset(context.getAssets(),
                "Roboto-Light.ttf");
    }

}

You can define different fonts in above class, Now Define a font Helper class that will apply fonts :

   public class FontHelper {

    private static Font font;

    public static void applyFont(View parentView, Context context) {

        font = Font.getInstance(context);

        apply((ViewGroup)parentView);

    }

    private static void apply(ViewGroup parentView) {
        for (int i = 0; i < parentView.getChildCount(); i++) {

            View view = parentView.getChildAt(i);

//You can add any view element here on which you want to apply font 

            if (view instanceof EditText) {

                ((EditText) view).setTypeface(font.ROBO_LIGHT);

            }
            if (view instanceof TextView) {

                ((TextView) view).setTypeface(font.ROBO_LIGHT);

            }

            else if (view instanceof ViewGroup
                    && ((ViewGroup) view).getChildCount() > 0) {
                apply((ViewGroup) view);
            }

        }

    }

}

In the above code, I am applying fonts on textView and EditText only , you can apply fonts on other view elements as well similarly.You just need to pass the id of your root View group to the above apply font method. for example your layout is :

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:id="@+id/mainParent"
    tools:context="${relativePackage}.${activityClass}" >

    <RelativeLayout
        android:id="@+id/mainContainer"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_above="@+id/homeFooter"
        android:layout_below="@+id/edit" >

        <ImageView
            android:id="@+id/PreviewImg"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:src="@drawable/abc_list_longpressed_holo"
            android:visibility="gone" />

        <RelativeLayout
            android:id="@+id/visibilityLayer"
            android:layout_width="match_parent"
            android:layout_height="fill_parent" >

            <ImageView
                android:id="@+id/UseCamera"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentTop="true"
                android:layout_centerHorizontal="true"
                android:src="@drawable/camera" />

            <TextView
                android:id="@+id/tvOR"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_below="@+id/UseCamera"
                android:layout_centerHorizontal="true"
                android:layout_marginTop="20dp"
                android:text="OR"
                android:textSize="30dp" />

            <TextView
                android:id="@+id/tvAND"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerHorizontal="true"
                android:layout_marginTop="20dp"
                android:text="OR"
                android:textSize="30dp" />

</RelativeLayout>

In the Above Layout the root parent id is "Main Parent " now lets apply font

public class MainActivity extends BaseFragmentActivity {

    private EditText etName;
    private EditText etPassword;
    private TextView tvTitle;
    public static boolean isHome = false;

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

       Font font=Font.getInstance(getApplicationContext());
        FontHelper.applyFont(findViewById(R.id.mainParent),          getApplicationContext());
   }    
}

Cheers :)

Haroun answered 12/3, 2015 at 19:0 Comment(1)
Nice.. no more same font re-create, only 1 font use for all field. :)Foothill
C
3

I would suggest extending TextView, and always using your custom TextView within your XML layouts or wherever you need a TextView. In your custom TextView, override setTypeface

@Override
public void setTypeface(Typeface tf, int style) {
    //to handle bold, you could also handle italic or other styles here as well
    if (style == 1){
        tf = Typeface.createFromAsset(getContext().getApplicationContext().getAssets(), "MuseoSans700.otf");
    }else{
        tf = Typeface.createFromAsset(getContext().getApplicationContext().getAssets(), "MuseoSans500.otf");
    }
    super.setTypeface(tf, 0);
}
Chase answered 7/3, 2012 at 20:27 Comment(0)
J
2

Tom's solution works great, but only works with TextView and EditText.

If you want to cover most of the views (RadioGroup, TextView, Checkbox...), I created a method doing that :

protected void changeChildrenFont(ViewGroup v, Typeface font){
    for(int i = 0; i < v.getChildCount(); i++){

        // For the ViewGroup, we'll have to use recursivity
        if(v.getChildAt(i) instanceof ViewGroup){
            changeChildrenFont((ViewGroup) v.getChildAt(i), font);
        }
        else{
            try {
                Object[] nullArgs = null;
                //Test wether setTypeface and getTypeface methods exists
                Method methodTypeFace = v.getChildAt(i).getClass().getMethod("setTypeface", new Class[] {Typeface.class, Integer.TYPE});
                //With getTypefaca we'll get back the style (Bold, Italic...) set in XML
                Method methodGetTypeFace = v.getChildAt(i).getClass().getMethod("getTypeface", new Class[] {});
                Typeface typeFace = ((Typeface)methodGetTypeFace.invoke(v.getChildAt(i), nullArgs));
                //Invoke the method and apply the new font with the defined style to the view if the method exists (textview,...)
                methodTypeFace.invoke(v.getChildAt(i), new Object[] {font, typeFace == null ? 0 : typeFace.getStyle()});
            }
            //Will catch the view with no such methods (listview...)
            catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

This method will get back the style of the view set in XML (bold, italic...) and apply them if they exists.

For the ListView, I always create an adapter, and I set the font inside getView.

Jasen answered 16/9, 2013 at 10:49 Comment(0)
G
2

I wrote a class assigning typeface to the views in the current view hierarchy and based os the current typeface properties (bold, normal, you can add other styles if you want):

public final class TypefaceAssigner {

public final Typeface DEFAULT;
public final Typeface DEFAULT_BOLD;

@Inject
public TypefaceAssigner(AssetManager assetManager) {
    DEFAULT = Typeface.createFromAsset(assetManager, "TradeGothicLTCom.ttf");
    DEFAULT_BOLD = Typeface.createFromAsset(assetManager, "TradeGothicLTCom-Bd2.ttf");
}

public void assignTypeface(View v) {
    if (v instanceof ViewGroup) {
        for (int i = 0; i < ((ViewGroup) v).getChildCount(); i++) {
            View view = ((ViewGroup) v).getChildAt(i);
            if (view instanceof ViewGroup) {
                setTypeface(view);
            } else {
                setTypeface(view);
            }
        }
    } else {
        setTypeface(v);
    }
}

private void setTypeface(View view) {
    if (view instanceof TextView) {
        TextView textView = (TextView) view;
        Typeface typeface = textView.getTypeface();
        if (typeface != null && typeface.isBold()) {
            textView.setTypeface(DEFAULT_BOLD);
        } else {
            textView.setTypeface(DEFAULT);
        }
    }
}
}

Now in all fragments in onViewCreated or onCreateView, in all activities in onCreate and in all view adapters in getView or newView just invoke:

typefaceAssigner.assignTypeface(view);
Gaudette answered 8/5, 2015 at 13:6 Comment(0)
C
2

in api 26 with build.gradle 3.0.0 and higher you can create a font directory in res and use this line in your style

<item name="android:fontFamily">@font/your_font</item>

for change build.gradle use this in your build.gradle dependecies

classpath 'com.android.tools.build:gradle:3.0.0'
Charlatan answered 21/11, 2017 at 10:55 Comment(3)
and how to insert the font into the project?Lyontine
@Lyontine copy your font into res/fontCharlatan
yeah, tanx. I already got that. But that item alone doesn't set the font for the entire app. any clue?Lyontine
C
1

Finally, Google realized the severity of this problem (applying custom font to UI components) and they devised a clean solution for it.

First, you need to update to support library 26+ (you may also need to update your gradle{4.0+}, android studio), then you can create a new resource folder called font. In this folder, you can put your font resources (.tff,...). Then you need to override the default app them and force your custom font into it :)

<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
    <item name="android:fontFamily">@font/my_custom_font</item>
</style>

Note: if you want to support devices with older API than 16, you have to use app namespace instead of android!

Conakry answered 20/1, 2018 at 9:52 Comment(0)
S
0

I would also like to improve weston's answer for API 21 Android 5.0.

I had the same issue on my Samsung s5, when using DEFAULT font. (with the others fonts it's working fine)

I managed to make it working by setting the typeface ("sans" for example) in XML files, for each Textview or Button

<TextView
android:layout_width="match_parent"
android:layout_height="39dp"
android:textColor="@color/abs__background_holo_light"
android:textSize="12sp"
android:gravity="bottom|center"
android:typeface="sans" />

and in MyApplication Class :

public class MyApplication extends Application {
    @Override
    public void onCreate() {
    TypefaceUtil.overrideFont(getApplicationContext(), "SANS_SERIF",
    "fonts/my_font.ttf");
    }
}

Hope it helps.

Snapdragon answered 8/4, 2015 at 13:30 Comment(0)
M
0

This solution does not work correctly in some situations.
So I extend it:

FontsReplacer.java

public class MyApplication extends Application {

    @Override
    public void onCreate() {
        FontsReplacer.replaceFonts(this);
        super.onCreate();
    }

}

https://gist.github.com/orwir/6df839e3527647adc2d56bfadfaad805

Maharaja answered 3/6, 2016 at 13:17 Comment(0)
K
0

Calligraphy works pretty well, but it is not suitable for me, since it does not support different weights (bold, italic, etc) for a font-family.

So I tried Fontain, which allows you to define custom Views and apply them custom font families.

in order to use Fontain, you should add the following to your app module build.gradle:

compile 'com.scopely:fontain:1.0.0'

Then, instead of using regular TextView, you should use FontTextView

Example of FontTextView with uppercase and bold content:

 <com.scopely.fontain.views.FontTextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@android:color/black"
            android:textColor="@android:color/white"
            android:textSize="11dp"
            android:gravity="center"
            android:id="@+id/tv1"
            app:font_family="myCustomFont"
            app:caps_mode="characters"
            app:font_weight="BOLD"/>
Koenraad answered 6/10, 2016 at 11:42 Comment(0)
T
0
package com.theeasylearn.demo.designdemo;
import android.content.Context;
import android.graphics.Typeface;
import android.util.AttributeSet;
import android.widget.TextView;

public class MyButton extends TextView {

    public MyButton(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init();
    }

    public MyButton(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public MyButton(Context context) {
        super(context);
        init();
    }

    private void init() {

            Typeface tf =
                    Typeface.createFromAsset(
                            getContext().getAssets(), "angelina.TTF");
            setTypeface(tf);

    }

}
Treatment answered 18/10, 2016 at 18:39 Comment(1)
Not a perfect solution. Using this solution will require custom workarounds to access default Android TextViewsMorea
H
0

For changing default font family of the TextViews, override textViewStyle in your app theme.

For using custom font in fontFamily, use font resources which is in support library.

The feature was added in Android 26 but backported to older versions via supportlib.

https://developer.android.com/guide/topics/resources/font-resource.html https://developer.android.com/guide/topics/ui/look-and-feel/fonts-in-xml.html#using-support-lib

Hydroquinone answered 26/1, 2018 at 17:59 Comment(0)
M
0

Since the release of Android Oreo and its support library (26.0.0) you can do this easily. Refer to this answer in another question.

Basically your final style will look like this:

<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
   <item name="fontFamily">@font/your_font</item> <!-- target android sdk versions < 26 and > 14 -->
</style>
Metabolite answered 13/5, 2018 at 9:16 Comment(0)
D
-1

I found the mixture of calligraphy 3 library and coderwall's post as my ultimate result.

Discriminator answered 12/10, 2019 at 16:14 Comment(0)
M
-15

Yes, its possible to set the font to the entire application.

The easiest way to accomplish this is to package the desired font(s) with your application.

To do this, simply create an assets/ folder in the project root, and put your fonts (in TrueType, or TTF, form) in the assets.

You might, for example, create assets/fonts/ and put your TTF files in there.

public class FontSampler extends Activity {
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
TextView tv=(TextView)findViewById(R.id.custom);

Typeface face=Typeface.createFromAsset(getAssets(), "fonts/HandmadeTypewriter.ttf");
tv.setTypeface(face);
}
}
Microphyte answered 27/5, 2010 at 17:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.