Java 是否可以为整个应用程序设置自定义字体?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2711858/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 11:57:57  来源:igfitidea点击:

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

javaandroidandroid-fonts

提问by Samuh

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?

我需要为我的整个应用程序使用某种字体。我有相同的 .ttf 文件。是否可以在应用程序启动时将其设置为默认字体,然后在应用程序的其他地方使用它?设置后,如何在布局 XML 中使用它?

回答by Selvakumar

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.

为此,只需 在项目根目录中创建一个assets/文件夹,并将您的字体(以 TrueType 或 TTF 形式)放入资产中。

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

例如,您可以创建assets/fonts/并将您的 TTF 文件放在那里。

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);
}
}

回答by Tom

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 TextViewso they should work just fine. I added a boolean conditional for using reflection because it seems very hackish and might notably compromise performance.

虽然这不适用于整个应用程序,但它适用于 Activity 并且可以重新用于任何其他 Activity。感谢@FR073N,我更新了我的代码以支持其他视图。我不确定, 等的问题ButtonsRadioGroups因为这些类都扩展了,TextView所以它们应该可以正常工作。我为使用反射添加了一个布尔条件,因为它看起来很hackish,可能会显着影响性能。

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

注意:正如所指出的,这不适用于动态内容!为此,可以使用onCreateVieworgetView方法调用此方法,但需要额外的努力。

/**
 * 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.

希望有帮助。

回答by Sam Dozor

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

我建议扩展 TextView,并始终在 XML 布局中或任何需要 TextView 的地方使用自定义 TextView。在您的自定义 TextView 中,覆盖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);
}

回答by Andrey Mischenko

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

我还建议扩展 TextView 和其他控件,但最好考虑在构造中设置字体。

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));
}

回答by weston

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.

(注意:由于缺乏对自定义字体的支持,这是一种解决方法,因此如果您想改变这种情况,请在此处android 问题点赞)。注意:不要在这个问题上留下“我也是”的评论,当你这样做时,所有关注它的人都会收到一封电子邮件。所以请“加星”吧。

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 applicationclass:

然后您需要重载少数默认字体,例如在应用程序类中:

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:

但是,我倾向于只覆盖一个,例如"MONOSPACE",然后设置一种样式来强制该字体字体应用广泛:

<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

API 21 安卓 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.

我已经调查了评论中的报告,它不起作用,并且似乎与主题不兼容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>

回答by FR073N

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

Tom 的解决方案效果很好,但仅适用于 TextView 和 EditText。

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

如果您想涵盖大部分视图(RadioGroup、TextView、Checkbox...),我创建了一个方法:

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.

此方法将取回在 XML 中设置的视图样式(粗体、斜体...),并在它们存在时应用它们。

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

对于 ListView,我总是创建一个适配器,并在 getView 中设置字体。

回答by Palani

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

它非常简单...... 1.下载并将你的自定义字体放入资产中......然后为文本视图编写一个单独的类,如下所示:这里我使用了futura字体

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 :

并在 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"
      />

回答by Roger Huang

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

我想改进weston对 API 21 Android 5.0 的回答。

Cause

原因

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

在 API 21 下,大多数文本样式都包含 fontFamily 设置,例如:

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

Which applys the default Roboto Regular font:

这应用了默认的 Roboto Regular 字体:

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

原始答案未能应用等宽字体,因为 android:fontFamily 对 android:typeface 属性(参考)具有更高的优先级。使用 Theme.Holo.* 是一种有效的解决方法,因为里面没有 android:fontFamily 设置。

Solution

解决方案

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

由于 Android 5.0 将系统字体放在静态变量 Typeface.sSystemFontMap(参考)中,我们可以使用相同的反射技术来替换它:

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();
        }
    }
}

回答by Guy Micciche

Working for Xamarin.Android:

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

回答by Ajji

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

您可以为每个布局一个一个地设置自定义字体,通过传递其根 View.First 来从每个布局调用一个函数,创建一个单一的方法来访问这样的字体对象

 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 :

在上面的代码中,我只在 textView 和 EditText 上应用字体,您也可以在其他视图元素上类似地应用字体。您只需要将根视图组的 id 传递给上面的应用字体方法。例如你的布局是:

<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

在上面的布局中,根父 ID 是“Main Parent”,现在让我们应用字体

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 :)

干杯:)