用户喜欢图像、视频和其他富有表现力的内容,但在应用中插入和移动这些内容并不总是那么容易。为了简化应用接收富内容的过程,Android 12(API 级别 31)引入了一个统一的 API,允许您的应用接受来自任何来源的内容:剪贴板、键盘或拖动。
您可以将一个接口(例如 OnReceiveContentListener
)附加到 UI 组件,并在通过任何机制插入内容时获得回调。回调成为您的代码处理接收所有内容的集中位置,从纯文本和格式化文本到标记、图像、视频、音频文件等。
为了与以前的 Android 版本向后兼容,此 API 也在 AndroidX 中可用,从 Core 1.7 和 Appcompat 1.4 开始,我们建议您在实现此功能时使用它们。
概述
对于其他现有 API,每个 UI 机制(例如触摸并按住菜单或拖动)都有其对应的 API。这意味着您必须分别与每个 API 集成,为每个插入内容的机制添加类似的代码
OnReceiveContentListener
API 通过创建一个要实现的单个 API 来整合这些不同的代码路径,因此您可以专注于您的应用特定逻辑,并让平台处理其余部分
这种方法还意味着,当向平台添加新的插入内容方式时,您无需进行额外的代码更改即可在您的应用中启用支持。如果您的应用需要为特定用例实现完全自定义,您仍然可以使用现有的 API,它们的工作方式保持不变。
实施
该 API 是一个带有单个方法的侦听器接口,OnReceiveContentListener
。为了支持旧版本的 Android 平台,我们建议在 AndroidX Core 库中使用匹配的 OnReceiveContentListener
接口。
要使用该 API,请通过指定您的应用可以处理的哪种内容类型来实现侦听器
Kotlin
object MyReceiver : OnReceiveContentListener { val MIME_TYPES = arrayOf("image/*", "video/*") // ... override fun onReceiveContent(view: View, payload: ContentInfoCompat): ContentInfoCompat? { TODO("Not yet implemented") } }
Java
public class MyReceiver implements OnReceiveContentListener { public static final String[] MIME_TYPES = new String[] {"image/*", "video/*"}; // ... }
在指定应用支持的所有内容 MIME 类型后,实现侦听器的其余部分
Kotlin
class MyReceiver : OnReceiveContentListener { override fun onReceiveContent(view: View, contentInfo: ContentInfoCompat): ContentInfoCompat { val split = contentInfo.partition { item: ClipData.Item -> item.uri != null } val uriContent = split.first val remaining = split.second if (uriContent != null) { // App-specific logic to handle the URI(s) in uriContent. } // Return anything that your app didn't handle. This preserves the // default platform behavior for text and anything else that you aren't // implementing custom handling for. return remaining } companion object { val MIME_TYPES = arrayOf("image/*", "video/*") } }
Java
public class MyReceiver implements OnReceiveContentListener { public static final String[] MIME_TYPES = new String[] {"image/*", "video/*"}; @Override public ContentInfoCompat onReceiveContent(View view, ContentInfoCompat contentInfo) { Pairsplit = contentInfo.partition( item -> item.getUri() != null); ContentInfo uriContent = split.first; ContentInfo remaining = split.second; if (uriContent != null) { // App-specific logic to handle the URI(s) in uriContent. } // Return anything that your app didn't handle. This preserves the // default platform behavior for text and anything else that you aren't // implementing custom handling for. return remaining; } }
如果您的应用已支持使用 intent 共享,则可以重用您的应用特定逻辑来处理内容 URI。将任何剩余数据返回以委托处理该数据给平台。
实现侦听器后,将其设置为应用中适当的 UI 元素
Kotlin
class MyActivity : Activity() { public override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // ... val myInput = findViewById(R.id.my_input) ViewCompat.setOnReceiveContentListener(myInput, MyReceiver.MIME_TYPES, MyReceiver()) } }
Java
public class MyActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { // ... AppCompatEditText myInput = findViewById(R.id.my_input); ViewCompat.setOnReceiveContentListener(myInput, MyReceiver.MIME_TYPES, new MyReceiver()); } }
URI 权限
平台会自动授予和释放传递给 OnReceiveContentListener
的有效负载中任何 内容 URI 的读取权限。
通常,您的应用在服务或活动中处理内容 URI。对于长时间运行的处理,请使用 WorkManager。当您实现此功能时,通过使用 Intent.setClipData
传递内容并 设置 标志 FLAG_GRANT_READ_URI_PERMISSION
来将权限扩展到目标服务或活动。
或者,您可以使用当前上下文中的后台线程来处理内容。在这种情况下,您必须维护对侦听器接收到的 payload
对象的引用,以帮助确保平台不会过早撤销权限。
自定义视图
如果您的应用使用自定义 View
子类,请注意确保不会绕过 OnReceiveContentListener
。
如果您的 View
类覆盖了 onCreateInputConnection
方法,请使用 Jetpack API InputConnectionCompat.createWrapper
来配置 InputConnection
。
如果您的 View
类覆盖了 onTextContextMenuItem
方法,则当菜单项为 R.id.paste
或 R.id.pasteAsPlainText
时,委托给 super。
与键盘图像 API 的比较
您可以将 OnReceiveContentListener
API 视为现有 键盘图像 API 的下一版本。此统一 API 支持键盘图像 API 的功能以及一些其他功能。设备和功能兼容性因您使用的是 Jetpack 库还是 Android SDK 中的原生 API 而异。
操作或功能 | 键盘图像 API 支持 | 统一 API 支持 |
---|---|---|
从键盘插入 | 是(API 级别 13 及更高版本) | 是(API 级别 13 及更高版本) |
使用触摸并按住菜单中的粘贴插入 | 否 | 是 |
使用拖放插入 | 否 | 是(API 级别 24 及更高版本) |
操作或功能 | 键盘图像 API 支持 | 统一 API 支持 |
---|---|---|
从键盘插入 | 是(API 级别 25 及更高版本) | 是(Android 12 及更高版本) |
使用触摸并按住菜单中的粘贴插入 | 否 | |
使用拖放插入 | 否 |