能否为整个应用设置自定义字体?

275

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


在Android Studio 3.0中,您可以轻松设置自定义字体:https://developer.android.com/guide/topics/ui/look-and-feel/fonts-in-xml - MHSaffari
@MHSFisher 字体在 XML 中是 Android 8.0(API 级别 26)的一个功能。 - Emad Razavi
1
@EmiRaz 请阅读文档 https://developer.android.com/guide/topics/ui/look-and-feel/fonts-in-xml#using-support-lib。该功能已添加到支持库26中,但支持API 16及以上版本。 - MHSaffari
25个回答

6
从 Android O 开始,现在可以直接从 XML 定义,并且 我的 bug 现在已关闭! 详情请参见此处 TL;DR:
首先,您必须将字体添加到项目中。
其次,您可以像这样添加字体系列:
<?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>

最后,您可以在布局或样式中使用字体:
<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>

享受!


谢谢。我们应该使用Android Studio 2.4才能添加字体文件夹。 - Photon Point
实际上,我只需要将它应用到我的风格中,然后它就会通过应用程序应用。但是一些(以编程方式添加的控件)需要通过Java代码应用。 - Desolator

4
您可以通过对每个布局单独调用一个函数并传递其根视图来为每个布局设置自定义字体。首先,像这样创建一个访问字体对象的单例方法:
 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");
    }

}

您可以在上述类中定义不同的字体。现在定义一个字体助手类,它将应用字体:
   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);
            }

        }

    }

}

在上述代码中,我仅对textView和EditText应用了字体,您也可以类似地将字体应用于其他视图元素。您只需要将您的根View组的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>

在上面的布局中,根父元素的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());
   }    
}

祝福您:)


很好,不再需要重新创建相同的字体,所有字段都只使用一个字体。 :) - Arfan Mirza

3
我建议扩展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);
}

2

我写了一个类,为当前视图层次结构中的视图分配字体,并基于当前字体属性(加粗、普通,如果需要可以添加其他样式):

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

现在,在所有的fragments的onViewCreated或onCreateView方法中,在所有的activities的onCreate方法中,在所有的view adapters的getView或newView方法中,只需调用以下内容:

typefaceAssigner.assignTypeface(view);

2
在API 26及以上版本中,使用build.gradle 3.0.0及更高版本,您可以在res目录下创建一个字体目录,并在您的样式中使用以下行:
<item name="android:fontFamily">@font/your_font</item>

要更改 build.gradle,请在您的 build.gradle 依赖项中使用以下内容

classpath 'com.android.tools.build:gradle:3.0.0'

如何将字体插入项目中? - azerafati
@azerafati 将你的字体复制到 res/font 中。 - Mohammad Hosein Heidari
是的,谢谢。我已经明白了。但是仅仅修改那个“item”并不能设置整个应用程序的字体。有什么线索吗? - azerafati

2

汤姆的解决方案非常有效,但仅适用于TextView和EditText。

如果您想涵盖大多数视图(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();
            }
        }
    }
}

这个方法将获取在XML中设置的视图样式(加粗、斜体等),如果存在,则应用它们。

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


1
最后,Google 意识到了这个问题(将自定义字体应用于 UI 组件)的严重性,并为此设计了一个干净的解决方案。 首先,您需要更新到支持库 26+(您可能还需要更新 gradle{4.0+}、android studio),然后可以创建一个名为 font 的新资源文件夹。在此文件夹中,您可以放置您的字体资源(.tff 等)。 然后,您需要覆盖默认的应用程序主题并强制使用您的自定义字体 :)
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
    <item name="android:fontFamily">@font/my_custom_font</item>
</style>

注意:如果您想支持API版本低于16的设备,则必须使用app命名空间而不是android!

0

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

当使用DEFAULT字体时,我在我的三星S5上遇到了同样的问题。(使用其他字体时它可以正常工作)

我通过在XML文件中为每个Textview或Button设置"typeface"(例如"sans"),成功使其正常工作。

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

在 MyApplication 类中:

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

希望能帮到你。

0

自从发布了Android Oreo及其支持库(26.0.0)后,你可以轻松地做到这一点。请参考另一个问题中的this answer

基本上,你的最终样式会像这样:

<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
   <item name="fontFamily">@font/your_font</item> <!-- target android sdk versions < 26 and > 14 -->
</style>

0

书法很不错,但对我来说并不适用,因为它不支持为字体系列设置不同的权重(粗体、斜体等)。

所以我尝试了Fontain,它允许您定义自定义视图并应用它们的自定义字体系列。

为了使用Fontain,您应该将以下内容添加到您的应用程序模块build.gradle中:

compile 'com.scopely:fontain:1.0.0'

那么,你应该使用FontTextView而不是普通的TextView

以下是使用大写和粗体内容的FontTextView示例:

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接