概述
从 API 级别 26 开始,Android SDK 允许将字体视为资源,就像布局或绘图对象一样。 Android 支持库 26 NuGet 会将新的字体 API 移植到面向 API 级别 14 或更高版本的应用。
定位 API 26 或安装 Android 支持库 v26 后,可通过两种方法在 Android 应用程序中使用字体:
- 将字体打包为 Android 资源 - 这可确保字体始终可供应用程序使用,但会增大 APK 的大小。
- 下载字体 – Android 还支持从 字体提供程序下载字体。 如果字体已在设备上,则字体提供程序检查。 如有必要,将在设备上下载并缓存字体。 此字体可以在多个应用程序之间共享。
类似的字体(或可能具有多种不同样式的字体)可以分组为 字体系列。 这样,开发人员就可以指定字体的某些属性,例如字体粗细,Android 会自动从字体系列中选择相应的字体。
Android 支持库 v26 将支持字体支持到 API 级别 26。 面向较旧的 API 级别时,必须声明 app
XML 命名空间,并使用命名空间和app:
命名空间命名各种字体属性android:
。 如果仅 android:
使用命名空间,则不会显示运行 API 级别 25 或更低级别的设备的字体。 例如,此 XML 代码片段声明一个新的 字体系列 资源,该资源将在 API 级别 14 及更高版本下工作:
<?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/sourcesanspro_regular"
android:fontStyle="normal"
android:fontWeight="400"
app:font="@font/sourcesanspro_regular"
app:fontStyle="normal"
app:fontWeight="400" />
</font-family>
只要字体以正确的方式提供给 Android 应用程序,就可以通过设置 fontFamily
属性将其应用于 UI 小组件。 例如,以下代码片段演示如何在 TextView 中显示字体:
<TextView
android:text="The quick brown fox jumped over the lazy dog."
android:fontFamily="@font/sourcesanspro_regular"
app:fontFamily="@font/sourcesanspro_regular"
android:textAppearance="?android:attr/textAppearanceLarge"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
本指南将首先讨论如何将字体用作 Android 资源,然后继续讨论如何在运行时下载字体。
字体作为资源
将字体打包到 Android APK 中可确保它始终可供应用程序使用。 字体文件(任一 .TTF 或 .OTF 文件将像任何其他资源一样添加到 Xamarin.Android 应用程序,方法是将文件复制到 Xamarin.Android 项目的 Resources 文件夹中的子目录。 字体资源保存在项目的 Resources 文件夹的字体子目录中。
注意
字体应具有 AndroidResource 的生成操作,否则不会打包到最终 APK 中。 生成操作应由 IDE 自动设置。
当有许多类似的字体文件(例如,具有不同粗细或样式的相同字体)时,可以将它们分组到字体系列中。
字体系列
字体系列是一组具有不同粗细和样式的字体。 例如,可能有单独的字体文件用于粗体或斜体字体。 字体系列由font
保存在 Resources/font 目录中的 XML 文件中的元素定义。 每个字体系列都应有自己的 XML 文件。
若要创建字体系列,请先将所有字体添加到 Resources/font 文件夹中。 然后在字体系列的字体文件夹中创建新的 XML 文件。 XML 文件的名称与所引用的字体没有关联或关系;资源文件可以是任何合法的 Android 资源文件名。 此 XML 文件将具有包含一个或多个font
元素的根font-family
元素。 每个 font
元素声明字体的属性。
以下 XML 是 Sources Sans Pro 字体的字体系列示例,用于定义许多不同的字体粗细。 这在名为sourcesanspro.xml的资源/字体文件夹中保存为文件:
<?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/sourcesanspro_regular"
android:fontStyle="normal"
android:fontWeight="400"
app:font="@font/sourcesanspro_regular"
app:fontStyle="normal"
app:fontWeight="400" />
<font android:font="@font/sourcesanspro_bold"
android:fontStyle="normal"
android:fontWeight="800"
app:font="@font/sourcesanspro_bold"
app:fontStyle="normal"
app:fontWeight="800" />
<font android:font="@font/sourcesanspro_italic"
android:fontStyle="italic"
android:fontWeight="400"
app:font="@font/sourcesanspro_italic"
app:fontStyle="italic"
app:fontWeight="400" />
</font-family>
该 fontStyle
属性有两个可能的值:
- normal – 普通字体
- 斜体 - 斜体字体
该 fontWeight
属性对应于 CSS font-weight
属性,并引用字体的粗细。 这是 100 - 900 范围内的值。 以下列表描述了常见的字体粗细值及其名称:
- 瘦 – 100
- 额外浅 色 – 200
- 浅 色 – 300
- 普通 – 400
- 中 – 500
- 半粗体 – 600
- 粗体 – 700
- 额外粗体 – 800
- 黑色 – 900
定义字体系列后,可以通过在布局文件中设置fontFamily
和textStyle
fontWeight
属性来以声明方式使用字体系列。 例如,以下 XML 代码片段设置 600 粗字体(普通)和斜体文本样式:
<TextView
android:text="Sans Source Pro semi-bold italic, 600 weight, italic"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fontFamily="@font/sourcesanspro"
android:textAppearance="?android:attr/textAppearanceLarge"
android:gravity="center_horizontal"
android:fontWeight="600"
android:textStyle="italic"
/>
以编程方式分配字体
可以使用方法检索Typeface
对象,以编程方式设置Resources.GetFont
字体。 许多视图都有一个 TypeFace
属性,可用于将字体分配给小组件。 此代码片段演示如何以编程方式在 TextView 上设置字体:
Android.Graphics.Typeface typeface = this.Resources.GetFont(Resource.Font.caveat_regular);
textView1.Typeface = typeface;
textView1.Text = "Changed the font";
该方法 GetFont
将自动加载字体系列中的第一个字体。 若要加载与特定样式匹配的字体,请使用 Typeface.Create
该方法。 此方法将尝试加载与指定样式匹配的字体。 例如,此代码片段将尝试从资源/字体中定义的字体系列加载粗体Typeface
对象:
var typeface = Typeface.Create("<FONT FAMILY NAME>", Android.Graphics.TypefaceStyle.Bold);
textView1.Typeface = typeface;
下载字体
Android 可以从远程源下载字体,而不是将字体打包为应用程序资源。 这将产生降低 APK 大小所需的效果。
字体在字体提供商的帮助下下载。 这是一个专门的内容提供程序,用于管理将字体下载和缓存到设备上的所有应用程序。 Android 8.0 包括用于从 Google 字体存储库下载字体的字体提供程序。 此默认字体提供程序使用 Android 支持库 v26 将回移植到 API 级别 14。
当应用请求字体时,字体提供程序将首先检查以查看字体是否已在设备上。 否则,它将尝试下载字体。 如果无法下载字体,则 Android 将使用默认系统字体。 下载字体后,它可供设备上的所有应用程序使用,而不仅仅是发出初始请求的应用。
当请求下载字体时,应用不会直接查询字体提供程序。 相反,应用将使用 API 的 FontsContract
实例(或者 FontsContractCompat
正在使用支持库 26)。
Android 8.0 支持以两种不同的方式下载字体:
- 将可下载字体声明为资源 - 应用可以通过 XML 资源文件将可下载字体声明到 Android。 这些文件将包含 Android 在应用启动时异步下载字体并在设备上缓存字体所需的所有元数据。
- 以编程方式 – Android API 级别 26 中的 API 允许应用程序以编程方式下载字体,同时运行应用程序。 应用将为给定字体创建一个
FontRequest
对象,并将此对象传递给FontsContract
该类。 获取FontsContract
字体提供程序中的字体并从中检索字体。FontRequest
Android 将同步下载字体。 本指南稍后会显示创建示例FontRequest
。
无论使用哪种方法,都必须将资源文件添加到 Xamarin.Android 应用程序,然后才能下载字体。 首先,必须在 Resources/font 目录中的 XML 文件中声明字体 (s)作为字体系列的一部分。 此代码片段是如何使用 Android 8.0 附带的默认字体提供程序(或支持库 v26)从 Google Fonts 开放源代码集合 下载字体的示例:
<?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"
android:fontProviderAuthority="com.google.android.gms.fonts"
android:fontProviderPackage="com.google.android.gms"
android:fontProviderQuery="VT323"
android:fontProviderCerts="@array/com_google_android_gms_fonts_certs"
app:fontProviderAuthority="com.google.android.gms.fonts"
app:fontProviderPackage="com.google.android.gms"
app:fontProviderQuery="VT323"
app:fontProviderCerts="@array/com_google_android_gms_fonts_certs"
>
</font-family>
该 font-family
元素包含以下属性,声明 Android 下载字体所需的信息:
- fontProviderAuthority – 要用于请求的字体提供程序的授权。
- fontPackage – 要用于请求的字体提供程序的包。 这用于验证提供程序的标识。
- fontQuery – 这是一个字符串,可帮助字体提供程序找到请求的字体。 字体查询的详细信息特定于字体提供程序。
QueryBuilder
可下载字体示例应用中的类提供有关 Google Fonts 开放源代码集合中字体的查询格式的一些信息。 - fontProviderCerts – 一个资源数组,其中包含提供程序应为其签名的证书的哈希集的列表。
定义字体后,可能需要提供有关 下载所涉及的字体证书 的信息。
字体证书
如果未在设备上预安装字体提供程序,或者应用正在使用 Xamarin.Android.Support.Compat
库,则 Android 需要字体提供程序的安全证书。 这些证书将列在资源 /值 目录中的数组资源文件中。
例如,以下 XML 名为 Resources/values/fonts_cert.xml ,并存储 Google 字体提供程序的证书:
<?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>
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-array>
<string-array name="com_google_android_gms_fonts_certs_prod">
<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>
</string-array>
</resources>
有了这些资源文件,应用就能够下载字体。
将可下载字体声明为资源
通过在AndroidManifest.XML中列出可下载字体,Android 将在应用首次启动时异步下载字体。 字体本身列在数组资源文件中,如下所示:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<array name="downloadable_fonts" translatable="false">
<item>@font/vt323</item>
</array>
</resources>
若要下载这些字体,必须通过添加meta-data
为元素的application
子元素在AndroidManifest.XML中声明这些字体。 例如,如果在资源/值/downloadable_fonts.xml的资源文件中声明可下载字体,则必须将此代码片段添加到清单:
<meta-data android:name="downloadable_fonts" android:resource="@array/downloadable_fonts" />
使用字体 API 下载字体
可以通过实例化 FontRequest
对象并将其传递给 FontContractCompat.RequestFont
方法,以编程方式下载字体。 该方法FontContractCompat.RequestFont
首先检查以查看设备上是否存在字体,然后如有必要,将异步查询字体提供程序并尝试下载应用的字体。 如果 FontRequest
无法下载字体,则 Android 将使用默认系统字体。
对象 FontRequest
包含字体提供程序将用于查找和下载字体的信息。 A FontRequest
需要四段信息:
- 字体提供程序颁发机构 – 要用于请求的字体提供程序的颁发机构。
- 字体包 – 要用于请求的字体提供程序的包。 这用于验证提供程序的标识。
- 字体查询 - 这是一个字符串,可帮助字体提供程序找到请求的字体。 字体查询的详细信息特定于字体提供程序。 字符串的详细信息特定于字体提供程序。
QueryBuilder
可下载字体示例应用中的类提供有关 Google Fonts 开放源代码集合中字体的查询格式的一些信息。 - 字体提供程序证书 - 一个资源数组,其中包含提供程序应为其签名的证书的哈希集的列表。
此代码片段是实例化新 FontRequest
对象的示例:
FontRequest request = new FontRequest("com.google.android.gms.fonts", "com.google.android.gms", <FontToDownload>, Resource.Array.com_google_android_gms_fonts_certs);
在前面的代码片段 FontToDownload
中,查询将帮助 Google Fonts 开源集合中的字体。
在FontRequest
FontContractCompat.RequestFont
传递给方法之前,必须创建两个对象:
FontsContractCompat.FontRequestCallback
– 这是必须扩展的抽象类。 它是将在完成时RequestFont
调用的回调。 Xamarin.Android 应用必须子类FontsContractCompat.FontRequestCallback
并重写OnTypefaceRequestFailed
OnTypefaceRetrieved
,并提供在下载失败或成功时要执行的操作。Handler
– 这是一个Handler
将用于在线程上下载字体(如有必要)的RequestFont
用法。 不应在 UI 线程上下载字体。
此代码片段是 C# 类的一个示例,它将从 Google Fonts 开放源代码集合异步下载字体。 它实现 FontRequestCallback
接口,并在完成后引发 C# 事件 FontRequest
。
public class FontDownloadHelper : FontsContractCompat.FontRequestCallback
{
// A very simple font query; replace as necessary
public static readonly String FontToDownload = "Courgette";
Android.OS.Handler Handler = null;
public event EventHandler<FontDownloadEventArg> FontDownloaded = delegate
{
// just an empty delegate to avoid null reference exceptions.
};
public void DownloadFonts(Context context)
{
FontRequest request = new FontRequest("com.google.android.gms.fonts", "com.google.android.gms",FontToDownload , Resource.Array.com_google_android_gms_fonts_certs);
FontsContractCompat.RequestFont(context, request, this, GetHandlerThreadHandler());
}
public override void OnTypefaceRequestFailed(int reason)
{
base.OnTypefaceRequestFailed(reason);
FontDownloaded(this, new FontDownloadEventArg(null));
}
public override void OnTypefaceRetrieved(Android.Graphics.Typeface typeface)
{
base.OnTypefaceRetrieved(typeface);
FontDownloaded(this, new FontDownloadEventArg(typeface));
}
Handler GetHandlerThreadHandler()
{
if (Handler == null)
{
HandlerThread handlerThread = new HandlerThread("fonts");
handlerThread.Start();
Handler = new Handler(handlerThread.Looper);
}
return Handler;
}
}
public class FontDownloadEventArg : EventArgs
{
public FontDownloadEventArg(Android.Graphics.Typeface typeface)
{
Typeface = typeface;
}
public Android.Graphics.Typeface Typeface { get; private set; }
public bool RequestFailed
{
get
{
return Typeface != null;
}
}
}
若要使用此帮助程序,将创建一个新 FontDownloadHelper
程序,并分配事件处理程序:
var fontHelper = new FontDownloadHelper();
fontHelper.FontDownloaded += (object sender, FontDownloadEventArg e) =>
{
//React to the request
};
fontHelper.DownloadFonts(this); // this is an Android Context instance.
总结
本指南讨论了 Android 8.0 中的新 API,以支持可下载字体和字体作为资源。 它讨论了如何在 APK 中嵌入现有字体并在布局中使用它们。 它还讨论了 Android 8.0 如何支持以编程方式或通过在资源文件中声明字体元数据从字体提供程序下载字体。