【问题标题】:Custom fonts and XML layouts (Android)自定义字体和 XML 布局 (Android)
【发布时间】:2011-01-23 11:48:03
【问题描述】:

我正在尝试在 Android 中使用 XML 文件定义 GUI 布局。据我所知,没有办法指定您的小部件应在 XML 文件中使用自定义字体(例如您放置在 assets/font/ 中的字体),您只能使用系统安装的字体。

我知道,在 Java 代码中,我可以使用唯一 ID 手动更改每个小部件的字体。或者,我可以遍历 Java 中的所有小部件以进行此更改,但这可能会非常慢。

我还有什么其他选择?有没有更好的方法来制作具有自定义外观的小部件?我不想为我添加的每个新小部件手动更改字体。

【问题讨论】:

标签: java android user-interface interface fonts


【解决方案1】:

您可以扩展 TextView 以设置自定义字体,正如我所学 here

TextViewPlus.java:

package com.example;

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

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

    public TextViewPlus(Context context) {
        super(context);
    }

    public TextViewPlus(Context context, AttributeSet attrs) {
        super(context, attrs);
        setCustomFont(context, attrs);
    }

    public TextViewPlus(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.TextViewPlus);
        String customFont = a.getString(R.styleable.TextViewPlus_customFont);
        setCustomFont(ctx, customFont);
        a.recycle();
    }

    public boolean setCustomFont(Context ctx, String asset) {
        Typeface tf = null;
        try {
        tf = Typeface.createFromAsset(ctx.getAssets(), asset);  
        } catch (Exception e) {
            Log.e(TAG, "Could not get typeface: "+e.getMessage());
            return false;
        }

        setTypeface(tf);  
        return true;
    }

}

attrs.xml:(在 res/values 中)

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

ma​​in.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:foo="http://schemas.android.com/apk/res/com.example"
    android:orientation="vertical" android:layout_width="fill_parent"
    android:layout_height="fill_parent">

    <com.example.TextViewPlus
        android:id="@+id/textViewPlus1"
        android:layout_height="match_parent"
        android:layout_width="match_parent"
        android:text="@string/showingOffTheNewTypeface"
        foo:customFont="saxmono.ttf">
    </com.example.TextViewPlus>
</LinearLayout>

您可以将“saxmono.ttf”放在 assets 文件夹中。

2013 年 8 月 1 日更新

这种方法存在严重的内存问题。请参阅下面的chedabob's comment

【讨论】:

  • 看起来不错,但是,当我尝试在 main.xml 中使用“TextViewPlus”时出现错误。我得到以下信息: - 错误:解析 XML 时出错:未绑定前缀 - 与元素类型“supportLibs.TextViewPlus”关联的属性“foo:customFont”的前缀“foo”未绑定。
  • 需要注意的是,这会生成几十个、几十个 TypeFace 对象,并且会占用内存。 4.0 之前的 Android 中存在一个错误,无法正确释放 TypeFaces。最简单的做法是使用 HashMap 创建 TypeFace 缓存。这使我的应用程序中的内存使用量从 120+ mb 下降到 18mb。 code.google.com/p/android/issues/detail?id=9904
  • @Majjoodi:如果您忘记将第二个命名空间添加到布局中,通常会发生这种情况:xmlns:foo="http://schemas.android.com/apk/res/com.example
  • 非常重要 - 我只想强调 chedabob 的建议。除非您遵循它,否则您将在 ICS 之前出现内存泄漏。解决方案很少,其中一个在 chedabob 提供的链接中,另一个在这里:stackoverflow.com/questions/8057010/listview-memory-leak。彼得 - 请更新你的答案 - 很好但不完整
  • 除了宽度和高度等其他 textview 属性外,如何在 styles.xml 中设置此自定义属性?
【解决方案2】:

我迟到了 3 年 :( 但这对于可能偶然发现这篇文章的人来说可能很有用。

我编写了一个缓存字体的库,还允许您直接从 XML 中指定自定义字体。你可以找到图书馆here

这是您使用 XML 布局时的样子。

<com.mobsandgeeks.ui.TypefaceTextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/hello_world"
    geekui:customTypeface="fonts/custom_font.ttf" />

【讨论】:

  • 嘿@Ragunath-jawahar,我将如何为 gradle 项目导入库?我试过compile 'com.mobsandgeeks:android-typeface-textview:1.0',但没用。
  • 您需要一个 AAR 才能以这种方式使用它。它还没有。您现在可以复制源代码并构建 Android Studio 库项目。
  • 你的 geekui 标签来自哪里?
  • 来自父标签中指定的命名空间 - xmlns:geekui="http://schemas.android.com/apk/res-auto"
  • @MuhammedRefaat,是的:)
【解决方案3】:

这可能有点晚了,但您需要创建一个返回自定义字体的单例类以避免内存泄漏。

字体类:

public class OpenSans {

private static OpenSans instance;
private static Typeface typeface;

public static OpenSans getInstance(Context context) {
    synchronized (OpenSans.class) {
        if (instance == null) {
            instance = new OpenSans();
            typeface = Typeface.createFromAsset(context.getResources().getAssets(), "open_sans.ttf");
        }
        return instance;
    }
}

public Typeface getTypeFace() {
    return typeface;
}
}

自定义文本视图:

public class NativelyCustomTextView extends TextView {

    public NativelyCustomTextView(Context context) {
        super(context);
        setTypeface(OpenSans.getInstance(context).getTypeFace());
    }

    public NativelyCustomTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
        setTypeface(OpenSans.getInstance(context).getTypeFace());
    }

    public NativelyCustomTextView(Context context, AttributeSet attrs,
            int defStyle) {
        super(context, attrs, defStyle);
        setTypeface(OpenSans.getInstance(context).getTypeFace());
    }

}

通过 xml:

<com.yourpackage.views.NativelyCustomTextView
            android:id="@+id/natively_text_view"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerHorizontal="true"
            android:layout_margin="20dp"
            android:text="@string/natively"
            android:textSize="30sp" /> 

以编程方式:

TextView programmaticallyTextView = (TextView) 
       findViewById(R.id.programmatically_text_view);

programmaticallyTextView.setTypeface(OpenSans.getInstance(this)
                .getTypeFace());

【讨论】:

  • 似乎可以在运行时工作,但它应该也可以在设计器中工作吗?
  • 当然。您可以用作 xml。 @马格努斯
  • 我想你误会了,它似乎在 Design 时间(设计器预览)中不起作用。 (xml!=设计师)。在编译到运行时的 Xml 布局文件中指定它可以正常工作。无论如何,我正在使用这个扩展程序github.com/danh32/Fontify,它可以更好地满足我的需求(它支持多种字体样式,常规、粗体等,以及不同的字体名称以及 TextView 之外的其他控件)
  • getTypeFace 在每次调用时都会创建一个新字体……这违背了单例的目的。它应该有一个静态字段,在第一次调用时设置并在后续调用中返回该字段的值。
  • @chiwai 你是对的!关于第一个问题,它不需要它。大约在第二个是一个错字。
【解决方案4】:

老问题,但我确实希望在我开始自己寻找一个好的解决方案之前在这里阅读这个答案。 Calligraphy 扩展了 android:fontFamily 属性以添加对资产文件夹中自定义字体的支持,如下所示:

<TextView 
  android:text="@string/hello_world"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:fontFamily="fonts/Roboto-Bold.ttf"/>

您唯一需要做的就是将其附加到您正在使用的 Activity 的上下文中:

@Override
protected void attachBaseContext(Context newBase) {
    super.attachBaseContext(new CalligraphyContextWrapper(newBase));
}

您还可以指定自己的自定义属性来替换android:fontFamily

它也适用于主题,包括 AppTheme。

【讨论】:

    【解决方案5】:

    使用数据绑定

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

    在 XML 中:

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

    字体文件必须在assets/fonts/

    【讨论】:

      【解决方案6】:

      如果您只想添加一种字体,并且想要编写更少的代码,您可以为您的特定字体创建一个专用的 TextView。请参阅下面的代码。

      package com.yourpackage;
      import android.content.Context;
      import android.graphics.Typeface;
      import android.util.AttributeSet;
      import android.widget.TextView;
      
      public class FontTextView extends TextView {
          public static Typeface FONT_NAME;
      
      
          public FontTextView(Context context) {
              super(context);
              if(FONT_NAME == null) FONT_NAME = Typeface.createFromAsset(context.getAssets(), "fonts/FontName.otf");
              this.setTypeface(FONT_NAME);
          }
          public FontTextView(Context context, AttributeSet attrs) {
              super(context, attrs);
              if(FONT_NAME == null) FONT_NAME = Typeface.createFromAsset(context.getAssets(), "fonts/FontName.otf");
              this.setTypeface(FONT_NAME);
          }
          public FontTextView(Context context, AttributeSet attrs, int defStyle) {
              super(context, attrs, defStyle);
              if(FONT_NAME == null) FONT_NAME = Typeface.createFromAsset(context.getAssets(), "fonts/FontName.otf");
              this.setTypeface(FONT_NAME);
          }
      }
      

      在 main.xml 中,您现在可以像这样添加您的 textView:

      <com.yourpackage.FontTextView
          android:id="@+id/tvTimer"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:text="" />
      

      【讨论】:

      • 在 init() 上执行此操作并为自己节省 3 倍的相同调用。
      • @ericosg 当我使用 yourthis 解决方案时出现此错误 android.view.InflateException: Binary XML file line #9: Error inflating class com.ascent.adwad.utils.CustomTextView
      • @SagarDevanga,如果没有更多信息,很难提供帮助。也许尽可能地提出一个新问题。
      【解决方案7】:

      从 Android O 预览版开始,最好的方法就是这样
      1.)右击res文件夹,进入新建> Android资源目录。新的
      出现资源目录窗口。
      2.)在资源类型列表中,选择字体,然后点击确定。
      3.)在字体文件夹中添加您的字体文件。下面的文件夹结构生成 R.font.dancing_script、R.font.la_la 和 R.font.ba_ba。
      4.)双击字体文件以在编辑器中预览文件的字体。

      接下来我们必须创建一个字体系列

      1.)右键单击字体文件夹并转到新建>字体资源文件。将出现“新建资源文件”窗口。
      2.)输入文件名,然后单击确定。新字体资源 XML 在编辑器中打开。
      3.)在字体标签元素中包含每个字体文件、样式和粗细属性。以下 XML 说明了在字体资源 XML 中添加字体相关属性:

      <?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/hey_regular" />
          <font
          android:fontStyle="italic"
          android:fontWeight="400"
          android:font="@font/hey_bababa" />
      </font-family>
      

      向 TextView 添加字体:

         <TextView
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          **android:fontFamily="@font/ba_ba"**/>
      

      根据文档

      Working With Fonts

      所有步骤都正确。

      【讨论】:

      • 最佳答案!字体文件名必须小写。
      • 您还可以创建一个自定义样式,应用于您的应用程序(在 AndroidManifest 文件下),并将其提供给所有视图。而不是仅仅将它放在单个 TextView 上,因为这不会影响工具栏。
      【解决方案8】:

      扩展TextView 并给它一个自定义属性,或者只使用 android:tag 属性传入一个你想使用的字体的字符串。您将需要选择一个约定并遵守它,例如我会将所有字体放在 res/assets/fonts/ 文件夹中,以便您的 TextView 类知道在哪里可以找到它们。然后在您的构造函数中,您只需在超级调用之后手动设置字体。

      【讨论】:

        【解决方案9】:

        使用自定义字体的唯一方法是通过源代码。

        请记住,Android 在资源和字体非常有限的设备上运行可能需要大量 RAM。内置的 Droid 字体是特制的,如果您注意的话,会丢失许多字符和装饰。

        【讨论】:

        • “请记住,Android 运行在资源非常有限的设备上” --> 这种情况越来越少。四核手机?真的吗??
        • 我会做更多的工作来考虑 tareqHs 回答的背景,这比您的评论早了两年半。
        • 当您在 2010 年写下您的答案时,您回复的第一部分可能是正确的。后者是多余的,而且题外话:Droid 很糟糕,在 2012 年被 Google 抛弃,转而支持 Roboto。 Android 中缺乏排版选择是缺陷,而不是功能;自 2008 年以来,iOS 已经为开发人员提供了多种字体,按照今天的标准在原始设备下使用。
        • 这个答案不再有效。
        【解决方案10】:

        我可能对这个问题有一个简单的答案,而无需扩展 TextView 并实现长代码。

        代码:

         TextView tv = (TextView) findViewById(R.id.textview1);
            tv.setTypeface(Typeface.createFromAsset(getAssets(), "font.ttf"));
        

        像往常一样将自定义字体文件放在 assets 文件夹中,然后试试这个。这个对我有用。 我只是不明白为什么彼得为这个简单的事情给出了这么大的代码,或者他在旧版本中给出了答案。

        【讨论】:

          【解决方案11】:

          也可以在xml中定义而无需创建自定义类

          style.xml

          <style name="ionicons" parent="android:TextAppearance">
              <!-- Custom Attr-->
              <item name="fontPath">fonts/ionicons.ttf</item>
          </style>
          

          activity_main.xml

          <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                        xmlns:app="http://schemas.android.com/apk/res-auto"
                        android:layout_width="match_parent"
                        android:layout_height="match_parent"
                        android:orientation="vertical" >
              <Button
                  android:layout_width="wrap_content"
                  android:layout_height="wrap_content"
                  android:textAppearance="@style/ionicons"
                  android:text=""/>
          </LinearLayout>
          

          一个简短的说明,因为我总是忘记把字体放在哪里,它的字体必须在 assets 中,并且这个文件夹位于 ressrc 的同一级别,在我的以assets/fonts/ionicons.ttf

          为例

          更新添加了根布局,因为此方法需要xmlns:app="http://schemas.android.com/apk/res-auto" 才能工作

          Update 2忘记了我之前安装的一个名为Calligraphy的库

          【讨论】:

          • 这对我不起作用。当我尝试构建它时,我收到错误消息:错误:(49、5)找不到与给定名称匹配的资源:attr 'fontPath'。
          • 尝试将xmlns:app="http://schemas.android.com/apk/res-auto" 添加到您的根布局中,同时检查更新后的答案
          • 错误不是来自布局 XML 文件,而是来自样式 XML 文件。似乎它不“知道”什么是 fontPath。
          • 你对,忘了我有一个叫 Calligrapy 的库
          【解决方案12】:

          Peter 的答案是最好的,但可以通过使用 Android 中的 styles.xml 来为应用中的所有文本视图自定义字体来改进它。

          我的代码是here

          【讨论】:

            【解决方案13】:

            自定义字体有两种方式:

            !!!我在 assets/fonts/iran_sans.ttf 中的自定义字体

            方式一: 反射字体.class ||| 最好的方法

            在类extends Application中调用FontsOverride.setDefaultFont(),这段代码会导致所有软件字体发生变化,甚至Toasts字体

            AppController.java

            public class AppController extends Application {
            
                @Override
                public void onCreate() {
                    super.onCreate();
            
                    //Initial Font
                    FontsOverride.setDefaultFont(getApplicationContext(), "MONOSPACE", "fonts/iran_sans.ttf");
            
                }
            }
            

            FontsOverride.java

            public class FontsOverride {
            
                public static void setDefaultFont(Context context, String staticTypefaceFieldName, String fontAssetName) {
                    final Typeface regular = Typeface.createFromAsset(context.getAssets(), fontAssetName);
                    replaceFont(staticTypefaceFieldName, regular);
                }
            
                private 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();
                    }
                }
            }
            

            方式2:使用setTypeface

            对于特殊视图只需调用 setTypeface() 来更改字体。

            CTextView.java

            public class CTextView extends TextView {
            
                public CTextView(Context context) {
                    super(context);
                    init(context,null);
                }
            
                public CTextView(Context context, @Nullable AttributeSet attrs) {
                    super(context, attrs);
                    init(context,attrs);
                }
            
                public CTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
                    super(context, attrs, defStyleAttr);
                    init(context,attrs);
                }
            
                @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
                public CTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
                    super(context, attrs, defStyleAttr, defStyleRes);
                    init(context,attrs);
                }
            
                public void init(Context context, @Nullable AttributeSet attrs) {
            
                    if (isInEditMode())
                        return;
            
                    // use setTypeface for change font this view
                    setTypeface(FontUtils.getTypeface("fonts/iran_sans.ttf"));
            
                }
            }
            

            FontUtils.java

            public class FontUtils {
            
                private static Hashtable<String, Typeface> fontCache = new Hashtable<>();
            
                public static Typeface getTypeface(String fontName) {
                    Typeface tf = fontCache.get(fontName);
                    if (tf == null) {
                        try {
                            tf = Typeface.createFromAsset(AppController.getInstance().getApplicationContext().getAssets(), fontName);
                        } catch (Exception e) {
                            e.printStackTrace();
                            return null;
                        }
                        fontCache.put(fontName, tf);
                    }
                    return tf;
                }
            
            }
            

            【讨论】:

              【解决方案14】:

              这是一个教程,向您展示如何设置自定义字体,如 @peter 所述:http://responsiveandroid.com/2012/03/15/custom-fonts-in-android-widgets.html

              它还考虑了潜在的内存泄漏 ala http://code.google.com/p/android/issues/detail?id=9904 。本教程中还有一个在按钮上设置自定义字体的示例。

              【讨论】:

                【解决方案15】:

                您可以轻松自定义 textview 类:-

                所以你首先需要做的是,创建Custom textview 类,它使用AppCompatTextView 扩展。

                public class CustomTextView extends AppCompatTextView {
                    private int mFont = FontUtils.FONTS_NORMAL;
                    boolean fontApplied;
                
                    public CustomTextView(Context context, AttributeSet attrs, int defStyle) {
                        super(context, attrs, defStyle);
                        init(attrs, context);
                    }
                
                    public CustomTextView(Context context, AttributeSet attrs) {
                        super(context, attrs);
                        init(attrs, context);
                    }
                
                    public CustomTextView(Context context) {
                        super(context);
                        init(null, context);
                    }
                
                    protected void init(AttributeSet attrs, Context cxt) {
                        if (!fontApplied) {
                            if (attrs != null) {
                                mFont = attrs.getAttributeIntValue(
                                        "http://schemas.android.com/apk/res-auto", "Lato-Regular.ttf",
                                        -1);
                            }
                            Typeface typeface = getTypeface();
                            int typefaceStyle = Typeface.NORMAL;
                            if (typeface != null) {
                                typefaceStyle = typeface.getStyle();
                            }
                            if (mFont > FontUtils.FONTS) {
                                typefaceStyle = mFont;
                            }
                            FontUtils.applyFont(this, typefaceStyle);
                            fontApplied = true;
                        }
                    }
                }
                

                现在,每次自定义文本视图调用时,我们都会从属性int fontValue = attrs.getAttributeIntValue("http://schemas.android.com/apk/res-auto","Lato-Regular.ttf",-1) 中获取 int 值。

                或者

                我们还可以从我们在 xml (android:textStyle="bold|normal|italic") 中设置的视图中获取 getTypeface()。所以做任何你想做的事。

                现在,我们使用FontUtils 将任何 .ttf 字体设置到我们的视图中。

                public class FontUtils {
                
                    public static final int FONTS = 1;
                    public static final int FONTS_NORMAL = 2;
                    public static final int FONTS_BOLD = 3;
                    public static final int FONTS_BOLD1 = 4;
                
                    private static Map<String, Typeface> TYPEFACE = new HashMap<String, Typeface>();
                
                    static Typeface getFonts(Context context, String name) {
                        Typeface typeface = TYPEFACE.get(name);
                        if (typeface == null) {
                            typeface = Typeface.createFromAsset(context.getAssets(), name);
                            TYPEFACE.put(name, typeface);
                        }
                        return typeface;
                    }
                
                    public static void applyFont(TextView tv, int typefaceStyle) {
                
                        Context cxt = tv.getContext();
                        Typeface typeface;
                
                        if(typefaceStyle == Typeface.BOLD_ITALIC) {
                            typeface = FontUtils.getFonts(cxt, "FaktPro-Normal.ttf");
                        }else if (typefaceStyle == Typeface.BOLD || typefaceStyle == SD_FONTS_BOLD|| typefaceStyle == FONTS_BOLD1) {
                            typeface = FontUtils.getFonts(cxt, "FaktPro-SemiBold.ttf");
                        } else if (typefaceStyle == Typeface.ITALIC) {
                            typeface = FontUtils.getFonts(cxt, "FaktPro-Thin.ttf");
                        } else {
                            typeface = FontUtils.getFonts(cxt, "FaktPro-Normal.ttf");
                        }
                        if (typeface != null) {
                            tv.setTypeface(typeface);
                        }
                    }
                }
                

                【讨论】:

                  【解决方案16】:

                  知道从 Android 8.0(API 级别 26)开始您可以use a custom font in XML 可能会很有用。

                  您可以通过以下方式将自定义字体应用到整个应用程序。

                  1. 将字体放入文件夹res/font

                  2. res/values/styles.xml 在应用程序主题中使用它。 <style name="AppTheme" parent="{whatever you like}"> <item name="android:fontFamily">@font/myfont</item> </style>

                  【讨论】:

                    【解决方案17】:

                    Fontinator 是一个 Android 库,可以轻松使用自定义字体。 https://github.com/svendvd/Fontinator

                    【讨论】:

                      【解决方案18】:

                      您不能扩展 TextView 来创建小部件或在小部件布局中使用一个: http://developer.android.com/guide/topics/appwidgets/index.html

                      【讨论】:

                      • 啊哈——著名的“widget”这个词的双重用法又来了!
                      猜你喜欢
                      • 1970-01-01
                      • 2013-01-13
                      • 2011-04-21
                      • 2012-07-16
                      • 2023-03-14
                      • 1970-01-01
                      • 1970-01-01
                      • 2014-06-25
                      • 2016-09-06
                      相关资源
                      最近更新 更多