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

后端 未结 25 2693
日久生厌
日久生厌 2020-11-22 02:44

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 else

相关标签:
25条回答
  • 2020-11-22 03:15

    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.

    0 讨论(0)
  • 2020-11-22 03:17
    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);
    
        }
    
    }
    
    0 讨论(0)
  • 2020-11-22 03:18

    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>
    
    0 讨论(0)
  • 2020-11-22 03:18

    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

    0 讨论(0)
  • 2020-11-22 03:22

    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.

    0 讨论(0)
  • 2020-11-22 03:22

    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();
            }
        }
    }
    
    0 讨论(0)
提交回复
热议问题