如何在Android中更改TextView的字体族?

834

我希望更改Android中的android:fontFamily,但是我没有在Android中看到任何预定义的字体。我该如何选择其中一个预定义的字体?我不需要定义自己的Typeface,但我需要一些与当前显示内容不同的东西。

<TextView
    android:id="@+id/HeaderText"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignParentTop="true"
    android:layout_centerHorizontal="true"
    android:layout_marginTop="52dp"
    android:gravity="center"
    android:text="CallerBlocker"
    android:textSize="40dp"
    android:fontFamily="Arial"
 />

看起来我之前做的那个不会真正起作用!顺便说一下,android:fontFamily="Arial" 是一个愚蠢的尝试!


请查看此链接 https://dev59.com/33E95IYBdhLWcg3wSb7P#48642116 - duggu
39个回答

1749

从 Android 4.1 / 4.2 / 5.0 开始,以下 Roboto 字体系列可用:

android:fontFamily="sans-serif"           // roboto regular
android:fontFamily="sans-serif-light"     // roboto light
android:fontFamily="sans-serif-condensed" // roboto condensed
android:fontFamily="sans-serif-black"     // roboto black
android:fontFamily="sans-serif-thin"      // roboto thin (android 4.2)
android:fontFamily="sans-serif-medium"    // roboto medium (android 5.0)

在与之组合时

in combination with

翻译为“在与之组合时”。
android:textStyle="normal|bold|italic"

这里有16种不同的变体:

  • Roboto正常字体
  • Roboto斜体字
  • Roboto粗体
  • Roboto粗斜体
  • Roboto-Light字体
  • Roboto-Light斜体字
  • Roboto-Thin字体
  • Roboto-Thin斜体字
  • Roboto-Condensed字体
  • Roboto-Condensed斜体字
  • Roboto-Condensed粗体
  • Roboto-Condensed粗斜体
  • Roboto-Black字体
  • Roboto-Black斜体字
  • Roboto-Medium字体
  • Roboto-Medium斜体字

fonts.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="font_family_light">sans-serif-light</string>
    <string name="font_family_medium">sans-serif-medium</string>
    <string name="font_family_regular">sans-serif</string>
    <string name="font_family_condensed">sans-serif-condensed</string>
    <string name="font_family_black">sans-serif-black</string>
    <string name="font_family_thin">sans-serif-thin</string>
</resources>

19
别忘了这个:android:fontFamily="sans-serif-thin" // roboto thin - Sam Lu
6
我在Roboto字体展示册中看到了一个名为“黑色小型大写字母”的变种,但我无法使用它。使用android:fontFamily="sans-serif-black-small-caps"不起作用。有人知道原因吗? - tbruyelle
4
我找不到你在这里输入的任何字体名称,也无法找到 "sans-serif" 这个词组。 - Monty
9
这是一个很好的列表。有没有人有这些信息的来源链接?如果Google能够在TextView的android:fontFamily文档中方便地提供这些信息,那就太好了。 - Christopher Perry
8
可在system_fonts.xml中找到字体的最终列表,如此处所述。 - Newtonx
显示剩余23条评论

283
Android-Studio 3.0 开始,更改字体族非常容易。
使用支持库26,它将在运行Android API版本16及更高版本的设备上工作。
res 目录下创建一个 font 文件夹。下载您想要的字体并将其粘贴到 font 文件夹中。结构应该像下面这样。

Here

注意:从Android Support Library 26.0开始,您必须声明两组属性(android:和app:),以确保您的字体在运行Api 26或更低版本的设备上加载。
现在,您可以在布局中使用以下方法更改字体:
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/dancing_script"
app:fontFamily="@font/dancing_script"/>

通过编程进行更改

 Typeface typeface = getResources().getFont(R.font.myfont);
   //or to support all versions use
Typeface typeface = ResourcesCompat.getFont(context, R.font.myfont);
 textView.setTypeface(typeface);  

使用 styles.xml 更改字体,请创建样式

 <style name="Regular">
        <item name="android:fontFamily">@font/dancing_script</item>
        <item name="fontFamily">@font/dancing_script</item>
        <item name="android:textStyle">normal</item>
 </style>

并将此样式应用于 TextView

  <TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    style="@style/Regular"/>

你也可以创建自己的字体族。
- 右键点击字体文件夹,选择“新建 > 字体资源文件”。新建资源文件窗口将出现。 - 输入文件名,然后点击“确定”。新的字体资源 XML 将在编辑器中打开。 - 在这里编写你自己的字体族,例如。
<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>

这是将特定的字体样式和字体粗细映射到将用于呈现该特定变体的字体资源。fontStyle 的有效值为 normal 或 italic;fontWeight 符合 CSS font-weight specification
1. 要在布局中更改字体族,您可以编写
 <TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:fontFamily="@font/lobster"/>

2. 以编程方式更改

 Typeface typeface = getResources().getFont(R.font.lobster);
   //or to support all versions use
Typeface typeface = ResourcesCompat.getFont(context, R.font.lobster);
 textView.setTypeface(typeface);  

要更改整个应用程序的字体,请在AppTheme中添加以下两行

 <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
     <item name="android:fontFamily">@font/your_font</item>
     <item name="fontFamily">@font/your_font</item>
  </style>

请参阅文档Android自定义字体教程获取更多信息。

7
提示:目前此方法仅适用于Android Studio 3.0预览版,在Android Studio 2.3.3上并未成功。希望这能节省其他人的时间! - Tash Pemhiwa
3
由于无法使用 getResources(),在片段中如何获取字体?请注意,不要改变原始意思。 编辑:你答案末尾的这行代码对我有用: Typeface typeface = ResourcesCompat.getFont(context, R.font.myfont); - Paradox
请注意,在根元素声明中必须包含 xmlns:app="http://schemas.android.com/apk/res-auto",以确保不会出现错误“未绑定命名空间'app'”。 - jungledev
1
右键点击主文件,选择“新建”,选择“Android资源文件”。在弹出窗口中输入“font”作为名称,在下拉的“资源类型”列表中选择“字体”。点击“确定”。 - Androidcoder
@Manohar 如何将其与Textvo结合使用 - Sharan
显示剩余4条评论

244

以下是以编程方式设置字体的方法:

TextView tv = (TextView) findViewById(R.id.appname);
Typeface face = Typeface.createFromAsset(getAssets(),
            "fonts/epimodem.ttf");
tv.setTypeface(face);

将字体文件放在您的资产文件夹中。在我的情况下,我创建了一个名为 fonts 的子目录。

编辑:如果你不知道资产文件夹在哪里,请参考这个问题


41
虽然这样可以运行,但请注意它可能会导致内存泄漏。可以使用此答案进行修复。 - Charles Madere
@ScootrNova 当我使用你的解决方案时,出现了以下错误: 错误:找不到字体资源gothic.ttf。 - Sagar Devanga
如何将此应用于整个应用程序? 目前在示例中,您只在一个textview上应用它。 - Pritish Joshi

110

最近我需要解析 /system/etc/fonts.xml。以下是Lollipop版本中的当前字体族:

╔════╦════════════════════════════╦═════════════════════════════╗
║    ║ FONT FAMILY                ║ TTF FILE                    ║
╠════╬════════════════════════════╬═════════════════════════════╣
║  1 ║ casual                     ║ ComingSoon.ttf              ║
║  2 ║ cursive                    ║ DancingScript-Regular.ttf   ║
║  3 ║ monospace                  ║ DroidSansMono.ttf           ║
║  4 ║ sans-serif                 ║ Roboto-Regular.ttf          ║
║  5 ║ sans-serif-black           ║ Roboto-Black.ttf            ║
║  6 ║ sans-serif-condensed       ║ RobotoCondensed-Regular.ttf ║
║  7 ║ sans-serif-condensed-light ║ RobotoCondensed-Light.ttf   ║
║  8 ║ sans-serif-light           ║ Roboto-Light.ttf            ║
║  9 ║ sans-serif-medium          ║ Roboto-Medium.ttf           ║
║ 10 ║ sans-serif-smallcaps       ║ CarroisGothicSC-Regular.ttf ║
║ 11 ║ sans-serif-thin            ║ Roboto-Thin.ttf             ║
║ 12 ║ serif                      ║ NotoSerif-Regular.ttf       ║
║ 13 ║ serif-monospace            ║ CutiveMono.ttf              ║
╚════╩════════════════════════════╩═════════════════════════════╝

这是解析器(基于FontListParser):

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;

import android.util.Xml;

/**
 * Helper class to get the current font families on an Android device.</p>
 * 
 * Usage:</p> {@code List<SystemFont> fonts = FontListParser.safelyGetSystemFonts();}</p>
 */
public final class FontListParser {

    private static final File FONTS_XML = new File("/system/etc/fonts.xml");

    private static final File SYSTEM_FONTS_XML = new File("/system/etc/system_fonts.xml");

    public static List<SystemFont> getSystemFonts() throws Exception {
        String fontsXml;
        if (FONTS_XML.exists()) {
            fontsXml = FONTS_XML.getAbsolutePath();
        } else if (SYSTEM_FONTS_XML.exists()) {
            fontsXml = SYSTEM_FONTS_XML.getAbsolutePath();
        } else {
            throw new RuntimeException("fonts.xml does not exist on this system");
        }
        Config parser = parse(new FileInputStream(fontsXml));
        List<SystemFont> fonts = new ArrayList<>();

        for (Family family : parser.families) {
            if (family.name != null) {
                Font font = null;
                for (Font f : family.fonts) {
                    font = f;
                    if (f.weight == 400) {
                        break;
                    }
                }
                SystemFont systemFont = new SystemFont(family.name, font.fontName);
                if (fonts.contains(systemFont)) {
                    continue;
                }
                fonts.add(new SystemFont(family.name, font.fontName));
            }
        }

        for (Alias alias : parser.aliases) {
            if (alias.name == null || alias.toName == null || alias.weight == 0) {
                continue;
            }
            for (Family family : parser.families) {
                if (family.name == null || !family.name.equals(alias.toName)) {
                    continue;
                }
                for (Font font : family.fonts) {
                    if (font.weight == alias.weight) {
                        fonts.add(new SystemFont(alias.name, font.fontName));
                        break;
                    }
                }
            }
        }

        if (fonts.isEmpty()) {
            throw new Exception("No system fonts found.");
        }

        Collections.sort(fonts, new Comparator<SystemFont>() {

            @Override
            public int compare(SystemFont font1, SystemFont font2) {
                return font1.name.compareToIgnoreCase(font2.name);
            }

        });

        return fonts;
    }

    public static List<SystemFont> safelyGetSystemFonts() {
        try {
            return getSystemFonts();
        } catch (Exception e) {
            String[][] defaultSystemFonts = {
                    {
                            "cursive", "DancingScript-Regular.ttf"
                    }, {
                            "monospace", "DroidSansMono.ttf"
                    }, {
                            "sans-serif", "Roboto-Regular.ttf"
                    }, {
                            "sans-serif-light", "Roboto-Light.ttf"
                    }, {
                            "sans-serif-medium", "Roboto-Medium.ttf"
                    }, {
                            "sans-serif-black", "Roboto-Black.ttf"
                    }, {
                            "sans-serif-condensed", "RobotoCondensed-Regular.ttf"
                    }, {
                            "sans-serif-thin", "Roboto-Thin.ttf"
                    }, {
                            "serif", "NotoSerif-Regular.ttf"
                    }
            };
            List<SystemFont> fonts = new ArrayList<>();
            for (String[] names : defaultSystemFonts) {
                File file = new File("/system/fonts", names[1]);
                if (file.exists()) {
                    fonts.add(new SystemFont(names[0], file.getAbsolutePath()));
                }
            }
            return fonts;
        }
    }

    /* Parse fallback list (no names) */
    public static Config parse(InputStream in) throws XmlPullParserException, IOException {
        try {
            XmlPullParser parser = Xml.newPullParser();
            parser.setInput(in, null);
            parser.nextTag();
            return readFamilies(parser);
        } finally {
            in.close();
        }
    }

    private static Alias readAlias(XmlPullParser parser) throws XmlPullParserException, IOException {
        Alias alias = new Alias();
        alias.name = parser.getAttributeValue(null, "name");
        alias.toName = parser.getAttributeValue(null, "to");
        String weightStr = parser.getAttributeValue(null, "weight");
        if (weightStr == null) {
            alias.weight = 0;
        } else {
            alias.weight = Integer.parseInt(weightStr);
        }
        skip(parser); // alias tag is empty, ignore any contents and consume end tag
        return alias;
    }

    private static Config readFamilies(XmlPullParser parser) throws XmlPullParserException,
            IOException {
        Config config = new Config();
        parser.require(XmlPullParser.START_TAG, null, "familyset");
        while (parser.next() != XmlPullParser.END_TAG) {
            if (parser.getEventType() != XmlPullParser.START_TAG) {
                continue;
            }
            if (parser.getName().equals("family")) {
                config.families.add(readFamily(parser));
            } else if (parser.getName().equals("alias")) {
                config.aliases.add(readAlias(parser));
            } else {
                skip(parser);
            }
        }
        return config;
    }

    private static Family readFamily(XmlPullParser parser) throws XmlPullParserException,
            IOException {
        String name = parser.getAttributeValue(null, "name");
        String lang = parser.getAttributeValue(null, "lang");
        String variant = parser.getAttributeValue(null, "variant");
        List<Font> fonts = new ArrayList<Font>();
        while (parser.next() != XmlPullParser.END_TAG) {
            if (parser.getEventType() != XmlPullParser.START_TAG) {
                continue;
            }
            String tag = parser.getName();
            if (tag.equals("font")) {
                String weightStr = parser.getAttributeValue(null, "weight");
                int weight = weightStr == null ? 400 : Integer.parseInt(weightStr);
                boolean isItalic = "italic".equals(parser.getAttributeValue(null, "style"));
                String filename = parser.nextText();
                String fullFilename = "/system/fonts/" + filename;
                fonts.add(new Font(fullFilename, weight, isItalic));
            } else {
                skip(parser);
            }
        }
        return new Family(name, fonts, lang, variant);
    }

    private static void skip(XmlPullParser parser) throws XmlPullParserException, IOException {
        int depth = 1;
        while (depth > 0) {
            switch (parser.next()) {
            case XmlPullParser.START_TAG:
                depth++;
                break;
            case XmlPullParser.END_TAG:
                depth--;
                break;
            }
        }
    }

    private FontListParser() {

    }

    public static class Alias {

        public String name;

        public String toName;

        public int weight;
    }

    public static class Config {

        public List<Alias> aliases;

        public List<Family> families;

        Config() {
            families = new ArrayList<Family>();
            aliases = new ArrayList<Alias>();
        }

    }

    public static class Family {

        public List<Font> fonts;

        public String lang;

        public String name;

        public String variant;

        public Family(String name, List<Font> fonts, String lang, String variant) {
            this.name = name;
            this.fonts = fonts;
            this.lang = lang;
            this.variant = variant;
        }

    }

    public static class Font {

        public String fontName;

        public boolean isItalic;

        public int weight;

        Font(String fontName, int weight, boolean isItalic) {
            this.fontName = fontName;
            this.weight = weight;
            this.isItalic = isItalic;
        }

    }

    public static class SystemFont {

        public String name;

        public String path;

        public SystemFont(String name, String path) {
            this.name = name;
            this.path = path;
        }

    }
}

请随意在您的项目中使用上述类。例如,您可以为用户提供字体系列的选择,并根据他们的偏好设置字体。

以下是一个小而不完整的示例:

final List<FontListParser.SystemFont> fonts = FontListParser.safelyGetSystemFonts();
String[] items = new String[fonts.size()];
for (int i = 0; i < fonts.size(); i++) {
    items[i] = fonts.get(i).name;
}

new AlertDialog.Builder(this).setSingleChoiceItems(items, -1, new DialogInterface.OnClickListener() {
    @Override
    public void onClick(DialogInterface dialog, int which) {
        FontListParser.SystemFont selectedFont = fonts.get(which);
        // TODO: do something with the font
        Toast.makeText(getApplicationContext(), selectedFont.path, Toast.LENGTH_LONG).show();
    }
}).show();

你知道安卓的哪个版本添加了哪种字体吗? - android developer
@androiddeveloper 我不知道。你可以通过查看此处的更改来找到答案:https://github.com/android/platform_frameworks_base/blob/lollipop-release/data/fonts/system_fonts.xml - Jared Rummler
1
@Samuel 我有一段时间没有看过这段代码了,但是400字重通常用于“normal”或“regular”字体。例如,Roboto-Regular的字重为400。 - Jared Rummler
这需要root权限或其他什么吗?我在Android模拟器(版本8.1)上运行了这段代码,当我调用getSystemFonts()时,我得到了一个异常org.xmlpull.v1.XmlPullParserException: END_TAG expected (position:START_TAG (empty) <axis tag='wdth' stylevalue='100.0'>@219:51 in java.io.InputStreamReader@f001fb3) - Damn Vegetables
好的,我看到你使用了400作为阈值来确定默认字体。但是为什么是400呢? - coolDude
显示剩余2条评论

52

在Android中,你无法从XML布局设置自定义字体。相反,你必须将特定的字体文件打包到应用程序的assets文件夹中,并以编程方式进行设置。可以使用以下代码:

TextView textView = (TextView) findViewById(<your TextView ID>);
Typeface typeFace = Typeface.createFromAsset(getAssets(), "<file name>");
textView.setTypeface(typeFace);

请注意,只有在调用setContentView()后才能运行此代码。此外,Android仅支持某些字体,这些字体应该是以.ttf (TrueType).otf (OpenType)格式存在的。即使如此,有些字体可能仍无法正常工作。

这个字体肯定在Android上可以使用,如果您的字体文件不受Android支持,您可以使用它来确认您的代码是否正在工作。

Android O更新:现在在Android O中使用XML也成为可能,基于Roger的评论。


Android不允许您从XML布局设置自定义字体。这在Android O中已经改变,它允许您创建自定义字体系列并在XML中应用它们:https://developer.android.com/preview/features/working-with-fonts.html - Roger Huang

33

Kotlin代码 - 设置自定义字体的Textview来自资源文件夹

res -> font -> avenir_next_regular.ttf设置自定义字体

textView!!.typeface = ResourcesCompat.getFont(context!!, R.font.avenir_next_regular)

31

要在程序中设置Roboto字体:

paint.setTypeface(Typeface.create("sans-serif-thin", Typeface.NORMAL));

31
如果你想要以编程方式实现它,你可以使用:

label.setTypeface(Typeface.SANS_SERIF, Typeface.ITALIC);

SANS_SERIF字体可使用以下选项:

  • DEFAULT
  • DEFAULT_BOLD
  • MONOSPACE
  • SANS_SERIF
  • SERIF

ITALIC字体可使用以下选项:

  • BOLD
  • BOLD_ITALIC
  • ITALIC
  • NORMAL

所有内容详见Android开发者网站


26

这与android:typeface相同。

内置字体包括:

  • normal(普通)
  • sans(无衬线)
  • serif(衬线)
  • monospace(等宽)

请参见android:typeface


4
我不认为这是同一件事,但似乎我们不能同时使用它们。现在好像有至少三个不同的属性映射到“setTypeface()”,分别是“fontFamily”、“typeface”和“textStyle”。但我无论如何都搞不清楚它们是如何精确组合以解析具体的Typeface实例的。有人已经搞清楚了吗? Google的文档帮助甚少... - Rad Haring

25
Typeface typeface = ResourcesCompat.getFont(context, R.font.font_name);
textView.setTypeface(typeface);

通过编程轻松地将字体设置为来自res>字体目录的任何textview


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