支持Google Emoji字体策略
谷歌推出了一个Android: Android Emoji策略,强制要求应用开发者支持Unicode Emoji的最新版本。策略说明
使用自定义emoji实现的应用程序,包括第三方库提供的应用程序,必须在新的Unicode Emoji发布后4个月内,在Android 12+设备上完全支持最新的Unicode版本。
本指南展示了如何通过捆绑emoji字体或使用Android: Google下载字体来支持此策略。
捆绑emoji字体与Google下载字体
对于支持最新emoji,这两种方法都有一些优缺点。最佳选项取决于每个应用程序。以下是两种方法的优缺点:
捆绑字体的优点
- 字体加载速度更快
- 用户没有互联网时也能工作
- 在所有操作系统上都能工作
- 独立(没有除Qt之外的其他依赖项)
- 解决方案简单
捆绑字体的缺点
- 增加应用程序大小(NotoColorEmoji约为10MB)
- 需要更新较新版本中的字体
- 旧版本的应用程序不会自动更新emoji
Google下载字体的优点
- 不会改变应用程序大小
- 自动更新
- 没有关系的多个应用程序共享相同的字体
Google下载字体的缺点
- 依赖于谷歌移动服务
- 仅限Android
- 如果没有预先缓存将会下载字体
- 没有预先缓存且没有互联网时不工作
- 比捆绑字体更复杂
如何捆绑字体
需要获取并捆绑字体,然后使用QML或C++加载它。
获取字体
在本指南中,我们将使用谷歌的NotoColorEmoji字体。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字体为emoji字体提供了一种自动更新的emoji字体,而不会增加应用程序的大小。使用可下载字体功能的下载字体的过程可以在Android:可下载字体过程中更详细地查看
对于本指南,流程将是:
- C++代码开始
- C++调用Java函数
- Java调用GDF来获取字体
- Java打开字体URI
- Java向C++返回文件描述符
- C++使用QFontDatabase加载字体
配置
可下载的Google字体适用于API级别26(Android 8.0)。但如果应用程序使用AndroidX,则可以支持到API 14的较早API。
注意:Android文档中使用的是Android:支持库而不是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代码
好的,我们现在深入代码!
我们需要将Java/Kotlin代码添加到我们的Android模板中。将其放置在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);
获取字体信息和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 Android Notifier示例。
从Java代码获取文件描述符后,将文件描述符包装到QFile类中,并使用QFontDatabase加载字体文件。
QFile file; file.open(fd, QFile::OpenModeFlag::ReadOnly, QFile::FileHandleFlag::AutoCloseHandle); QFontDatabase::addApplicationFontFromData(file->readAll());
© 2024 The Qt Company Ltd。本文件中的文档贡献是各自所有者的版权。本文件中的文档是根据Free Software Foundation发布的GNU自由文档许可版1.3许可的。Qt及其相关标志是The Qt Company Ltd在芬兰和其他国家/地区的商标。所有其他商标均为其各自所有者的财产。