自定义字体和XML布局(Android)


174

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

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

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


68
DrDefrost-请接受一些答案,您将在该网站上获得更多回复。
SK9

1
这是另一个类似的问题:stackoverflow.com/questions/9030204/…–
Vins,

2017年5月5日更新:“支持库26.0 Beta在运行Android API版本14和更高版本的设备上为XML字体功能提供支持。” 请参阅:developer.android.com/preview/features/...
阿尔伯特ç布朗

Answers:


220

您可以扩展TextView来设置自定义字体,如我在此处了解的那样。

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>

main.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” 。

更新13年8月1日

此方法存在严重的内存问题。请参阅下面的chedabob评论


5
看起来不错,但是,当我尝试在main.xml中使用“ TextViewPlus”时出现错误。我得到以下信息:-错误:XML解析错误:未绑定的前缀-与元素类型“ supportLibs.TextViewPlus”相关联的属性“ foo:customFont”的前缀“ foo”未绑定。
Majjoodi 2011年

78
需要注意的一件事是,这将生成数十个TypeFace对象并消耗内存。4.0之前的Android中存在一个错误,无法正确释放TypeFaces。最简单的方法是使用HashMap创建TypeFace缓存。这使我的应用程序中的内存使用量从120+ mb降至18mb。 code.google.com/p/android/issues/detail?id=9904
chedabob 2012年

7
@Majjoodi:如果您忘记将第二个名称空间添加到布局中,通常会发生这种情况:xmlns:foo="http://schemas.android.com/apk/res/com.example
Theo

11
非常重要-我只想强调chedabob的建议。除非您遵循它,否则在pre-ICS中将发生内存泄漏。解决方案很少,其中一个在chedabob提供的链接中,另一个在这里:stackoverflow.com/questions/8057010/listview-memory-leak。彼得-请更新你的答案-这是伟大的,但不完全
米哈尔ķ

1
除了宽度和高度等其他textview属性外,如何在styles.xml中设置此自定义属性?
loeschg 2012年

35

我参加聚会晚了3年:(但是,这对于可能偶然发现这篇文章的人可能会有用。

我已经编写了一个库,它可以缓存字体,还可以让您直接从XML指定自定义字体。你可以在这里找到图书馆。

这是您使用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'但是没有用。
aimango 2014年

1
您将需要一个AAR才能以这种方式使用它。还没有呢 您现在可以复制源并构建一个Android Studio库项目。
Ragunath Jawahar 2014年

2
您的geekui标签来自哪里?
sefirosu 2014年

3
从父标记中指定的名称空间中xmlns:geekui="http://schemas.android.com/apk/res-auto"
Ragunath Jawahar 2014年

1
@MuhammedRefaat,是的,它是:)
Ragunath Jawahar

18

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

TypeFace类:

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

自定义TextView:

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

似乎可以在运行时工作,但是否也应该在设计器中工作?
Magnus Johansson

当然。您可以将其用作xml。@Magnus
莱昂纳多·卡多佐

我认为您误解了,它似乎在设计时不起作用(Designer预览)。(xml!=设计器)。在编译到运行时的Xml布局文件中指定的状态下,它可以正常工作。无论如何,我正在使用此扩展名github.com/danh32/Fontify,它可以更好地满足我的需要(它支持多种字体样式,常规,粗体等,以及不同的字体名称以及TextView之外的其他控件)
Magnus Johansson,

2
getTypeFace在每次调用时都会创建一个新字体...这违背了单例的目的。它应该具有一个静态字段,该字段在首次调用时设置,并在后续调用中返回该字段的值。
史蒂文·佩纳

1
@chiwai你是对的!关于第一个问题,它不需要它。大约第二次是错字。
莱昂纳多·卡多佐

13

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

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

激活它唯一要做的就是将其附加到您正在使用的活动的上下文中:

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

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

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


8

使用数据绑定

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


7

如果您只想添加一种字体,并且想要编写更少的代码,则可以为您的特定字体创建一个专用的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 2013年

@ericosg当我使用yourthis解决方案时出现此错误android.view.InflateException:二进制XML文件第9行:错误膨胀类com.ascent.adwad.utils.CustomTextView
Sagar Devanga 2014年

@SagarDevanga,如果没有更多信息,很难提供帮助。也许将它带到尽可能远的地方并提出一个新问题。
ericosg 2014年

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

从文档开始

使用字体

所有步骤都是正确的。


1
最好的答案!字体文件名必须小写。
德米特里(Dmitry)'18年

您还可以创建一个应用于您的应用程序的自定义样式(在AndroidManifest文件下),并将其分配给所有视图。而不是例如仅将其放置在单个TextView上,因为这不会影响工具栏。
goldenmaza

5

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


4

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

请记住,Android在资源非常有限的设备上运行,并且字体可能需要大量的RAM。内置的Droid字体是特制的,并且,请注意,缺少许多字符和装饰。


“请记住,Android在资源非常有限的设备上运行”->这种情况越来越少。四核手机?真??
桑迪2012年

9
我会做更多的事情来说明对tareqHs答案的上下文的考虑,这比您的评论早了两年半。
SK9 2012年

当您在2010年写下答案时,答案的第一部分可能是正确的。后者是多余的,更重要的是:Droid很糟糕,在2012年被Google抛弃了Roboto。Android排版中缺乏选择是一个缺陷,而不是功能。自2008年以来,iOS已按照当今的标准在原始设备下为开发人员提供了多种字体。
cosmix 2013年

该答案不再有效。
Martin Konecny 2014年

2

对于该问题,我可能会得到一个简单的答案,而无需扩展TextView并实现长长的代码。

代码:

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

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


2

也可以在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和这个文件夹位于同一水平res,并src在我的情况下,其assets/fonts/ionicons.ttf

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

更新2忘记了我之前安装的称为Calligraphy的库


这对我不起作用。当我尝试构建此文件时,收到错误消息:Error:(49,5)找不到与给定名称匹配的资源:attr'fontPath'。
Stef

尝试添加xmlns:app="http://schemas.android.com/apk/res-auto"到您的根目录布局中,同时也检查更新的答案
norman784

该错误不是来自布局XML文件,而是来自样式XML文件。似乎它并不“知道” fontPath是什么。
Stef

您的权利,忘了我有一个叫做Calligrapy的图书馆
norman784

1

Peter的答案是最好的,但是可以通过使用Android的styles.xml为应用程序中的所有textview自定义字体来改善。

我的代码在这里


1

有两种自定义字体的方法:

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

方式1: Refrection Typeface.class ||| 最好的办法

在类扩展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;
    }

}


0

您可以轻松地自定义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值 int fontValue = attrs.getAttributeIntValue("http://schemas.android.com/apk/res-auto","Lato-Regular.ttf",-1)

要么

我们还可以从在xml中设置的视图中获取getTypeface()(android:textStyle="bold|normal|italic")中)。因此,无论您要做什么。

现在,我们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);
        }
    }
}

0

知道从Android 8.0(API级别26)开始可以使用XML中的自定义字体可能会很有用。

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

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

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



By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.