按需从网络加载字体
load fonts from web on demand
我想让用户在我的 xamarin 表单应用程序中选择字体。我有大约 16 种字体,大小约为 10 MB,我的应用程序大小已经太大,这会导致更大的大小。无论如何,当用户想要更改字体时,我可以下载字体吗?请帮忙
我创建了一个示例,它完全可以满足您的需要。你可以找到源代码HERE。让我解释一下我做了什么。
从架构的角度来看,我们需要做以下事情
从后台下载字体文件
在设备上缓存这些文件
确保它们适用于 iOS 和 Android。在这一步中,我们需要做一些特定于设备的调整以使字体正常工作。对于 iOS 我们需要在 iOS 运行 时间注册字体。对于 Android,我们需要为要使用自定义字体的控件创建自定义渲染器。进一步阅读了解详情。
用于演示功能的演示 ViewModel 和 View。
从代码的角度来看,我已经完成了以下操作来实现上述架构
创建加载字体服务
IFontService
- 在本地设备上下载和安装字体的服务
public interface IFontService
{
//Directory where we'll keep custom fonts
IFolder FontFolder { get; }
//Contains mapping between font name and font file
Dictionary<string, string> Fonts { get; }
//Will download and install custom fonts and instantiate the Fonts mapping property
Task LoadAvailableFonts();
}
实施IFontService
BaseFontService
- 实现了 IFontService
的大部分内容,这不是特定于设备的。它有两个抽象方法,应该实现特定于缓存和安装字体的设备。
//We will cache all the fonts in caches folder, which is device specific,
//That's why we use abstract property which we'll implement separately for iOS and android
//and inject into PCL using dependency injection
protected abstract Task<IFolder> GetCacheFolder();
//For iOS we need to register custom fonts with iOS runtime.
//Check FontService implementation for more details.
protected abstract Task InstallFonts(IEnumerable<CustomFont> fonts);
实现设备特定的东西并使用依赖注入
在 iOS 和 Android 项目中,我创建了 FontService
实现,它派生自 BaseFontService
并实现了设备特定的逻辑 - 字体安装和缓存。
Android
对于Android我们只需要覆盖GetCacheFolder()
,对于Android我们不需要额外的安装步骤,这就是为什么我们可以将InstallFonts()
实现留空。
我们需要对 Android 进行的另一项调整是为那些我们要使用自定义字体的控件自定义渲染器。在 iOS 中不需要此步骤,因为当您注册字体时,您可以在整个应用程序中使用它们。在我的示例中,我创建了 ButtonRenderer
和 LabelRenderer
。他们只是听 FontFamily
属性 的变化,然后尝试使用 IFontService
找到该字体。我创建了一个 FontToolbox
class ,它在 TryGetFont
方法中实现了字体加载逻辑。如果你想支持其他控件的自定义字体,你可以使用与 Button/Label 渲染器类似的模式,并使用 TryGetFont
加载实际字体。
iOS
对于 iOS 来说有点复杂。我们需要从缓存目录中加载字体文件,并使用ios字体管理API注册它们。参考 here 了解更多详情
演示代码
我创建了 FontDemoViewModel,它将调用 IFontService
,将可用字体加载到我们将在视图中绑定的集合中。
我创建了 FontDemoPage,它将允许 select 来自选择器的字体(为此我添加了 BindablePickerEx
控件)并更新按钮和标签的字体。
一些笔记
确保字体文件名与字体的 PostScript 名称匹配。 Android 不需要这个,因为 Android 可以加载任意名称的字体,因为它只需要一个字体文件的引用。但是对于 iOS 你需要确保这是真的,因为 运行-time 通过它们的 PostScript 名称搜索字体。 Here 您可以找到如何获取字体的 PostScript 名称。
另一件事是,在这个示例中,为了简单起见,我将所有字体嵌入到程序集资源中。在我的 BaseFontService
中,我使用此方法从程序集资源加载所有字体
private async Task<List<CustomFont>> DownloadFonts()
对于您的实施,您需要在此处发出实际的 Web 请求以从您的后端下载字体。
我想让用户在我的 xamarin 表单应用程序中选择字体。我有大约 16 种字体,大小约为 10 MB,我的应用程序大小已经太大,这会导致更大的大小。无论如何,当用户想要更改字体时,我可以下载字体吗?请帮忙
我创建了一个示例,它完全可以满足您的需要。你可以找到源代码HERE。让我解释一下我做了什么。
从架构的角度来看,我们需要做以下事情
从后台下载字体文件
在设备上缓存这些文件
确保它们适用于 iOS 和 Android。在这一步中,我们需要做一些特定于设备的调整以使字体正常工作。对于 iOS 我们需要在 iOS 运行 时间注册字体。对于 Android,我们需要为要使用自定义字体的控件创建自定义渲染器。进一步阅读了解详情。
用于演示功能的演示 ViewModel 和 View。
从代码的角度来看,我已经完成了以下操作来实现上述架构
创建加载字体服务
IFontService
- 在本地设备上下载和安装字体的服务
public interface IFontService
{
//Directory where we'll keep custom fonts
IFolder FontFolder { get; }
//Contains mapping between font name and font file
Dictionary<string, string> Fonts { get; }
//Will download and install custom fonts and instantiate the Fonts mapping property
Task LoadAvailableFonts();
}
实施IFontService
BaseFontService
- 实现了 IFontService
的大部分内容,这不是特定于设备的。它有两个抽象方法,应该实现特定于缓存和安装字体的设备。
//We will cache all the fonts in caches folder, which is device specific,
//That's why we use abstract property which we'll implement separately for iOS and android
//and inject into PCL using dependency injection
protected abstract Task<IFolder> GetCacheFolder();
//For iOS we need to register custom fonts with iOS runtime.
//Check FontService implementation for more details.
protected abstract Task InstallFonts(IEnumerable<CustomFont> fonts);
实现设备特定的东西并使用依赖注入
在 iOS 和 Android 项目中,我创建了 FontService
实现,它派生自 BaseFontService
并实现了设备特定的逻辑 - 字体安装和缓存。
Android
对于Android我们只需要覆盖GetCacheFolder()
,对于Android我们不需要额外的安装步骤,这就是为什么我们可以将InstallFonts()
实现留空。
我们需要对 Android 进行的另一项调整是为那些我们要使用自定义字体的控件自定义渲染器。在 iOS 中不需要此步骤,因为当您注册字体时,您可以在整个应用程序中使用它们。在我的示例中,我创建了 ButtonRenderer
和 LabelRenderer
。他们只是听 FontFamily
属性 的变化,然后尝试使用 IFontService
找到该字体。我创建了一个 FontToolbox
class ,它在 TryGetFont
方法中实现了字体加载逻辑。如果你想支持其他控件的自定义字体,你可以使用与 Button/Label 渲染器类似的模式,并使用 TryGetFont
加载实际字体。
iOS
对于 iOS 来说有点复杂。我们需要从缓存目录中加载字体文件,并使用ios字体管理API注册它们。参考 here 了解更多详情
演示代码
我创建了 FontDemoViewModel,它将调用 IFontService
,将可用字体加载到我们将在视图中绑定的集合中。
我创建了 FontDemoPage,它将允许 select 来自选择器的字体(为此我添加了 BindablePickerEx
控件)并更新按钮和标签的字体。
一些笔记
确保字体文件名与字体的 PostScript 名称匹配。 Android 不需要这个,因为 Android 可以加载任意名称的字体,因为它只需要一个字体文件的引用。但是对于 iOS 你需要确保这是真的,因为 运行-time 通过它们的 PostScript 名称搜索字体。 Here 您可以找到如何获取字体的 PostScript 名称。
另一件事是,在这个示例中,为了简单起见,我将所有字体嵌入到程序集资源中。在我的 BaseFontService
中,我使用此方法从程序集资源加载所有字体
private async Task<List<CustomFont>> DownloadFonts()
对于您的实施,您需要在此处发出实际的 Web 请求以从您的后端下载字体。