Платформа копирования и вставки Android на основе буфера обмена поддерживает примитивные и сложные типы данных, в том числе:
- Текстовые строки
- Сложные структуры данных
- Текстовые и двоичные потоковые данные
- Ресурсы приложения
Простые текстовые данные хранятся непосредственно в буфере обмена, а сложные данные хранятся в виде ссылки, которую приложение для вставки разрешает поставщику контента.
Копирование и вставка работают как внутри приложения, так и между приложениями, реализующими эту платформу.
Поскольку часть платформы использует поставщиков контента, этот документ предполагает некоторое знакомство с API контент-провайдеров Android .
Работа с текстом
Некоторые компоненты поддерживают копирование и вставку текста из поля, как показано в следующей таблице.
Компонент | Копирование текста | Вставка текста |
---|---|---|
Базистекстовое поле | ✅ | ✅ |
Текстовое поле | ✅ | ✅ |
Контейнер выбора | ✅ |
Например, вы можете скопировать текст карточки в буфер обмена в следующем фрагменте и вставить скопированный текст в TextField
. Меню для вставки текста отображается нажатием и удерживанием TextField
или нажатием маркера курсора.
val textFieldState = rememberTextFieldState()
Column {
Card {
SelectionContainer {
Text("You can copy this text")
}
}
BasicTextField(state = textFieldState)
}
Вы можете вставить текст с помощью следующего сочетания клавиш: Ctrl + V. Сочетание клавиш также доступно по умолчанию. Подробности см. в разделе «Обработка действий с клавиатуры» .
Копировать с помощью ClipboardManager
Вы можете копировать тексты в буфер обмена с помощью ClipboardManager
. Его метод setText() копирует переданный объект String в буфер обмена. Следующий фрагмент копирует «Привет, буфер обмена» в буфер обмена, когда пользователь нажимает кнопку.
// 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()
— это удобный метод создания объекта ClipData
из объекта String. Вы можете поместить созданный объект ClipEntry
в буфер обмена, вызвав метод setClip() над объектом ClipboardManager
.
// 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
Вы можете получить доступ к тексту, скопированному в буфер обмена, вызвав метод getText()
через ClipboardManager
. Его метод 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
в качестве аргумента и вызывает метод onReceive
переданного объекта, когда пользователь вставляет данные в BasicTextField
внутри измененного компонента.
Объект TransferableContent
передается методу onReceive, который описывает данные, которые в данном случае можно передавать между приложениями путем вставки. Вы можете получить доступ к объекту ClipEntry
, обратившись к атрибуту clipEntry
.
Объект ClipEntry
может иметь несколько объектов ClipData.Item
, например, когда пользователь выбирает несколько изображений и копирует их в буфер обмена. Вы должны пометить использованный или проигнорированный для каждого объекта ClipData.Item
и вернуть TransferableContent
содержащий игнорируемые объекты ClipData.Item
, чтобы модификатор contentReceiver
ближайшего предка мог его получить.
Метод TransferableContent.hasMediaType()
может помочь вам определить, может ли объект TransferableContent
предоставить элемент с типом мультимедиа. Например, следующий вызов метода возвращает true
, если объект TransferableContent
может предоставить изображение.
transferableContent.hasMediaType(MediaType.Image)
Работа со сложными данными
Вы можете копировать сложные данные в буфер обмена так же, как и богатый контент. Дополнительные сведения см. в разделе Использование поставщиков контента для копирования сложных данных .
Вы также можете обрабатывать вставки сложных данных таким же образом для расширенного контента. Вы можете получить URI вставленных данных. Фактические данные можно получить из ContentProvider
. Дополнительную информацию см. в разделе Получение данных от поставщика .
Отзыв о копировании контента
Пользователи ожидают обратной связи при копировании контента в буфер обмена, поэтому в дополнение к платформе, обеспечивающей копирование и вставку, Android показывает пользователям пользовательский интерфейс по умолчанию при копировании в Android 13 (уровень API 33) и выше. Из-за этой функции существует риск дублирования уведомлений. Подробнее об этом крайнем случае можно узнать в разделе «Избегайте дублирования уведомлений» .
Вручную предоставляйте пользователям обратную связь при копировании в Android 12L (уровень API 32) и ниже. Смотрите рекомендацию .
Деликатный контент
Если вы решите, чтобы ваше приложение позволяло пользователю копировать конфиденциальный контент в буфер обмена, например пароли, ваше приложение должно сообщить об этом системе, чтобы система могла избежать отображения скопированного конфиденциального контента в пользовательском интерфейсе (рис. 2).
Вы должны добавить флаг ClipDescription
в ClipData
перед вызовом метода setClip()
для объекта ClipboardManager
:
// 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)
}
}