用于复制和粘贴的 Android 基于剪贴板的框架支持基元数据类型和复杂数据类型,包括:
- 文本字符串
- 复杂数据结构
- 文本和二进制流数据
- 应用素材资源
简单的文本数据直接存储在剪贴板中, 而复杂数据则作为引用存储 应用通过 content provider 进行解析。
复制和粘贴既可以在应用内进行,也可以在应用间进行 实施该框架的应用。
由于该框架的一部分使用内容提供程序 本文档假定您对 Android Content Provider API 有一定的了解。
使用文本
有些组件支持直接复制和粘贴文本,如 下表。
组件 | 复制文本 | 粘贴文本 |
---|---|---|
BasicTextField | ✅ | ✅ |
TextField | ✅ | ✅ |
SelectionContainer | ✅ |
例如,您可以将卡片中的文字复制到剪贴板
并将复制的文本粘贴到 TextField
中。
您显示了菜单,以便按如下方式粘贴文本:
触摸和按住 TextField
,或者点按光标手柄。
val textFieldState = rememberTextFieldState()
Column {
Card {
SelectionContainer {
Text("You can copy this text")
}
}
BasicTextField(state = textFieldState)
}
您可以使用以下键盘快捷键粘贴文字: Ctrl + V。 默认情况下,您也可以使用键盘快捷键。 如需了解详情,请参阅处理键盘操作。
使用 ClipboardManager
复制
您可以使用 ClipboardManager
将文本复制到剪贴板。
它的 setText() 方法会复制
将传递的 String 对象复制到剪贴板。
以下代码段会复制“Hello, 剪贴板”
复制到剪贴板
// Retrieve a ClipboardManager object
val clipboardManager = LocalClipboardManager.current
Button(
onClick = {
// Copy "Hello, clipboard" to the clipboard
clipboardManager.setText("Hello, clipboard")
}
) {
Text("Click to copy a text")
}
以下代码段执行的是相同的操作,但为您提供了更精细的控制。
一个常见的用例是复制敏感内容,例如密码。ClipEntry
用于描述剪贴板中的内容。它包含一个 ClipData
对象,用于描述剪贴板中的数据。ClipData.newPlainText()
方法是一种便捷方法,用于从 String 对象创建 ClipData
对象。您可以通过对 ClipboardManager
对象调用 setClip() 方法,将创建的 ClipEntry
对象设置为剪贴板。
// Retrieve a ClipboardManager object
val clipboardManager = LocalClipboardManager.current
Button(
onClick = {
val clipData = ClipData.newPlainText("plain text", "Hello, clipboard")
val clipEntry = ClipEntry(clipData)
clipboardManager.setClip(clipEntry)
}
) {
Text("Click to copy a text")
}
使用 ClipboardManager 粘贴
您可以通过对 ClipboardManager
调用 getText()
方法来访问复制到剪贴板的文本。当文本复制到剪贴板时,其 getText()
方法会返回 AnnotatedString
对象。以下代码段会在剪贴板中附加文本
TextField
中的文本。
var textFieldState = rememberTextFieldState()
Column {
TextField(state = textFieldState)
Button(
onClick = {
// The getText method returns an AnnotatedString object or null
val annotatedString = clipboardManager.getText()
if(annotatedString != null) {
// The pasted text is placed on the tail of the TextField
textFieldState.edit {
append(text.toString())
}
}
}
) {
Text("Click to paste the text in the clipboard")
}
}
使用富媒体内容
用户喜欢图片、视频和其他富有表现力的内容。您的应用可以让用户使用 ClipboardManager
和 ClipEntry
复制富媒体内容。contentReceiver
修饰符可帮助您实现粘贴富媒体内容。
复制富媒体内容
您的应用无法直接将富媒体内容复制到剪贴板。而是由应用将 URI
对象传递给剪贴板,并使用 ContentProvider
提供对内容的访问权限。以下代码段展示了如何将 JPEG 图片复制到剪贴板。
如需了解详情,请参阅复制数据流。
// Get a reference to the context
val context = LocalContext.current
Button(
onClick = {
// URI of the copied JPEG data
val uri = Uri.parse("content://your.app.authority/0.jpg")
// Create a ClipData object from the URI value
// A ContentResolver finds a proper ContentProvider so that ClipData.newUri can set appropriate MIME type to the given URI
val clipData = ClipData.newUri(context.contentResolver, "Copied", uri)
// Create a ClipEntry object from the clipData value
val clipEntry = ClipEntry(clipData)
// Copy the JPEG data to the clipboard
clipboardManager.setClip(clipEntry)
}
) {
Text("Copy a JPEG data")
}
粘贴富媒体内容
借助 contentReceiver
修饰符,您可以处理将富内容粘贴到修改后的组件中的 BasicTextField
。以下代码段将粘贴的图片数据的 URI
Uri
对象的列表。
// A URI list of images
val imageList by remember{ mutableListOf<Uri>() }
// Remember the ReceiveContentListener object as it is created inside a Composable scope
val receiveContentListener = remember {
ReceiveContentListener { transferableContent ->
// Handle the pasted data if it is image data
when {
// Check if the pasted data is an image or not
transferableContent.hasMediaType(MediaType.Image)) -> {
// Handle for each ClipData.Item object
// The consume() method returns a new TransferableContent object containging ignored ClipData.Item objects
transferableContent.consume { item ->
val uri = item.uri
if (uri != null) {
imageList.add(uri)
}
// Mark the ClipData.Item object consumed when the retrieved URI is not null
uri != null
}
}
// Return the given transferableContent when the pasted data is not an image
else -> transferableContent
}
}
}
val textFieldState = rememberTextFieldState()
BasicTextField(
state = textFieldState,
modifier = Modifier
.contentReceiver(receiveContentListener)
.fillMaxWidth()
.height(48.dp)
)
contentReceiver
修饰符将 ReceiveContentListener
对象作为其参数,并在用户将数据粘贴到修改后的组件内的 BasicTextField
时调用传递对象的 onReceive
方法。
系统会将 TransferableContent
对象传递给 onReceive 方法,该方法描述了在这种情况下可通过粘贴在应用之间传输的数据。您可以通过引用 clipEntry
属性来访问 ClipEntry
对象。
一个 ClipEntry
对象可以有多个 ClipData.Item
对象
当用户选择多张图片并将其复制到剪贴板时触发
示例。
您应为每个 ClipData.Item
对象标记为已消耗或已忽略,并返回包含已忽略 ClipData.Item
对象的 TransferableContent
,以便最近的祖先 contentReceiver
修饰符可以接收它。
TransferableContent.hasMediaType()
方法可帮助您确定 TransferableContent
对象是否可以提供具有媒体类型的项。例如,如果 TransferableContent
对象可以提供图片,则以下方法调用会返回 true
。
transferableContent.hasMediaType(MediaType.Image)
处理复杂数据
您可以将复杂数据复制到剪贴板 处理方式与处理富媒体内容相同。 如需了解详情,请参阅使用 content provider 复制复杂的数据。
你还可以处理粘贴复杂的数据
以同样的方式呈现富媒体内容
您可以接收粘贴数据的 URI。实际数据可以从 ContentProvider
中检索。
如需了解详情,请参阅从提供程序检索数据。
复制内容的反馈
用户希望在将内容复制到剪贴板时获得反馈,因此除了支持复制和粘贴的框架之外,在 Android 13(API 级别 33)及更高版本中进行复制时,Android 还会向用户显示一个默认界面。因为此功能,有出现重复通知的风险。 如需详细了解此极端情况,请参阅避免重复通知。
手动向用户提供反馈 在 Android 12L(API 级别 32)及更低版本中复制时。 查看建议。
敏感内容
如果您选择让应用允许用户将敏感内容复制到剪贴板, 例如密码,您的应用必须告知系统 这样,系统就可以避免显示复制的敏感内容 (图 2)。
您必须先向 ClipData
中的 ClipDescription
添加标志,然后才能对 ClipboardManager
对象调用 setClip()
方法:
// If your app is compiled with the API level 33 SDK or higher.
clipData.apply {
description.extras = PersistableBundle().apply {
putBoolean(ClipDescription.EXTRA_IS_SENSITIVE, true)
}
}
// If your app is compiled with a lower SDK.
clipData.apply {
description.extras = PersistableBundle().apply {
putBoolean("android.content.extra.IS_SENSITIVE", true)
}
}