支持 Google 表情符号字体政策
Google 推出了一项名为“Android 表情符号政策”的规定,要求应用开发者必须支持最新版本的 Unicode 表情符号。该政策规定:
采用自定义表情符号实现的应用(包括由第三方库提供的实现),在新版 Unicode 表情符号发布后的 4 个月内,若在 Android 12 及以上版本上运行,必须完全支持最新版本的 Unicode。
本指南将介绍如何通过打包表情符号字体或使用Android: Google 可下载字体来支持该政策。
打包表情符号字体与 Google 可下载字体
这两种支持最新表情符号的方法各有优缺点,最佳选择取决于具体应用。以下是这两种方法的优缺点:
打包字体的优点:
- 字体加载速度更快
- 在用户无法连接互联网时仍可正常使用
- 可在所有操作系统上运行
- 独立性强(除 Qt 外无其他依赖)
- 解决方案更简单
打包字体的缺点:
- 增加应用程序大小(NotoColorEmoji 约 10 MB)
- 在新版本发布时需要更新字体
- 旧版应用不会自动更新表情符号
Google 可下载字体的优点:
- 不改变应用程序大小
- 自动更新
- 多个互不相关的应用程序可共享同一套字体
Google 可下载字体的缺点:
- 依赖于 Google 移动服务
- 仅限 Android
- 若未预先缓存,将下载该字体
- 若未预先缓存,且无网络连接时无法使用
- 比将字体打包到应用中更为复杂
如何打包字体
必须先获取并打包字体,随后通过 QML 或 C++ 加载该字体。
获取字体
在本指南中,我们将使用 GoogleNotoColorEmoji 字体。NotoColorEmoji 是一款采用SIL OPEN FONT LICENSE 许可的字体。
注意:如果 从代码库下载,请下载 NotoColorEmoji_WindowsCompatible.ttf 字体,而不是 NotoColorEmoji.ttf。NotoColorEmoji.ttf 是内部构建的,采用不同的格式,仅在 Android/Chrome/Chromium OS 上得到良好支持。 由于 Qt 运行在其他平台上,Qt 字体加载器需要标准格式的 TrueType/OpenType 字体。
添加字体
正确打包字体的做法是将其添加到Qt 资源系统文件中。 例如,您可以为该字体创建一个独立的资源文件——“font.qrc”,其中包含 NotoColorEmoji_WindowsCompatible.ttf。要在 CMakeLists.txt 中嵌入新的资源文件,请使用以下代码:
qt_add_big_resources(PROJECT_SOURCES font.qrc)在 C++ 中加载打包的字体
若要使用 C++ 加载字体,请使用 `QFontDatabase`。
// Loading NotoColorEmoji bundled using C++ QFontDatabase
QFontDatabase::addApplicationFont(QStringLiteral(":/NotoColorEmoji_WindowsCompatible.ttf"));注意: 上述代码应在QQmlApplicationEngine 加载QML之前调用,这样当QML被加载时,字体已就绪并可供使用。
在 QML 中加载捆绑的字体
要在 QML 中加载字体,请使用 `FontLoader`:
// Loading NotoColorEmoji using QML FontLoader
FontLoader {
source:"NotoColorEmoji_WindowsCompatible.ttf"
}使用 Google 可下载字体:
使用 Google 可下载字体作为表情符号字体,可在不增加应用体积的情况下获得自动更新的表情符号字体。有关如何使用“可下载字体”功能下载字体的详细流程,请参阅《Android:可下载字体流程》
在本指南中,具体步骤如下:
- C++ 代码开始
- C++ 调用 Java 函数
- Java 调用 GDF 获取字体
- Java 打开字体 URI
- Java 将文件描述符返回给 C++
- C++ 使用QFontDatabase
配置
Google 可下载字体支持 API 级别 26(Android 8.0)。但如果应用使用 AndroidX,则可支持低至 API 14 的早期 API 版本。
注意: Android文档中 使用的是“Android: Support Library”而非“AndroidX”。但由于该支持库已不再维护且已被 AndroidX 取代,我们遵循了 Google 的建议,改用 AndroidX。
自定义 Android 包模板
首先,需要自定义 Android 打包模板。为此,请在Qt Creator 中进入“项目”选项卡,然后在“构建设置”中搜索“构建 Android APK”。该选项应位于“构建步骤”中,展开详细信息后,会出现一个名为“创建模板”的按钮。

点击“创建模板”,按照向导操作,最终将生成一个包含多个 Android 配置文件的文件夹。默认情况下,该文件夹位于项目目录下的android 中。
有关如何使用 qmake 自定义 Android 模板的信息,请参阅Android 包模板。
如果您像本指南中一样使用 CMake 和 Qt 6,则需要设置QT_ANDROID_PACKAGE_SOURCE_DIR属性。例如:
set_property(TARGET emojiremotefont PROPERTY
QT_ANDROID_PACKAGE_SOURCE_DIR
${CMAKE_CURRENT_SOURCE_DIR}/android)添加 AndroidX
要添加 AndroidX,请打开上述已添加的QT_ANDROID_PACKAGE_SOURCE_DIR文件夹中的build.gradle 文件,并在其中添加依赖项:
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
implementation 'androidx.appcompat:appcompat:1.4.1'
}要使用 AndroidX,我们需要设置相应的标志。为此,请在QT_ANDROID_PACKAGE_SOURCE_DIR目录下创建一个名为gradle.properties 的文件,并添加以下内容:
android.useAndroidX=true添加字体提供商证书
由于我们正在使用 AndroidX,还需要进行另一项配置——添加Android: 字体提供商证书。若要使用 GMS 字体提供商,请下载Android: GMS 字体提供商证书。若使用其他字体提供商,则需从该提供商处获取证书。
下载文件后,将其复制到 android 模板文件夹中的values 文件夹内,将其添加到 Android 资源中(而非 Qt 资源系统)。下图显示了正确的文件夹位置(1):

Java 代码
好,现在让我们深入研究代码吧!
我们需要在 Android 模板中添加Java/Kotlin 代码。将其放置在 android 模板文件夹中的src 文件夹下。您可能需要创建src 文件夹以及 Java 文件所需的文件夹结构。您可以在上一节的“Android 模板文件夹”图片中(位置 (2))查看此文件夹结构。
要在 C++ 中获取字体,Java 代码必须执行以下操作:
- 创建字体请求
- 使用字体请求从 FontsContractCompat 获取字体
- 获取字体信息和字体 URI(内容方案文件)
- 打开 URI 并获取文件描述符
- 将文件描述符返回给 C++ 代码
要创建字体请求,您需要字体提供商信息(授权、包和证书)以及字体的搜索查询。对于证书,请使用先前添加到 Android 资源中的 GMS 字体提供商证书文件fonts_cert.xml 。
// GMS fonts provider data
private static final String PROVIDER_AUTHORITY = "com.google.android.gms.fonts";
private static final String PROVIDER_PACKAGE = "com.google.android.gms";
// Emoji font search query (copied from EmojiCompat source)
private static final String EMOJI_QUERY = "emojicompat-emoji-font";
// Font Certificates resources strings (from fonts_certs.xml)
private static final String FONT_CERTIFICATE_ID = "com_google_android_gms_fonts_certs";
private static final String FONT_CERTIFICATE_TYPE = "array";
(...)
// obtain id for the font_certs.xml
int certificateId = context.getResources().getIdentifier(
FONT_CERTIFICATE_ID,
FONT_CERTIFICATE_TYPE,
context.getPackageName());
// creating the request
FontRequest request = new FontRequest(
PROVIDER_AUTHORITY,
PROVIDER_PACKAGE,
EMOJI_QUERY,
certificateId);现在,使用刚刚创建的请求来获取字体:
// fetch the font
FontsContractCompat.FontFamilyResult result =
FontsContractCompat.fetchFonts(context, null, request);获取FontInfo 和 URI:
final FontsContractCompat.FontInfo[] fontInfos = result.getFonts();
final Uri emojiFontUri = fontInfos[0].getUri();根据 URI 打开一个新的本机文件描述符:
final ContentResolver resolver = context.getContentResolver();
// in this case the Font URI is always a content scheme file, made
// so the app requesting it has permissions to open
final ParcelFileDescriptor fileDescriptor =
resolver.openFileDescriptor(fontInfos[0].getUri(), "r");
// the detachFd will return a native file descriptor that we must close
// later in C++ code
int fd = fileDescriptor.detachFd();
// return fd to C++注意: Java 中编写的全部内容 均可通过 JNI 在 C++ 中实现。本指南中展示的代码经过简化。投入生产环境的代码必须经过验证,并添加异常捕获等处理……
C++ 代码
好,Java 端的工作已经全部完成。接下来进入 C++ 端。
C++ 负责调用 Java 代码,并使用文件描述符将字体加载到 Qt 中。
若要更深入地了解 Qt 6 中 C++ 与 Java 之间的通信机制,请参考Qt for Android Notifier示例。
从 Java 代码获取文件描述符后,将其封装到 `QFile ` 类中,并使用 `QFontDatabase` 加载字体文件:
QFile file;
file.open(fd, QFile::OpenModeFlag::ReadOnly, QFile::FileHandleFlag::AutoCloseHandle);
QFontDatabase::addApplicationFontFromData(file->readAll());© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.