Android 自定义和可下载字体

原文:Custom and Downloadable Fonts on Android
作者:Ivan Kust
译者:kmyhy

Android 一开始的时候,没有使用自定义字体的开箱即用解决方案。设备上只能使用少部分预装字体。

你必须要做一些创新,并为这种小事情编写大量的代码。

最近,Google 为 Android 8.0 推出了自定义和可下载字体。他们还通过Support Library 版本 26 提供了对早期 Android 版本的支持。

在本教程中,你将通过创建一个简单的 demo 来了解如何使用它们。在此过程中,您将学习:

  • 如何为 app 添加自定义字体
  • 如何定义字族
  • 如何从提供者添加一个可下载字体
  • 如何检索字体学习

让我们开始吧!:]

注:本教程假设你了解用 Kotlin 开发 Android 程序的基础。如果你是一个新手,可以看一下我们的 Kotlin 入门教程。如果你是 Android 开发新手,请先看看我们的 Android教程

历史:不一样的过去

一直到最近,要在视图上使用自定义字体,必须进行以下操作:

  1. 将字体文件放到 assets 文件夹
  2. 从 asset 文件将字体加载为 Typeface 对象
  3. 用 setTypeface() 设置视图的 Typeface

这种方式的主要缺点在于你在布局文件中设置字体——你只能使用代码:

val myTypeface = Typeface.createFromAsset(assets, "fonts/myFont.ttf")
myTextView.typeface = myTypeface

要解决这种情况,你可以扩展这个视图类并添加一个自定义属性,用于从布局中传递字体文件。这是很好,但仍然存在一些问题:

  • 你必须扩展每个想使用自定义字体的 View
  • 有的设备加载 assets 时会花很长时间,因此你必须将字体缓存到内存中
  • 某些情况下会出现问题——例如如果你必须修改 toolbar 上的字体时

更不必说,仅仅为了设置一个自定义字体就必须扩展一个 TextView 的感觉很不好。

开始

前提条件

要使用自定义字体,首先必须安装最新的 AndroidStudio3.x。这一点很重要,因为 AndroidStudio2.x 不支持某些特性,例如字体资源目录。

安装 AndroidStudio 时,请按照我们在 Android 开发教程中所说的去做。

您的第一个任务是切换到最新的支持库。您可以在这里下载开始项目

打开 AndroidStudio 并用 File\Open 导入开始项目。选择开始项目的解压缩位置,然后单击对话框上的 Open。

当项目打开并同步时,build& run。

你会看到一个简单的 Font Quiz app。如果你开始答题,你会被提 5 个问题,让你猜出文本所用的字体。

等等——这些文字显示的都是同一个(默认)字体!我们后面会解决这个问题。首先来添加一个最新支持库。

添加最新支持库

打开 \app 文件夹下的 build.gradle 文件(在 Android project 视图中,你可以在 Gradle Scripts 文件夹下找到它),然后修改其中的 dependecies{…} 一行:

implementation ‘com.android.support:support-v4:27.0.2’

这将添加最新版本的 Android 支持库,该支持库将使 API 14 以后支持自定义字体和可下载字体。

您还需要将 compileSdkVersion 和 targetSdkVersion 更改为27。最后,将 other support library dependencies(即 appcompat 和 design) 更改为27.0.2版本。

然后,点击编辑器窗口顶端 gradle 通知中的 Sync now。

当 gradle 同步完,build & run,看看是否正常:

看起来没有任何改变,但是我们已经为自定义字体的添加做好了准备!

把字体打包

谷歌在 Android 8 推出了字体资源的新特性。将字体文件放入res\fonts 文件夹中,就能将其已资源方式打包到 .apk 中。这些字体会编译到 R 文件中,同时可以在 Android Studio 中可以像字符串、drawable 和 color 资源一样使用。

注:字体资源只能在 Android Studio 3.x 中使用,而不能在 2.x 中使用。

接下来你要做的就是给应用程序添加一个自定义的 .ttf 字体。在这里下载OpenSans-regular 字体。

回到 Android Studio,选择 project 导航器中的 Android:

点击 res 文件夹,按 cmd+N(或者 File\New)然后选择 Directory。

弹出一个对话框,要你输入目录名,输入 font:

现在,右键单击新目录,然后单击“在Finder中显示”(MacOS)、“在资源管理器中显示”(Windows)或“在文件中显示”(Linux)。将下载的 OpenSans-Regular.ttf 文件移到您打开的字体文件夹中,并将其重命名为opensans_regular.ttf。在Android资源名只能使用字母数字和下划线。

回到 Android Studio 并打开res\layout\activity_main.xml文件。找到id 为 tvFonQuiz 的 AppCompatTextView。向其添加以下属性:

app:fontFamily="@font/opensans_regular"

text view 的布局代码现在是这个样子:

<android.support.v7.widget.AppCompatTextView
  android:id="@+id/tvMessage"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:text="@string/quiz_message"
  android:textAlignment="center"
  app:fontFamily="@font/opensans_regular"
  app:layout_constraintBottom_toTopOf="@+id/startButton"
  app:layout_constraintLeft_toLeftOf="parent"
  app:layout_constraintRight_toRightOf="parent"
  app:layout_constraintTop_toTopOf="parent" />

Build & run。

你可以看到文字消息变成了 OpenSans 字体。真简单!

创建字族

另一个新功能是,您可以创建字族,用于包含一系列字体文件,以及它们的样式和字重。要创建一个新的字族,您实际上需要创建一个新的 XML 字体资源。好处是您可以作为一个整体访问它,而不是将每个样式和字重的单个字体文件引用为单独的资源。

你现在可以创建一个新字族。首先,来创建 OpenSans 的粗体

重复上面的步骤,将新的字体文件添加到项目的字体文件夹中。将文件重命名为opensans_bold.ttf。

接下来创建一个字族资源。

点击 res\font 文件夹,按 ⌘N (或者 File\New) 并选择 Font resource file。

在 File name 栏输入 opensans 然后点击 OK。

Android studio 会生成一个空的字族资源:

<?xml version="1.0" encoding="utf-8"?>
<font-family xmlns:android="http://schemas.android.com/apk/res/android">

</font-family>

标签添加两个字体文件到字族中。这个标签有 3 个属性:

  • font: 字体文件的资源 ID
  • fontStyle: 字体文件的风格,可以是 normal 或 italic
  • fontWeight: 字重

为了添加 regular 和 italic 字体,你需要用两个 元素:

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

注意,为了向后兼容 8.0 以下 Android 版本,还必须以命名空间 app 来声明所有字体属性。这将使用支持库的自定义字体实现。添加它们之后,您的资源文件应该如下所示:

<?xml version="1.0" encoding="utf-8"?>
<font-family xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <font
        android:font="@font/opensans_regular"
        android:fontStyle="normal"
        android:fontWeight="400"
        app:fontFamily="@font/opensans_regular"
        app:fontStyle="normal"
        app:fontWeight="400" />
    <font
        android:font="@font/opensans_bold"
        android:fontStyle="italic"
        android:fontWeight="400"
        app:font="@font/opensans_bold"
        app:fontStyle="italic"
        app:fontWeight="400" />
</font-family>

现在回到 res/layout/activity_main.xml 修改 tvMessage 的 app:fontFamily 属性为 opensans:

<android.support.v7.widget.AppCompatTextView
  android:id="@+id/tvMessage"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:text="@string/quiz_message"
  android:textAlignment="center"
  app:fontFamily="@font/opensans"
  app:layout_constraintBottom_toTopOf="@+id/startButton"
  app:layout_constraintLeft_toLeftOf="parent"
  app:layout_constraintRight_toRightOf="parent"
  app:layout_constraintTop_toTopOf="parent" />

Build & run。

在布局中使用自定义字体

在前面的步骤中,您已经看到了如何向 TextView 添加自定义字体。现在,您将向主题添加自定义字体,更改所有使用该主题的 activity 的默认字体。

打开文件 res/values/styles.xml。

修改 app 的 Theme.FontQuiz ——加入 fontFamily 属性:

<style name="Theme.FontQuiz" parent="Theme.AppCompat.Light.DarkActionBar">
    <item name="colorPrimary">@color/colorPrimary</item>
    <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
    <item name="colorAccent">@color/colorAccent</item>
    <item name="fontFamily">@font/opensans</item>
</style>

Build & run。

您可以看到在整个应用程序中都使用了 OpenSans:

在代码中使用自定义字体

您也可以通过代码来设置自定义字体。为此,您将用到支持库中的 ResourcesCompat 类。在 MainActivity 的 onCreate()方法的末尾输入:

val typeface = ResourcesCompat.getFont(this, R.font.opensans_bold)
startButton.typeface = typeface

Build & run。

你可以看到开始按钮被设置为 OpenSans 粗体。

注意,您使用支持库来支持小于 Android 8.0 的版本。

可下载字体

你已经了解了自定义字体的工作方式,那么让我们跳到另一个新知识点,可下载字体。可下载字体允许你按需下载或在应用程序启动时向应用程序添加字体。

它还有其它好处:

  • 只在需要时才下载字体
  • 减少 apk 的大小
  • 通过字体提供者在多个 app 共享字体

字体提供者的机制

字体提供者负责检索和缓存跨应用程序使用的可下载字体。请求字体的过程如下:

所有使用可下载字体的应用程序都通过 FontsContractCompat 传递它们的请求。然后,它与请求的字体提供者进行通信。字体提供者是负责获取和缓存适当字体的应用程序。可以在设备上安装多个字体提供者,但目前只有 Google 字体提供者可用。

安全和证书

为了在使用字体提供者时确保安全,你必须提供字体提供者的签名证书。这样 Android 才能够校验字体提供者的身份。你必须在设备未预安装的字体提供者时或使用支持库时这样做。

接下来的任务是添加 Google 字体提供者的证书。

点击 res\values 文件夹,按 ⌘N (或 File\New) 并选择 Values resource file。

在对话框中,命名为 font_certs 然后点 Ok。

以字符串数组的形式指定字体提供者证书。如果字体提供者有多个证书集,则必须定义一个字符串数组的数组。支持库所用的 Google 字体提供者使用两组证书,下一步是为每个组证书定义一个数组。

在新文件中添加一个字符串数组,在“”节中添加一个 ,命名为 com_google_android_gms_fonts_certs_dev。

为它添加一个 item,内容如下:

<item>
MIIEqDCCA5CgAwIBAgIJANWFuGx90071MA0GCSqGSIb3DQEBBAUAMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTAeFw0wODA0MTUyMzM2NTZaFw0zNTA5MDEyMzM2NTZaMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTCCASAwDQYJKoZIhvcNAQEBBQADggENADCCAQgCggEBANbOLggKv+IxTdGNs8/TGFy0PTP6DHThvbbR24kT9ixcOd9W+EaBPWW+wPPKQmsHxajtWjmQwWfna8mZuSeJS48LIgAZlKkpFeVyxW0qMBujb8X8ETrWy550NaFtI6t9+u7hZeTfHwqNvacKhp1RbE6dBRGWynwMVX8XW8N1+UjFaq6GCJukT4qmpN2afb8sCjUigq0GuMwYXrFVee74bQgLHWGJwPmvmLHC69EH6kWr22ijx4OKXlSIx2xT1AsSHee70w5iDBiK4aph27yH3TxkXy9V89TDdexAcKk/cVHYNnDBapcavl7y0RiQ4biu8ymM8Ga/nmzhRKya6G0cGw8CAQOjgfwwgfkwHQYDVR0OBBYEFI0cxb6VTEM8YYY6FbBMvAPyT+CyMIHJBgNVHSMEgcEwgb6AFI0cxb6VTEM8YYY6FbBMvAPyT+CyoYGapIGXMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbYIJANWFuGx90071MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADggEBABnTDPEF+3iSP0wNfdIjIz1AlnrPzgAIHVvXxunW7SBrDhEglQZBbKJEk5kT0mtKoOD1JMrSu1xuTKEBahWRbqHsXclaXjoBADb0kkjVEJu/Lh5hgYZnOjvlba8Ld7HCKePCVePoTJBdI4fvugnL8TsgK05aIskyY0hKI9L8KfqfGTl1lzOv2KoWD0KWwtAWPoGChZxmQ+nBli+gwYMzM1vAkP+aayLe0a1EQimlOalO762r0GXO0ks+UeXde2Z4e+8S/pf7pITEI/tP+MxJTALw9QUWEv9lKTk+jkbqxbsh8nfBUapfKqYn0eidpwq2AzVp3juYl7//fKnaPhJD9gs=
</item>

另外添加一个 string arry,名为 com_google_android_gms_fonts_certs_prod 然后为它添加一个 item ,内容如下:



<item>
MIIEQzCCAyugAwIBAgIJAMLgh0ZkSjCNMA0GCSqGSIb3DQEBBAUAMHQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtHb29nbGUgSW5jLjEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDAeFw0wODA4MjEyMzEzMzRaFw0zNjAxMDcyMzEzMzRaMHQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtHb29nbGUgSW5jLjEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDCCASAwDQYJKoZIhvcNAQEBBQADggENADCCAQgCggEBAKtWLgDYO6IIrgqWbxJOKdoR8qtW0I9Y4sypEwPpt1TTcvZApxsdyxMJZ2JORland2qSGT2y5b+3JKkedxiLDmpHpDsz2WCbdxgxRczfey5YZnTJ4VZbH0xqWVW/8lGmPav5xVwnIiJS6HXk+BVKZF+JcWjAsb/GEuq/eFdpuzSqeYTcfi6idkyugwfYwXFU1+5fZKUaRKYCwkkFQVfcAs1fXA5V+++FGfvjJ/CxURaSxaBvGdGDhfXE28LWuT9ozCl5xw4Yq5OGazvV24mZVSoOO0yZ31j7kYvtwYK6NeADwbSxDdJEqO4k//0zOHKrUiGYXtqw/A0LFFtqoZKFjnkCAQOjgdkwgdYwHQYDVR0OBBYEFMd9jMIhF1Ylmn/Tgt9r45jk14alMIGmBgNVHSMEgZ4wgZuAFMd9jMIhF1Ylmn/Tgt9r45jk14aloXikdjB0MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEUMBIGA1UEChMLR29vZ2xlIEluYy4xEDAOBgNVBAsTB0FuZHJvaWQxEDAOBgNVBAMTB0FuZHJvaWSCCQDC4IdGZEowjTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBAUAA4IBAQBt0lLO74UwLDYKqs6Tm8/yzKkEu116FmH4rkaymUIE0P9KaMftGlMexFlaYjzmB2OxZyl6euNXEsQH8gjwyxCUKRJNexBiGcCEyj6z+a1fuHHvkiaai+KL8W1EyNmgjmyy8AW7P+LLlkR+ho5zEHatRbM/YAnqGcFh5iZBqpknHf1SKMXFh4dd239FJ1jWYfbMDMy3NS5CTMQ2XFI1MvcyUTdZPErjQfTbQe3aDQsQcafEQPD+nqActifKZ0Np0IS9L9kR/wbNvyz6ENwPiTrjV2KRkEjH78ZMcUQXg0L3BYHJ3lc69Vs5Ddf9uUGGMYldX3WfMBEmh/9iFBDAaTCK
</item>

最后,创建一个数组,名为 com_google_android_gms_fonts_certs,将之前定义的两个 string arrays 作为其 item.

你的 font_certs.xml 文件变成这个样子:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <array name="com_google_android_gms_fonts_certs">
        <item>@array/com_google_android_gms_fonts_certs_dev</item>
        <item>@array/com_google_android_gms_fonts_certs_prod</item>
    </array>
    <string-array name="com_google_android_gms_fonts_certs_dev">
        <item>
            MIIEqDCCA5CgAwIBA…
        </item>
    </string-array>
    <string-array name="com_google_android_gms_fonts_certs_prod">
        <item>
            MIIEQzCCAyugAwIBAgIJAMLgh0…
        </item>
    </string-array>
</resources>

Build & run。

没有任何变化,但已经为可下载字体准备好了。

在代码中使用可下载字体

FontQuiz app 还缺少一个重要的功能——问题中的文本应该只使用提问到的字体。

你可以用一个请求来获取可下载的字体,并将它们以代码方式应用到 View 中。必须使用支持库中的 FontsContractCompat 类来支持 8.0 以下的 Android 版本。

你的任务将是使用它来请求和设置一个随机字体的问答页面。

打开 QuestionActivity 的 showFont() 方法。

用于测验的字族名位于 res\value\family_names.xml 文件中的一个 list 中。选择一个随机字体的问题和对应 4 个选项的代码已经写好。你的任务是请求并显示传递给 showFont() 的字体。

首先,隐藏所有按钮并显示一个 ProgressView,表示字体正在被加载:

buttons.forEach { button -> button.isEnabled = false }
progressView.visibility = View.VISIBLE

Build & run。

点击 “Start Quiz” ,当第一个问题出来时,你会看到所有按钮禁用,加载进度显示。

接下来该创建字体请求。

创建一个字体请求

接下来的任务是添加一个请求用于下载字体到 QuestionActivity。

创建一个 query 字串,以及一个可下载字体的请求:

val query = "name=$familyName"
val request = FontRequest(
  "com.google.android.gms.fonts",
  "com.google.android.gms",
  query,
  R.array.com_google_android_gms_fonts_certs
)

注:确认你使用的是来自 android.support.v4.provider 包的FontRequest类。android.provider 包的和支持库不兼容。

创建 FontRequest 时,你必须传入这些参数:

  • provider authority – 目前为止只能使用 Google 提供者 com.google.android.gms.fonts
  • provider package – 对于google 的字体提供者,包名是 com.google.android.gms
  • query - 你所请求的字体的查询字符串
  • 证书数组 – 用于校验提供者

要请求一个新字体,需要使用 FontsContractCompat 的 requestFont() 方法。在 showFont() 最后一句加入:

FontsContractCompat.requestFont(
    this,
    request,
    object : FontsContractCompat.FontRequestCallback() {
      override fun onTypefaceRetrieved(typeface: Typeface?) {

      }

      override fun onTypefaceRequestFailed(reason: Int) {

      }
    },
    handler
)

请求可下载字体是异步操作。 requestFont() 方法过一个 FontsContractCompat.FontRequestCallback 接口返回结果。如果请求成功, FontContractorCompat 会调用 onTypefaceRetreived() 方法。用传回的 Typeface 对象设置 View 的字体。启用所有按钮并隐藏进度指示器:

override fun onTypefaceRetrieved(typeface: Typeface?) {
  buttons.forEach { button -> button.isEnabled = true }

  progressView.visibility = View.INVISIBLE
  fontTextView.typeface = typeface
}

如果请求错误,FontContractorCompat 会调用 onTypefaceRequestFailed()。通过向 showError() 传递一个错误代码来显示错误信息。

override fun onTypefaceRequestFailed(reason: Int) {
  showError(reason)
}

请求字体时的最后一件东西是一个 Handler 实例。

注:简而言之,处理程序使你可以将代码放到不同的线程执行。

FentContractorCompat 用它来在于 Handler 关联的线程检索字体。确保您提供的 Handle 与UI线程无关。

val handlerThread = HandlerThread("fonts")
handlerThread.start()

handler = Handler(handlerThread.looper)

为了方便起见,创建一个私有字段,该属性负责保存这个 handler,通过这个属性初始化和检索 handler:

private var handler: Handler? = null

private val handlerThreadHandler: Handler
  get() {
    if (handler == null) {
      val handlerThread = HandlerThread("fonts")
      handlerThread.start()
      handler = Handler(handlerThread.looper)
    }

    return handler ?: throw AssertionError("Set to null by another thread")
  }

第一次使用这个 handlerThreadHandler 属性时将初始化 handler 并返回它。

现在 showFont() 最后一句变成这个样子:

FontsContractCompat.requestFont(
    this,
    request,
    object : FontsContractCompat.FontRequestCallback() {
      override fun onTypefaceRetrieved(typeface: Typeface?) {
        buttons.forEach { button -> button.isEnabled = true }

        progressView.visibility = View.INVISIBLE
        fontTextView.typeface = typeface
      }

      override fun onTypefaceRequestFailed(reason: Int) {
        showError(reason)
      }
    },
    handlerThreadHandler
)

Build& run。开始答题:

现在你会在每个问题上看到对应的字体!:]

检索字体信息

当你回答一个问题后,显示一个简单的问题所指字体的信息是一个不错的主意。因此,你的下一个任务是检索有关字族的信息。

找到 QuestionActivity 的 loadFontFact() 方法。

要获得字体信息,需要用 FontsContractCompact 的 fetchFonts() 方法。和之前一样,首先创建一个 FontRequest:

val query = "name=$familyName"
val request = FontRequest(
  "com.google.android.gms.fonts",
  "com.google.android.gms",
  query,
  R.array.com_google_android_gms_fonts_certs
)

然后传递给 fetchFonts() 方法。

val result = FontsContractCompat.fetchFonts(this@QuestionActivity, null, request)

如果指定名称有效,这将返回所请求字族的信息。您将在返回的对象的字体数组中查找它。你可以从返回对象中检索字体数组。

注:不像 requestFont(),fetchFonts() 是同步的。它会在调用线程中执行并返回有效字体的信息。

每个字体有以下属性:

  • uri – 字体提供者中该字体文件的 URI
  • ttcIndex – 如果提供一个 TTC_INDEX 文件,则指向该索引,否则返回 0
  • weight – 字重,整数值
  • italic – 如果字体风格为 italic,返回 true

检查 result 的状态码,如果为 ok 则显示该字体的字重:

if (result.statusCode == FontsContractCompat.FontFamilyResult.STATUS_OK) {
  with(textView) {
    text = getString(R.string.correct_answer_message, familyName, result.fonts[0].weight)
    visibility = View.VISIBLE
  }
}

字符串 R.string.correct_answer_message 已经有值了,并且它有一个代表字重的整型参数。

获取字体数据是一个阻塞操作,应该在后台执行。使用 kotlin 的 doAsync 和 uiThread 块在后台线程上执行它:

doAsync {
  val query = "name=$familyName"
  val request = FontRequest(
    "com.google.android.gms.fonts",
    "com.google.android.gms",
    query,
    R.array.com_google_android_gms_fonts_certs
  )

  val result = FontsContractCompat.fetchFonts(this@QuestionActivity, null, request)

  if (result.statusCode == FontsContractCompat.FontFamilyResult.STATUS_OK) {
    uiThread {

      with(textView) {
        text = getString(R.string.correct_answer_message, familyName, result.fonts[0].weight)
        visibility = View.VISIBLE
      }
    }
  } 
}

最后,进行错误处理并隐藏进度指示器。loadFontFact() 的最终代码如下:

progressView.visibility = View.INVISIBLE

doAsync {
  val query = "name=$familyName"
  val request = FontRequest(
      "com.google.android.gms.fonts",
      "com.google.android.gms",
      query,
      R.array.com_google_android_gms_fonts_certs
  )

  val result = FontsContractCompat.fetchFonts(this@QuestionActivity, null, request)

  if (result.statusCode == FontsContractCompat.FontFamilyResult.STATUS_OK) {
    uiThread {
      progressView.visibility = View.GONE

      with(textView) {
        text = getString(R.string.correct_answer_message, familyName, result.fonts[0].weight)
        visibility = View.VISIBLE
      }
    }
  } else {
    uiThread {
      showError(result.statusCode)
    }
  }
}

Build & run。回答一个问题,你会看到关于该字体的信息。

XML 资源的可下载字体

你还可以将可下载字体定义为 XML 资源。

在 res\font 文件夹右键点击。选择 New\Font resource file。在对话框中,将文件名输入为 acme。

为 元素添加字体相关属性:

<?xml version="1.0" encoding="utf-8"?>
<font-family xmlns:app="http://schemas.android.com/apk/res-auto"
  app:fontProviderAuthority="com.google.android.gms.fonts"
  app:fontProviderPackage="com.google.android.gms"
  app:fontProviderQuery="Acme"
  app:fontProviderCerts="@array/com_google_android_gms_fonts_certs">
</font-family>

这些内容不陌生。是你设置的这些属性是前面你传递给 requestFont() 的参数。只不过这次是放到 XML 中了。

引用创建的字体资源,就像使用字族列和 .ttf 文件一样。打开res\activity_main.xml 布局文件,并将 acme 字体设置给 TextView tvFontQuiz:

<android.support.v7.widget.AppCompatTextView
  android:id="@+id/tvFontQuiz"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:text="@string/app_name"
  android:textColor="@android:color/black"
  android:textSize="@dimen/logo_text_size"
  android:textStyle="bold"
  app:fontFamily="@font/acme"
  app:layout_constraintRight_toRightOf="parent"
  app:layout_constraintLeft_toLeftOf="parent"               
  app:layout_constraintTop_toBottomOf="@+id/horizontalGuideline" />

重复这个过程,添加 Bilbo Swash Caps 字体。

打开 res/activity_main.xml,将 TextView tvTheGreat 的 font 属性设为 bilbo_swash_caps:

<android.support.v7.widget.AppCompatTextView
  android:id="@+id/tvTheGreat"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:text="@string/the_great"
  android:textColor="@color/colorAccent"
  android:textSize="@dimen/logo_text_size_small"
  app:fontFamily="@font/bilbo_swash_caps"
  app:layout_constraintBottom_toTopOf="@+id/horizontalGuideline"
  app:layout_constraintLeft_toLeftOf="parent"
  app:layout_constraintRight_toRightOf="parent" />

Build & run:

现在看到 FontQuiz 标签的字体了吗?第一次运行 app 时,你会注意字体在显示前有一个下载过程,因为它还没有下载并缓存。

在 manifest 中提前声明字体

还有一个意外之喜——你可以指定让 Android 在 App 启动前预加载的字体!为此,你必须在 manifest 中进行声明。

点击 res\values 文件夹,按 ⌘N (或者 File\New) ,选择 Values resource file。

在对话框中命名文件为 preloaded_fonts。

在 resources 标签下添加字体数组,name 设为 preloaded_fonts:

<array name="preloaded_fonts" translatable="false">
  <item>@font/acme</item>
  <item>@font/bilbo_swash_caps</item>
</array>

打开 manifests\AndroidManifest.xml 在 标签中添加 元素:

<meta-data android:name="preloaded_fonts" android:resource="@array/preloaded_fonts" />

Build & run:

呵!你的字体现在是预加载的了,只要应用程序一启动就可以使用。

你创建了一个漂亮的 Font Quiz app——来玩一下吧!你能 5 题全对吗?:]

接下来做什么

这里是具有完整代码的最终项目

选择你知道如何向 app 中添加自定义和可下载的字体了。

google的官方文档是一个查找更多信息的好地方:font resourcesFonts in XMLDownloadable fonts

如果你想看看 Google 字体提供者都有哪些字体,请看这里

如果有任何问题或其他自定义和可下载字体的使用技巧,请在下面留言!

猜你喜欢

转载自blog.csdn.net/kmyhy/article/details/80492530