用户喜欢图片、视频和其他富有表现力的内容,但在应用中插入和移动这些内容并不总是那么容易。为了让应用更轻松地接收富媒体内容,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) { Pair<ContentInfoCompat, ContentInfoCompat> split = 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 的载荷(payload)中包含的任何 内容 URI,读取权限均由平台自动授予和释放。
通常,您的应用会在服务或 Activity 中处理内容 URI。对于长时间运行的处理,请使用 WorkManager。实现此操作时,通过使用 Intent.setClipData 传递内容并设置 FLAG_GRANT_READ_URI_PERMISSION 标志,将权限扩展到目标服务或 Activity。
或者,您可以使用当前上下文中的后台线程来处理内容。在这种情况下,您必须保持对监听器接收到的 payload 对象的引用,以帮助确保权限不会被平台过早撤销。
自定义视图
如果您的应用使用了自定义 View 子类,请注意确保 OnReceiveContentListener 不会被绕过。
如果您的 View 类重写了 onCreateInputConnection 方法,请使用 Jetpack API InputConnectionCompat.createWrapper 来配置 InputConnection。
如果您的 View 类重写了 onTextContextMenuItem 方法,当菜单项为 R.id.paste 或 R.id.pasteAsPlainText 时,请委托给父类方法。
与键盘图像 API 的比较
您可以将 OnReceiveContentListener API 视为现有 键盘图像 API 的下一版本。此统一 API 支持键盘图像 API 的功能以及一些附加特性。设备和功能兼容性取决于您是使用 Jetpack 库还是 Android SDK 中的原生 API。
| 操作或功能 | 键盘图像 API 支持 | 统一 API 支持 |
|---|---|---|
| 从键盘插入 | 是(API 级别 13 及更高) | 是(API 级别 13 及更高) |
| 从触摸并按住菜单使用粘贴功能插入 | 否 | 是 |
| 使用拖放操作插入 | 否 | 是(API 级别 24 及更高) |
| 操作或功能 | 键盘图像 API 支持 | 统一 API 支持 |
|---|---|---|
| 从键盘插入 | 是(API 级别 25 及更高) | 是(Android 12 及更高) |
| 从触摸并按住菜单使用粘贴功能插入 | 否 | |
| 使用拖放操作插入 | 否 |