在使用xml的android TextView中使用自定义字体

Posted

技术标签:

【中文标题】在使用xml的android TextView中使用自定义字体【英文标题】:Using custom font in android TextView using xml 【发布时间】:2012-03-08 18:46:18 【问题描述】:

我已将自定义字体文件添加到我的资产/字体文件夹中。如何从我的 XML 中使用它?

我可以从如下代码中使用它:

TextView text = (TextView) findViewById(R.id.textview03);
Typeface tf = Typeface.createFromAsset(getAssets(), "fonts/Molot.otf");
text.setTypeface(tf);

我不能使用 android:typeface="/fonts/Molot.otf" 属性从 XML 中执行此操作吗?

【问题讨论】:

我已经搜索了很多,您无法从 xml 中做到这一点。 试试看这篇帖子***.com/questions/2376250/… 看看this answer!它允许您使用多种字体并使用 XML。 正如下面所说的,您可以使用Calligraphy 来实现这一点。 查看这篇文章gadgetsaint.com/tips/… 【参考方案1】:

简短回答:不。Android 没有内置支持通过 XML 将自定义字体应用于文本小部件。

但是,有一个解决方法并不难实现。

第一

您需要定义自己的样式。在您的 /res/values 文件夹中,打开/创建 attrs.xml 文件并添加一个可声明样式的对象,如下所示:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="FontText">
        <attr name="typefaceAsset" format="string"/>
    </declare-styleable>
</resources>

第二

假设您想经常使用这个小部件,您应该为加载的Typeface 对象设置一个简单的缓存,因为从内存中动态加载它们可能需要一些时间。比如:

public class FontManager 
    private static FontManager instance;

    private AssetManager mgr;

    private Map<String, Typeface> fonts;

    private FontManager(AssetManager _mgr) 
        mgr = _mgr;
        fonts = new HashMap<String, Typeface>();
    

    public static void init(AssetManager mgr) 
        instance = new FontManager(mgr);
    

    public static FontManager getInstance() 
        if (instance == null) 
            // App.getContext() is just one way to get a Context here
            // getContext() is just a method in an Application subclass
            // that returns the application context
            AssetManager assetManager = App.getContext().getAssets();
            init(assetManager);
        
        return instance;
    

    public Typeface getFont(String asset) 
        if (fonts.containsKey(asset))
            return fonts.get(asset);

        Typeface font = null;

        try 
            font = Typeface.createFromAsset(mgr, asset);
            fonts.put(asset, font);
         catch (Exception e) 

        

        if (font == null) 
            try 
                String fixedAsset = fixAssetFilename(asset);
                font = Typeface.createFromAsset(mgr, fixedAsset);
                fonts.put(asset, font);
                fonts.put(fixedAsset, font);
             catch (Exception e) 

            
        

        return font;
    

    private String fixAssetFilename(String asset) 
        // Empty font filename?
        // Just return it. We can't help.
        if (TextUtils.isEmpty(asset))
            return asset;

        // Make sure that the font ends in '.ttf' or '.ttc'
        if ((!asset.endsWith(".ttf")) && (!asset.endsWith(".ttc")))
            asset = String.format("%s.ttf", asset);

        return asset;
    

这个允许您使用 .ttc 文件扩展名,但它未经测试。

第三

创建一个子类TextView 的新类。此特定示例考虑了定义的 XML 字体(bolditalic 等)并将其应用于字体(假设您使用的是 .ttc 文件)。

/**
 * TextView subclass which allows the user to define a truetype font file to use as the view's typeface.
 */
public class FontText extends TextView 
    public FontText(Context context) 
        this(context, null);
    

    public FontText(Context context, AttributeSet attrs) 
        this(context, attrs, 0);
    

    public FontText(Context context, AttributeSet attrs, int defStyle) 
        super(context, attrs, defStyle);

        if (isInEditMode())
            return;

        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.FontText);

        if (ta != null) 
            String fontAsset = ta.getString(R.styleable.FontText_typefaceAsset);

            if (!TextUtils.isEmpty(fontAsset)) 
                Typeface tf = FontManager.getInstance().getFont(fontAsset);
                int style = Typeface.NORMAL;
                float size = getTextSize();

                if (getTypeface() != null)
                    style = getTypeface().getStyle();

                if (tf != null)
                    setTypeface(tf, style);
                else
                    Log.d("FontText", String.format("Could not create a font from asset: %s", fontAsset));
            
        
    

终于

用完全限定的类名替换 XML 中的 TextView 实例。像声明 Android 命名空间一样声明您的自定义命名空间。请注意,“typefaceAsset”应指向 /assets 目录中包含的 .ttf 或 .ttc 文件。

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:custom="http://schemas.android.com/apk/res-auto"
    android:layout_
    android:layout_>

    <com.example.FontText
        android:layout_
        android:layout_
        android:text="This is a custom font text"
        custom:typefaceAsset="fonts/AvenirNext-Regular.ttf"/>
</RelativeLayout>

【讨论】:

很好的答案!但是有一个问题:为什么你在 isInEditMode 时返回? 04-11 18:18:32.685 3506-3506/com.example.demo E/AndroidRuntime:致命异常:主进程:com.example.demo,PID:3506 android.view.InflateException:二进制 XML 文件第 2 行:在 android.view.LayoutInflater.createView(LayoutInflater.java:620) 在 android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:696) 处膨胀类 com.example.demo.libraries.LatoTextView 时出错android.view.LayoutInflater.inflate(LayoutInflater.java:469) 在 android.view.LayoutInflater.inflate(LayoutInflater.java:397) 在 ... @AvrahamShukron - 那是因为我在预览模式下一直在 Android Studio 中遇到错误(大概是因为它不知道如何处理应用自定义字体。 在 RelativeLayout 中添加了 xmlns:custom="schemas.android.com/tools" 并且错误消失了。谢谢大佬 Hello @themarshal: 你应该使用 xmlns:custom="schemas.android.com/tools" 而不是: xmlns:custom="schemas.android.com/apk/res-auto" 以便使用可样式化的属性。【参考方案2】:

这是执行此操作的示例代码。我在静态最终变量中定义了字体,字体文件位于 assets 目录中。

public class TextViewWithFont extends TextView 

    public TextViewWithFont(Context context, AttributeSet attrs) 
        super(context, attrs);
        this.setTypeface(MainActivity.typeface);
    

    public TextViewWithFont(Context context, AttributeSet attrs, int defStyle) 
        super(context, attrs, defStyle);
        this.setTypeface(MainActivity.typeface);
    

    public TextViewWithFont(Context context) 
        super(context);
        this.setTypeface(MainActivity.typeface);
    


【讨论】:

OP 特别声明他知道如何以编程方式设置字体(他甚至提供了一个示例)。问题是如何在xml中设置字体?【参考方案3】:

创建您自定义的 TextView 属于您要使用的字体。在这个类中,我使用静态 mTypeface 字段来缓存 Typeface(以获得更好的性能)

public class HeliVnTextView extends TextView 

/*
 * Caches typefaces based on their file path and name, so that they don't have to be created every time when they are referenced.
 */
private static Typeface mTypeface;

public HeliVnTextView(final Context context) 
    this(context, null);


public HeliVnTextView(final Context context, final AttributeSet attrs) 
    this(context, attrs, 0);


public HeliVnTextView(final Context context, final AttributeSet attrs, final int defStyle) 
    super(context, attrs, defStyle);

     if (mTypeface == null) 
         mTypeface = Typeface.createFromAsset(context.getAssets(), "HelveticaiDesignVnLt.ttf");
     
     setTypeface(mTypeface);



在xml文件中:

<java.example.HeliVnTextView
        android:id="@+id/textView1"
        android:layout_
        ... />

在java类中:

HeliVnTextView title = new HeliVnTextView(getActivity());
title.setText(issue.getName());

【讨论】:

【参考方案4】:

Activity 实现了 LayoutInflater.Factory2,它为每个创建的 View 提供回调。 可以使用自定义字体 Family 属性设置 TextView 的样式,按需加载字体并在实例化的文本视图上自动调用 setTypeface。

不幸的是,由于 Inflater 实例相对于活动和 Windows 的架构关系,在 android 中使用自定义字体的最简单方法是在应用程序级别缓存加载的字体。

示例代码库在这里:

https://github.com/leok7v/android-textview-custom-fonts

  <style name="Baroque" parent="@android:style/TextAppearance.Medium">
    <item name="android:layout_width">fill_parent</item>
    <item name="android:layout_height">wrap_content</item>
    <item name="android:textColor">#F2BAD0</item>
    <item name="android:textSize">14pt</item>
    <item name="fontFamily">baroque_script</item>
  </style>

  <?xml version="1.0" encoding="utf-8"?>
  <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
          xmlns:custom="http://schemas.android.com/apk/res/custom.fonts"
          android:orientation="vertical"
          android:layout_
          android:layout_
  >
  <TextView
    style="@style/Baroque"
    android:layout_
    android:layout_
    android:text="@string/sample_text"
  />

结果

【讨论】:

2 年没有碰过那个代码。但它应该。我看不出有什么理论上可以阻止它。【参考方案5】:

由于this fact,在 xml 中使用自定义字体不是一个好主意 也就是说,您必须以编程方式来避免内存泄漏!

【讨论】:

Ice Cream Sandwich 中的内存泄漏似乎已修复。 你应该使用TypedArray方法recycle()来避免内存泄漏。【参考方案6】:

更新:https://github.com/chrisjenx/Calligraphy 似乎是 对此的卓越解决方案。


也许您可以在创建应用程序时使用反射将您的字体注入/破解到可用字体的静态列表中?如果这是一个非常非常糟糕的想法,或者如果这是一个很好的解决方案,我对其他人的反馈很感兴趣——它似乎将成为那些极端之一。 ..

我能够使用我自己的字体系列名称将我的自定义字体注入系统字体列表,然后将自定义字体系列名称(“brush-script”)指定为标准 TextView 上 android:FontFamily 的值工作在我运行 Android 6.0 的 LG G4 上。

public class MyApplication extends android.app.Application

    @Override
    public void onCreate()
    
        super.onCreate();

        Typeface font = Typeface.createFromAsset(this.getResources().getAssets(),"fonts/brush-script.ttf");
        injectTypeface("brush-script", font);
    

    private boolean injectTypeface(String fontFamily, Typeface typeface)
    
        try
        
            Field field = Typeface.class.getDeclaredField("sSystemFontMap");
            field.setAccessible(true);
            Object fieldValue = field.get(null);
            Map<String, Typeface> map = (Map<String, Typeface>) fieldValue;
            map.put(fontFamily, typeface);
            return true;
        
        catch (Exception e)
        
            Log.e("Font-Injection", "Failed to inject typeface.", e);
        
        return false;
    

在我的布局中

<TextView
    android:id="@+id/name"
    android:layout_
    android:layout_
    android:text="Fancy Text"
    android:fontFamily="brush-script"/>

【讨论】:

对我不起作用,您能详细说明如何使用它吗?是否需要调用MyApplication 类? @Yawz 您需要在应用程序中至少调用一次 injectTypeface 方法。如果它记录异常,我会对细节感兴趣。我只在运行 Android 6.0 的 LG G4 上对此进行了测试(当时我正在做自己的一些研究),我希望它不适用于所有版本的 Android。 @RossBradbury 它在某些设备上不起作用.. 大多数设备都可以正常工作,但有些设备不接受这个 fontfamily.. 我测试过的设备 - lenova a7000 型号 更新:github.com/chrisjenx/Calligraphy 是一个出色的解决方案。【参考方案7】:

在 assets 中创建一个字体文件夹并在其中添加所有需要的字体。

public class CustomTextView extends TextView 
    private static final String TAG = "TextView";

    public CustomTextView(Context context) 
        super(context);
    

    public CustomTextView(Context context, AttributeSet attrs) 
        super(context, attrs);
        setCustomFont(context, attrs);
    

    public CustomTextView(Context context, AttributeSet attrs, int defStyle) 
        super(context, attrs, defStyle);
        setCustomFont(context, attrs);
    

    private void setCustomFont(Context ctx, AttributeSet attrs) 
        TypedArray a = ctx.obtainStyledAttributes(attrs, R.styleable.CustomTextView);
        String customFont = a.getString(R.styleable.CustomTextView_customFont);
        setCustomFont(ctx, customFont);
        a.recycle();
    

    public boolean setCustomFont(Context ctx, String fontName) 
        Typeface typeface = null;
        try 
            if(fontName == null)
                fontName = Constants.DEFAULT_FONT_NAME;
            
            typeface = Typeface.createFromAsset(ctx.getAssets(), "fonts/" + fontName);
         catch (Exception e) 
            Log.e(TAG, "Unable to load typeface: "+e.getMessage());
            return false;
        
        setTypeface(typeface);
        return true;
    

并在 attrs.xml

中添加一个可声明的
<declare-styleable name="CustomTextView">
      <attr name="customFont" format="string"/>
</declare-styleable>

然后添加您的 customFont 之类的

app:customFont="arial.ttf"

【讨论】:

你也可以自定义上面的类来使用这里的样式。github.com/leok7v/android-textview-custom-fonts/blob/master/res/…【参考方案8】:

我知道这是一个老问题,但我找到了一个更简单的解决方案。

首先像往常一样在 xml 中声明您的 TextView。 将您的字体(TTF 或 TTC)放入资产文件夹中

app\src\main\assets\

然后只需在 onCreate 方法中设置文本视图的字体即可。

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

    TextView textView = findViewById(R.id.my_textView);
    Typeface typeface = Typeface.createFromAsset(getAssets(), "fontName.ttf");
    textView.setTypeface(typeface);

完成。

【讨论】:

问题明确指出要在 xml 文件中使用它,而不是以编程方式【参考方案9】:

最好的解决方案是使用(最终)由 Google 引入的 XML 中的原生自定义字体功能。但是您必须针对 API 26。它支持 API 16+

https://developer.android.com/guide/topics/ui/look-and-feel/fonts-in-xml

【讨论】:

【参考方案10】:

而不是 xmlns:custom="schemas.android.com/tools";你应该使用:xmlns:custom="schemas.android.com/apk/res-auto";为了使用可样式化的属性。 我做了这个改变,它现在正在工作。

【讨论】:

【参考方案11】:

现在您可以在 XML 中设置字体而无需添加任何其他类,例如:

android:fontFamily="@font_folder/font_file"

【讨论】:

以上是关于在使用xml的android TextView中使用自定义字体的主要内容,如果未能解决你的问题,请参考以下文章

android 换行符( ) 在TextView中显示不正常的问题

禁用 EditText 可编辑性和焦点(如 TextView)

如何在listview android中使文本颜色透明?

在使用xml的android TextView中使用自定义字体

无法在我的 android 应用程序中使 proguard 工作

在 swift 中使 URL 链接属性和可点击以及可编辑的 Textview