Фреймворк Android для копирования и вставки на основе буфера обмена поддерживает простые и сложные типы данных, в том числе:
- Текстовые строки
- Сложные структуры данных
- Текстовые и двоичные потоковые данные.
- Объекты приложений
Простые текстовые данные хранятся непосредственно в буфере обмена, а сложные – в виде ссылки, которую приложение, вставляющее данные, разрешает с помощью поставщика контента.
Копировать и вставлять можно как в одном приложении, так и между разными приложениями, в которых реализована эта функция.
Поскольку часть фреймворка использует поставщиков контента, предполагается, что вы знакомы с 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() копирует переданный объект AnnotatedString в буфер обмена.
Следующий фрагмент копирует "Hello, clipboard" в буфер обмена, когда пользователь нажимает кнопку.
// Retrieve a ClipboardManager object val clipboardManager = LocalClipboardManager.current Button( onClick = { // Copy "Hello, clipboard" to the clipboard clipboardManager.setText(AnnotatedString("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.
val 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(annotatedString.text) } } } ) { 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 = remember { mutableStateListOf<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 13 (уровень API 33) и более поздних версиях ОС при копировании показывается стандартный интерфейс. Из-за этой функции есть риск дублирования уведомлений. Подробнее о том, как избежать дублирования уведомлений…
В Android 12L (уровень API 32) и более ранних версиях ОС нужно вручную настроить уведомление о копировании. Ознакомьтесь с рекомендацией.
Деликатный контент
Если вы разрешаете пользователям копировать в буфер обмена конфиденциальный контент, например пароли, ваше приложение должно сообщать об этом системе, чтобы она не показывала скопированный контент в интерфейсе (рис. 2).
Перед вызовом метода setClip() для объекта ClipboardManager необходимо добавить флаг в ClipDescription в ClipData:
// 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) } }