用户通常希望使用表情符号、贴纸和其他类型的富媒体进行交流。在以前的 Android 版本中,软键盘(也称为 输入法编辑器 或 IME)只能将 Unicode 表情符号发送到应用。对于富媒体,应用会构建特定于应用的 API,这些 API 不能在其他应用中使用,或者使用一些变通方法,例如通过 简单共享操作 或剪贴板发送图像。
从 Android 7.1(API 级别 25)开始,Android SDK 包含提交内容 API,该 API 为 IME 提供了一种通用方法,可以将图像和其他富媒体直接发送到应用中的文本编辑器。该 API 也包含在 v13 支持库(从修订版 25.0.0 开始)中。我们建议使用支持库,因为它包含简化实现的帮助器方法。
使用此 API,您可以构建接受来自任何键盘的富媒体的短信应用,以及可以将富媒体发送到任何应用的键盘。例如,Google 键盘 和 Google 消息 等应用在 Android 7.1 中支持提交内容 API,如图 1 所示。
本文档说明了如何在 IME 和应用中实现提交内容 API。
工作原理
键盘图像插入需要 IME 和应用共同参与。以下序列描述了图像插入过程中的每个步骤
当用户点击
EditText
时,编辑器会发送一个它接受的 MIME 内容类型列表,位于EditorInfo.contentMimeTypes
中。IME 会读取支持的类型列表,并在软键盘中显示编辑器可以接受的内容。
当用户选择图像时,IME 会调用
commitContent()
并向编辑器发送一个InputContentInfo
。调用commitContent()
与调用commitText()
相似,但用于富媒体。InputContentInfo
包含一个 URI,该 URI 在 内容提供程序 中标识内容。
此过程如图 2 所示
向应用添加图像支持
要从 IME 接受富媒体,应用必须告知 IME 它接受什么内容类型,并指定一个在收到内容时执行的回调方法。以下示例演示如何创建一个接受 PNG 图像的 EditText
Kotlin
var editText: EditText = object : EditText(this) { override fun onCreateInputConnection(outAttrs: EditorInfo): InputConnection { var ic = super.onCreateInputConnection(outAttrs) EditorInfoCompat.setContentMimeTypes(outAttrs, arrayOf("image/png")) val mimeTypes = ViewCompat.getOnReceiveContentMimeTypes(this) if (mimeTypes != null) { EditorInfoCompat.setContentMimeTypes(outAttrs, mimeTypes) ic = InputConnectionCompat.createWrapper(this, ic, outAttrs) } return ic } }
Java
EditText editText = new EditText(this) { @Override public InputConnection onCreateInputConnection(EditorInfo outAttrs) { InputConnection ic = super.onCreateInputConnection(outAttrs); EditorInfoCompat.setContentMimeTypes(outAttrs, new String[]{"image/png"}); String[] mimeTypes = ViewCompat.getOnReceiveContentMimeTypes(this); if (mimeTypes != null) { EditorInfoCompat.setContentMimeTypes(outAttrs, mimeTypes); ic = InputConnectionCompat.createWrapper(this, ic, outAttrs); } return ic; } };
以下是对其的进一步解释
此示例使用支持库,因此有一些引用指向
android.support.v13.view.inputmethod
而不是android.view.inputmethod
。此示例创建一个
EditText
并覆盖其onCreateInputConnection(EditorInfo)
方法以修改InputConnection
。InputConnection
是 IME 和接收其输入的应用之间的通信通道。调用
super.onCreateInputConnection()
会保留内置行为(发送和接收文本),并为您提供InputConnection
的引用。setContentMimeTypes()
将支持的 MIME 类型列表添加到EditorInfo
中。在setContentMimeTypes()
之前调用super.onCreateInputConnection()
。callback
在 IME 提交内容时执行。方法onCommitContent()
包含对InputContentInfoCompat
的引用,其中包含内容 URI。- 如果您的应用运行在 API 级别 25 或更高版本上,并且
INPUT_CONTENT_GRANT_READ_URI_PERMISSION
标志由 IME 设置,则请求并释放权限。否则,您已经可以访问内容 URI,因为它由 IME 授予,或者因为内容提供程序没有限制访问权限。有关更多信息,请参阅 向 IME 添加图像支持。
- 如果您的应用运行在 API 级别 25 或更高版本上,并且
createWrapper()
将InputConnection
、修改后的EditorInfo
和回调包装到一个新的InputConnection
中,并将其返回。
以下是一些建议做法
不支持富媒体的编辑器不会调用
setContentMimeTypes()
,并且它们会将自己的EditorInfo.contentMimeTypes
设置为null
。如果
InputContentInfo
中指定的 MIME 类型与它们接受的任何类型都不匹配,则编辑器会忽略该内容。富媒体不会影响文本光标的位置,反之亦然。编辑器在处理内容时可以忽略光标位置。
在编辑器的
OnCommitContentListener.onCommitContent()
方法中,您可以返回true
,即使在加载内容之前也可以异步返回。与可以在提交之前在 IME 中编辑的文本不同,富媒体会立即提交。如果要让用户编辑或删除内容,请自行实现逻辑。
要测试您的应用,请确保您的设备或模拟器具有可以发送富媒体的键盘。您可以在 Android 7.1 或更高版本中使用 Google 键盘。
向 IME 添加图像支持
要将富媒体发送到应用,IME 必须实现提交内容 API,如下例所示
- 覆盖
onStartInput()
或onStartInputView()
并从目标编辑器读取支持的内容类型列表。以下代码片段显示了如何检查目标编辑器是否接受 GIF 图像。
Kotlin
override fun onStartInputView(editorInfo: EditorInfo, restarting: Boolean) { val mimeTypes: Array<String> = EditorInfoCompat.getContentMimeTypes(editorInfo) val gifSupported: Boolean = mimeTypes.any { ClipDescription.compareMimeTypes(it, "image/gif") } if (gifSupported) { // The target editor supports GIFs. Enable the corresponding content. } else { // The target editor doesn't support GIFs. Disable the corresponding // content. } }
Java
@Override public void onStartInputView(EditorInfo info, boolean restarting) { String[] mimeTypes = EditorInfoCompat.getContentMimeTypes(editorInfo); boolean gifSupported = false; for (String mimeType : mimeTypes) { if (ClipDescription.compareMimeTypes(mimeType, "image/gif")) { gifSupported = true; } } if (gifSupported) { // The target editor supports GIFs. Enable the corresponding content. } else { // The target editor doesn't support GIFs. Disable the corresponding // content. } }
- 当用户选择图像时,将内容提交到应用。在存在任何正在撰写的文本时,避免调用
commitContent()
,因为它可能会导致编辑器失去焦点。以下代码片段显示了如何提交 GIF 图像。
Kotlin
// Commits a GIF image. // @param contentUri = Content URI of the GIF image to be sent. // @param imageDescription = Description of the GIF image to be sent. fun commitGifImage(contentUri: Uri, imageDescription: String) { val inputContentInfo = InputContentInfoCompat( contentUri, ClipDescription(imageDescription, arrayOf("image/gif")), null ) val inputConnection = currentInputConnection val editorInfo = currentInputEditorInfo var flags = 0 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) { flags = flags or InputConnectionCompat.INPUT_CONTENT_GRANT_READ_URI_PERMISSION } InputConnectionCompat.commitContent(inputConnection, editorInfo, inputContentInfo, flags, null) }
Java
// Commits a GIF image. // @param contentUri = Content URI of the GIF image to be sent. // @param imageDescription = Description of the GIF image to be sent. public static void commitGifImage(Uri contentUri, String imageDescription) { InputContentInfoCompat inputContentInfo = new InputContentInfoCompat( contentUri, new ClipDescription(imageDescription, new String[]{"image/gif"}), null ); InputConnection inputConnection = getCurrentInputConnection(); EditorInfo editorInfo = getCurrentInputEditorInfo(); Int flags = 0; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) { flags |= InputConnectionCompat.INPUT_CONTENT_GRANT_READ_URI_PERMISSION; } InputConnectionCompat.commitContent( inputConnection, editorInfo, inputContentInfo, flags, null); }
作为 IME 作者,您很可能需要实现自己的内容提供程序才能响应内容 URI 请求。唯一例外是,如果您的 IME 支持来自现有内容提供程序(如 MediaStore
)的内容。有关构建内容提供程序的信息,请参阅 内容提供程序 和 文件提供程序 文档。
如果您正在构建自己的内容提供程序,我们建议您通过将 android:exported
设置为 false
来避免导出它。相反,通过将 android:grantUriPermission
设置为 true
来启用提供程序中的权限授予。然后,您的 IME 可以授予访问内容 URI 的权限(在内容提交时)。您可以通过两种方式执行此操作
在 Android 7.1(API 级别 25)及更高版本上,在调用
commitContent()
时,将标志参数设置为INPUT_CONTENT_GRANT_READ_URI_PERMISSION
。然后,应用接收到的InputContentInfo
对象可以通过调用requestPermission()
和releasePermission()
来请求和释放临时读取权限。在 Android 7.0(API 级别 24)及更低版本上,
INPUT_CONTENT_GRANT_READ_URI_PERMISSION
将被忽略,因此请手动授予对内容的权限。一种方法是使用grantUriPermission()
,但您可以实施满足您自己要求的机制。
要测试您的 IME,请确保您的设备或模拟器具有可以接收富媒体的应用。您可以在 Android 7.1 或更高版本中使用 Google Messenger 应用。