在 Android 中使用自定义字体

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2973270/
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-20 08:07:38  来源:igfitidea点击:

Using a custom typeface in Android

xmlandroidlayoutfonts

提问by Codevalley

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

我想为我正在创建的 android 应用程序使用自定义字体。
我可以从 Code 中单独更改每个对象的字体,但我有数百个。

So,

所以,

  • Is there a way to do this from the XML? [Setting a custom typeface]
  • Is there a way to do it from code in one place, to say that the whole application and all the components should use the custom typeface instead of the default one?
  • 有没有办法从 XML 中做到这一点?[设置自定义字体]
  • 有没有办法从一个地方的代码中做到这一点,也就是说整个应用程序和所有组件都应该使用自定义字体而不是默认字体?

采纳答案by CommonsWare

Is there a way to do this from the XML?

有没有办法从 XML 中做到这一点?

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

不,对不起。您只能通过 XML 指定内置字体。

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

有没有办法从一个地方的代码中做到这一点,也就是说整个应用程序和所有组件都应该使用自定义字体而不是默认字体?

Not that I am aware of.

不是我所知道的。

There are a variety of options for these nowadays:

现在有多种选择:

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

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

  • Android SDK 中的字体资源和向后移植(如果您正在使用) appcompat

  • 不使用的第三方库appcompat,但并非所有人都支持在布局资源中定义字体

回答by Manish Singla

Yes It is possible.

对的,这是可能的。

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

您必须创建一个扩展文本视图的自定义视图。

In attrs.xmlin valuesfolder:

attrs.xmlvalues文件夹中:

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

In main.xml:

main.xml

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

In MyTextView.java:

MyTextView.java

package com.lht.ui;

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

public class MyTextView extends TextView {

    Context context;
    String ttfName;

    String TAG = getClass().getName();

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

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

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

            init();
        }

    }

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

    @Override
    public void setTypeface(Typeface tf) {

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

}

回答by Per Christian Henden

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

我以一种更“蛮力”的方式做到了这一点,不需要更改布局 xml 或活动。

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

在 Android 2.1 到 4.4 版本上测试。在应用程序启动时在您的 Application 类中运行它:

private void setDefaultFont() {

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

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

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

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

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

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

有关更完整的示例,请参阅http://github.com/perchrh/FontOverrideExample

回答by pospi

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

虽然我赞成 Manish 的答案是最快和最有针对性的方法,但我也看到了一些幼稚的解决方案,它们只是递归地遍历视图层次结构并依次更新所有元素的字体。像这样的东西:

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

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

您需要在膨胀布局之后和活动的onContentChanged()方法中在您的视图上调用此函数。

回答by M-WaJeEh

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

我能够以集中的方式做到这一点,结果如下:

enter image description here

在此处输入图片说明

I have following Activityand I extend from it if I need custom fonts:

我有以下内容Activity,如果我需要自定义字体,我会从中扩展:

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

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

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

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

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

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

但是,如果我使用支持包中的活动,例如,FragmentActivity那么我使用这个Activity

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

public class CustomFontFragmentActivity extends FragmentActivity {

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

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

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

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

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

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

我还没有用Fragments测试过这段代码,但希望它会起作用。

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

我的FontUtils很简单,它也解决了这里提到的 pre-ICS 问题https://code.google.com/p/android/issues/detail?id=9904

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

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

public class FontUtils {

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

public static Typeface getFonts(Context context, String name) { 
    Typeface typeface = TYPEFACE.get(name);
    if (typeface == null) {
        typeface = Typeface.createFromAsset(context.getAssets(), "fonts/"
                + name);
        TYPEFACE.put(name, typeface);
    }
    return typeface;
}
}

回答by Informatic0re

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

嘿,我的应用程序中还需要 2 种不同的字体用于不同的 widged!我用这种方式:

In my Application class i create an static method:

在我的 Application 类中,我创建了一个静态方法:

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

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

String 字体代表资产文件夹中的 xyz.ttf。(我创建了一个常量类)现在你可以在你的应用程序的任何地方使用它:

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

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

唯一的问题是,对于要使用字体的每个小部件,您都需要它!但我认为这是最好的方法。

回答by userM1433372

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

我在Lisa Wray 的博客上找到了一个不错的解决方案。使用新的数据绑定,可以在 XML 文件中设置字体。

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

In XML:

在 XML 中:

<TextView
app:font="@{`Source-Sans-Pro-Regular.ttf`}"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"/>

回答by majinboo

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

使用pospi 的建议并像Richard一样使用 'tag' 属性,我创建了一个自定义类,用于加载我的自定义字体并根据它们的标签将它们应用于视图。

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

所以基本上,不是在属性 android:fontFamily 中设置 TypeFace,而是使用 android:tag attritube 并将其设置为定义的枚举之一。

public class Fonts {
    private AssetManager mngr;

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

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

In your Activity or Fragment you just call

在您的 Activity 或 Fragment 中,您只需调用

Fonts fonts = new Fonts(getActivity());
fonts.setupLayoutTypefaces(mainLayout);

回答by Snicolas

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

我认为可以有一种更方便的方法来做到这一点。以下类将为应用程序的所有组件设置自定义字体(每个类都有一个设置)。

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

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

    private Typeface helloTypeface;

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

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

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

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

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

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

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

}

回答by Ryan Thomas

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

LayoutInflater 的默认实现不支持从 xml 指定字体字体。然而,我已经看到它通过为 LayoutInflater 提供一个自定义工厂来在 xml 中完成,该工厂将从 xml 标记解析此类属性。

The basic structure would like this.

基本的结构是这样。

public class TypefaceInflaterFactory implements LayoutInflater.Factory {

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

}

public class BaseActivity extends Activity {

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

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

本文更深入地解释了这些机制,以及作者如何尝试以这种方式为字体提供 xml 布局支持。作者的实现代码可以在这里找到。