表情符号兼容性

试试 Compose 的方式
Jetpack Compose 是 Android 推荐的界面工具包。了解如何在 Compose 中支持表情符号。

EmojiCompat 支持库旨在让 Android 设备保持最新的表情符号。它能防止您的应用显示为 ☐ 形式的缺失表情符号,☐ 表示您的设备没有可显示该文本的字体。通过使用 EmojiCompat 支持库,您的应用用户无需等待 Android 操作系统更新即可获取最新的表情符号。

Devices showing emoji
图 1. 表情符号对比

请参考以下相关资源

EmojiCompat 如何工作?

EmojiCompat 支持库提供了在运行 Android 4.4 (API level 19) 及更高版本的设备上实现向下兼容的表情符号支持的类。您可以使用打包字体或可下载字体来配置 EmojiCompat。有关配置的更多信息,请参阅以下部分。

EmojiCompat 会识别给定 CharSequence 中的表情符号,并在需要时将其替换为 EmojiSpan,最后渲染表情符号字形。图 2 演示了此过程。

EmojiCompat process
图 2. EmojiCompat 过程

可下载字体配置

可下载字体配置使用可下载字体支持库功能来下载表情符号字体。它还会更新 EmojiCompat 支持库所需的相关表情符号元数据,以便跟上最新的 Unicode 规范版本。

添加支持库依赖项

要使用 EmojiCompat 支持库,您必须修改开发环境中应用项目的 classpath 依赖项。

将支持库添加到您的应用项目

  1. 打开应用的 build.gradle 文件。
  2. 将支持库添加到 dependencies 部分。

Groovy

dependencies {
    ...
    implementation "androidx.emoji:emoji:28.0.0"
}

Kotlin

dependencies {
    ...
    implementation("androidx.emoji:emoji:28.0.0")
}

初始化可下载字体配置

您需要初始化 EmojiCompat 以加载元数据和字体。由于初始化可能需要一些时间,因此初始化过程在后台线程上运行。

要使用可下载字体配置初始化 EmojiCompat,请执行以下步骤

  1. 创建 FontRequest 类的一个实例,并提供字体提供程序授权、字体提供程序软件包、字体查询以及证书哈希集列表。有关 FontRequest 的更多信息,请参阅可下载字体文档的以编程方式使用可下载字体部分。
  2. 创建 FontRequestEmojiCompatConfig 的实例,并提供 ContextFontRequest 的实例。
  3. 通过调用 init() 方法并传递 FontRequestEmojiCompatConfig 的实例来初始化 EmojiCompat
  4. Kotlin

    class MyApplication : Application() {
    
        override fun onCreate() {
            super.onCreate()
            val fontRequest = FontRequest(
                    "com.example.fontprovider",
                    "com.example",
                    "emoji compat Font Query",
                    CERTIFICATES
            )
            val config = FontRequestEmojiCompatConfig(this, fontRequest)
            EmojiCompat.init(config)
        }
    }

    Java

    public class MyApplication extends Application {
      @Override
       public void onCreate() {
         super.onCreate();
         FontRequest fontRequest = new FontRequest(
           "com.example.fontprovider",
           "com.example",
           "emoji compat Font Query",
           CERTIFICATES);
         EmojiCompat.Config config = new FontRequestEmojiCompatConfig(this, fontRequest);
         EmojiCompat.init(config);
       }
    }
  5. 在布局 XML 中使用 EmojiCompat 小部件。如果您使用的是 AppCompat,请参阅搭配使用 EmojiCompat 小部件与 AppCompat 部分。
  6. <android.support.text.emoji.widget.EmojiTextView
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"/>
    
    <android.support.text.emoji.widget.EmojiEditText
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"/>
    
    <android.support.text.emoji.widget.EmojiButton
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"/>

有关如何使用可下载字体配置配置 EmojiCompat 的更多信息,请访问 Emoji Compatibility 示例应用 Java | Kotlin

库组件

Library components in EmojiCompat process
图 3. EmojiCompat 过程中的库组件
Widget:EmojiEditTextEmojiTextViewEmojiButton
搭配 TextViewEditTextButton 使用 EmojiCompat 的默认小部件实现。
EmojiCompat
支持库的主要公共接口。它负责所有外部调用并与系统的其他部分进行协调。
EmojiCompat.Config
配置要创建的单例实例。
EmojiSpan
一个 ReplacementSpan 子类,用于替换字符(序列)并渲染字形。
EmojiCompat 字体
EmojiCompat 使用字体来显示表情符号。此字体是 Android 表情符号字体的修改版本。字体修改如下:
  • 为了提供渲染表情符号的向后兼容性,所有表情符号字符在 Unicode 的补充私用区 A (Supplemental Private Use Area-A) 中以单个 Unicode 码点表示,起始码点为 U+F0001。
  • 额外的表情符号元数据以二进制格式插入到字体中,由 EmojiCompat 在运行时解析。数据嵌入在字体的 meta 表中,带有私有标签 Emji

配置选项

您可以使用 EmojiCompat 实例修改 EmojiCompat 的行为。您可以使用基类中的以下方法设置配置:

Kotlin

val config = FontRequestEmojiCompatConfig(...)
        .setReplaceAll(true)
        .setEmojiSpanIndicatorEnabled(true)
        .setEmojiSpanIndicatorColor(Color.GREEN)
        .registerInitCallback(object: EmojiCompat.InitCallback() {
            ...
        })

Java

EmojiCompat.Config config = new FontRequestEmojiCompatConfig(...)
       .setReplaceAll(true)
       .setEmojiSpanIndicatorEnabled(true)
       .setEmojiSpanIndicatorColor(Color.GREEN)
       .registerInitCallback(new InitCallback() {...})

添加初始化监听器

EmojiCompat 类提供了 registerInitCallback()unregisterInitCallback() 方法来注册初始化回调。要使用这些方法,请创建 EmojiCompat.InitCallback 类的一个实例。调用这些方法并传递 EmojiCompat.InitCallback 类的实例。当 EmojiCompat 支持库初始化成功时,EmojiCompat 类会调用 onInitialized() 方法。如果库初始化失败,EmojiCompat 类会调用 onFailed() 方法。

要随时检查初始化状态,请调用 getLoadState() 方法。它返回以下值之一:LOAD_STATE_LOADINGLOAD_STATE_SUCCEEDEDLOAD_STATE_FAILED

将 EmojiCompat 与 AppCompat widget 结合使用

如果您使用的是 AppCompat 小部件,则可以使用从 AppCompat 小部件扩展而来的 EmojiCompat 小部件。

  1. 将支持库添加到依赖项部分。

    Groovy

    dependencies {
        ...
        implementation "androidx.emoji:emoji-bundled:$version"
    }

    Kotlin

          dependencies {
              implementation("androidx.emoji:emoji-appcompat:$version")
          }
          

    Groovy

          dependencies {
              implementation "androidx.emoji:emoji-appcompat:$version"
          }
          
  2. 在布局 XML 中使用 EmojiCompat AppCompat Widget 小部件。
  3. <android.support.text.emoji.widget.EmojiAppCompatTextView
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"/>
    
    <android.support.text.emoji.widget.EmojiAppCompatEditText
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"/>
    
    <android.support.text.emoji.widget.EmojiAppCompatButton
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"/>

捆绑字体配置

EmojiCompat 支持库也提供打包字体版本。此软件包包含带有嵌入式元数据的字体。此软件包还包含一个使用 AssetManager 加载元数据和字体的 BundledEmojiCompatConfig

注意:字体的大小以兆字节计。

添加支持库依赖项

要使用带打包字体配置的 EmojiCompat 支持库,您必须修改开发环境中应用项目的 classpath 依赖项。

将支持库添加到您的应用项目

  1. 打开应用的 build.gradle 文件。
  2. 将支持库添加到 dependencies 部分。

Groovy

dependencies {
    ...
    implementation "androidx.emoji:emoji:28.0.0"
}

Kotlin

dependencies {
    ...
    implementation("androidx.emoji:emoji:28.0.0")
}

使用捆绑字体配置 EmojiCompat

要使用捆绑字体配置 EmojiCompat,请执行以下步骤

  1. 使用 BundledEmojiCompatConfig 创建 EmojiCompat 实例并提供 Context 实例。
  2. 调用 init() 方法来初始化 EmojiCompat,并传递 BundledEmojiCompatConfig 的实例。

Kotlin

class MyApplication : Application() {

    override fun onCreate() {
        super.onCreate()
        val config = BundledEmojiCompatConfig(this)
        EmojiCompat.init(config)
    }
}

Java

public class MyApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        EmojiCompat.Config config = new BundledEmojiCompatConfig(this);
        EmojiCompat.init(config);
        ...
    }
}

不使用 widget 使用 EmojiCompat

EmojiCompat 使用 EmojiSpan 渲染正确的图像。因此,它必须将任何给定的 CharSequence 转换为带有 EmojiSpanSpanned 实例。EmojiCompat 类提供了一种方法,可将 CharSequence 转换为带有 EmojiSpanSpanned 实例。使用此方法,您可以处理并缓存已处理的实例而不是原始字符串,这可以提高应用的性能。

Kotlin

val processed = EmojiCompat.get().process("neutral face \uD83D\uDE10")

Java

CharSequence processed = EmojiCompat.get().process("neutral face \uD83D\uDE10");

为 IME 使用 EmojiCompat

通过使用 EmojiCompat 支持库,键盘可以渲染与其交互的应用支持的表情符号。IME 可以使用 hasEmojiGlyph() 方法检查 EmojiCompat 是否能够渲染表情符号。此方法接受一个表情符号的 CharSequence,如果 EmojiCompat 可以检测和渲染该表情符号,则返回 true

键盘还可以检查应用支持的 EmojiCompat 支持库版本,以确定在调色板中渲染哪些表情符号。如需检查版本(如果可用),键盘需要检查 EditorInfo.extras 捆绑包中是否存在以下键:

EditorInfo.extras 捆绑包中接收到键后,键盘可以使用 hasEmojiGlyph() 方法(其中 metadataVersionEDITOR_INFO_METAVERSION_KEY 的值),以检查应用是否可以渲染特定的表情符号。

将 EmojiCompat 与自定义 widget 结合使用

您始终可以使用 process() 方法在应用中预处理 CharSequence 并将其添加到任何可以渲染 Spanned 实例的小部件中;例如,TextView。此外,EmojiCompat 提供了以下小部件助手类,让您可以以最小的精力为自定义小部件添加表情符号支持。

示例 TextView

Kotlin

class MyTextView(context: Context) : AppCompatTextView(context) {

    private val emojiTextViewHelper: EmojiTextViewHelper by lazy(LazyThreadSafetyMode.NONE) {
        EmojiTextViewHelper(this).apply {
            updateTransformationMethod()
        }
    }

    override fun setFilters(filters: Array<InputFilter>) {
        super.setFilters(emojiTextViewHelper.getFilters(filters))
    }

    override fun setAllCaps(allCaps: Boolean) {
        super.setAllCaps(allCaps)
        emojiTextViewHelper.setAllCaps(allCaps)
    }
}

Java

public class MyTextView extends AppCompatTextView {
   ...
   public MyTextView(Context context) {
       super(context);
       init();
   }
   ...
   private void init() {
       getEmojiTextViewHelper().updateTransformationMethod();
   }

   @Override
   public void setFilters(InputFilter[] filters) {
       super.setFilters(getEmojiTextViewHelper().getFilters(filters));
   }

   @Override
   public void setAllCaps(boolean allCaps) {
       super.setAllCaps(allCaps);
       getEmojiTextViewHelper().setAllCaps(allCaps);
   }

   private EmojiTextViewHelper getEmojiTextViewHelper() {
       ...
   }
}
示例 EditText

Kotlin

class MyEditText(context: Context) : AppCompatEditText(context) {

    private val emojiEditTextHelper: EmojiEditTextHelper by lazy(LazyThreadSafetyMode.NONE) {
        EmojiEditTextHelper(this).also {
            super.setKeyListener(it.getKeyListener(keyListener))
        }
    }

    override fun setKeyListener(input: KeyListener?) {
        input?.also {
            super.setKeyListener(emojiEditTextHelper.getKeyListener(it))
        }
    }

    override fun onCreateInputConnection(outAttrs: EditorInfo): InputConnection {
        val inputConnection: InputConnection = super.onCreateInputConnection(outAttrs)
        return emojiEditTextHelper.onCreateInputConnection(
                inputConnection,
                outAttrs
        ) as InputConnection
    }
}

Java

public class MyEditText extends AppCompatEditText {
   ...
   public MyEditText(Context context) {
       super(context);
       init();
   }
   ...
   private void init() {
       super.setKeyListener(getEmojiEditTextHelper().getKeyListener(getKeyListener()));
   }

   @Override
   public void setKeyListener(android.text.method.KeyListener keyListener) {
       super.setKeyListener(getEmojiEditTextHelper().getKeyListener(keyListener));
   }

   @Override
   public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
       InputConnection inputConnection = super.onCreateInputConnection(outAttrs);
       return getEmojiEditTextHelper().onCreateInputConnection(inputConnection, outAttrs);
   }

   private EmojiEditTextHelper getEmojiEditTextHelper() {
       ...
   }
}

常见问题解答

  • 如何启动字体下载?
  • 表情符号字体会在首次请求时下载,如果设备上不存在。下载调度对应用是透明的。

  • 初始化需要多长时间?
  • 字体下载后,初始化 EmojiCompat 大约需要 150 毫秒。

  • EmojiCompat 支持库使用多少内存?
  • 目前,用于查找表情符号的数据结构加载在应用的内存中,占用约 200KB。

  • 我可以将 EmojiCompat 用于自定义 TextView 吗?
  • 可以。EmojiCompat 为自定义小部件提供了助手类。还可以预处理给定的字符串并将其转换为 Spanned。有关小部件助手类的更多信息,请参阅搭配使用 EmojiCompat 与自定义小部件部分。

  • 如果我在运行 Android 4.4(API 级别 19)或更低版本的设备上的布局 XML 中添加 widget,会发生什么?
  • 您可以在支持 Android 4.4 (API level 19) 或更低版本的设备的应用中包含 EmojiCompat 支持库或其小部件。但是,如果设备运行的 Android 版本低于 API level 19,EmojiCompat 及其小部件将处于“无操作”状态。这意味着 EmojiTextView 的行为与常规的 TextView 完全相同。EmojiCompat 实例;当您调用 init() 方法时,它会立即进入 LOAD_STATE_SUCCEEDED 状态。

更多资源

有关使用 EmojiCompat 库的更多信息,请观看 EmojiCompat