Освой Android играючи

Сайт Александра Климова

Шкодим

/* Моя кошка замечательно разбирается в программировании. Стоит мне объяснить проблему ей - и все становится ясно. */
John Robbins, Debugging Applications, Microsoft Press, 2000

Буфер обмена: Копировать-Вставить

Пример копирования и вставки текста (Kotlin)
Слушатель для буфера обмена

Android поддерживает функции копирования-вставки. Работа с буфером обмена в целом идентична работе с аналогичной функциональностью в Windows. Мы копируем данные в одном приложении и вставляем данные в другом приложении/активности или в другом объекте этого же экрана.

На сайте разработки под Android есть картинка, иллюстрирующая работу с буфером обмена

Копировать можно текстовые строки, URI, Intent. Буфер обмена может содержать один объект, поэтому каждый новый копируемый объект вытесняет предыдущий.

Для работы с буфером обмена предназначен класс ClipBoardManager.

Существует устаревший класс из пакета android.text и новый класс из пакета android.content. Будьте бдительны, не путайте их при импорте классов. Также у нового класса есть несколько устаревших методов типа getText() и setText().

ClipData, ClipData.Item, ClipDescription

Чтобы добавить данные в буфер обмена, необходимо создать объект ClipData, содержащий описание данных и сами данные. Буфер обмена может содержать только один объект ClipData. ClipData содержит объект ClipDescription и один или больше объектов ClipData.Item.

Объект ClipDescription содержит метаданные о скопированном объекте - массив возможных MIME-типов для данных. Когда вы что-то копируете в буфер, данный массив доступен для приложений, которые могут определить, могут ли они работать с этими данными.

Объект ClipData.Item содержит текст, URI или данные для Intent.

Методы

Класс ClipData имеет в своём составе несколько удобных методов для создания объекта ClipData, содержащим объект ClipData.Item и объект ClipDescription:

  • newPlainText(label, text) - возвращает объект ClipData, в котором ClipData.Item содержит текстовую строку. Используется MIME-тип MIMETYPE_TEXT_PLAIN. Используйте метод для создания клипа из текстовой строки
  • newUri(resolver, label, URI) - возвращает объект ClipData, где единственный объект ClipData.Item содержит URI
  • newIntent(label, intent) - возвращает объект ClipData с единственным объектом ClipData.Item, который содержит Intent. MIME-тип MIMETYPE_TEXT_INTENT
  • newHtmlText (label, text, htmlText) - новый метод, который появился в API 16 (Jelly Bean). Создаёт объект ClipData, содержащий данные типа MIMETYPE_TEXT_HTML

Метод ClipData.Item.coerceToText()

Метод coerceToText() позволяет преобразовать скопированные данные в текст. Если в буфере содержится текст, то он так и останется текстом, а если содержит URI или Intent, то данные будет преобразованы (как Uri.toString() и Intent.toUri(URI_INTENT_SCHEME)).

Пример копирования и вставки текста (Kotlin)

Разместим на экране приложения текстовую метку, из которой будем копировать текст. Вставлять текст будем в текстовое поле. Также нам понадобятся две кнопки для самих операций копирования и вставки текста.


<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/paste_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="32dp"
        android:text="Paste From Clipboard"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textView" />

    <Button
        android:id="@+id/copy_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:text="Copy To Clipboard"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:text="Hello Kitty"
        android:textAppearance="@style/TextAppearance.AppCompat.Medium"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/copy_button" />

    <EditText
        android:id="@+id/editText"
        android:layout_width="409dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginTop="16dp"
        android:layout_marginEnd="8dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/paste_button" />

</androidx.constraintlayout.widget.ConstraintLayout>

Теперь напишем код для активности. Первая кнопка копирует текст в буфер обмена, вторая - вставляет из буфера обмена.


// Если этот код работает, его написал Александр Климов,
// а если нет, то не знаю, кто его писал.

package ru.alexanderklimov.hellokot

import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import kotlinx.android.synthetic.main.activity_main.*


class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager

        copy_button.setOnClickListener {
            // Copy text from TextView to clipboard
            val textToCopy = textView.text
            val clip = ClipData.newPlainText("simple text", textToCopy)
            //clipboard.primaryClip = clip // Kotlin-style doesn't work in SDK 29
            clipboard.setPrimaryClip(clip)
        }

        paste_button.setOnClickListener {
            // Paste clipboard text to EditText
            val clipData: ClipData? = clipboard.primaryClip

            clipData?.apply {
                val textToPaste: String = this.getItemAt(0).text.toString().trim()
                editText.setText(textToPaste)
            }
        }
    }
}
Copy Paste

Слушатель для буфера обмена

Мы можем отслеживать появление текста в буфере обмена через метод addPrimaryClipChangedListener(). Получим текст из буфера обмена из предыдущего примера не через кнопку вставить, а через слушатель и вставим текст в текстовое поле минуя щелчок кнопки.


override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager

    clipboard.addPrimaryClipChangedListener {
        val textToPaste: String = clipboard.primaryClip?.getItemAt(0)?.text.toString().trim()
        editText.setText(textToPaste)
    }

    copy_button.setOnClickListener {
        // Copy text from TextView to clipboard
        val textToCopy = textView.text
        val clip = ClipData.newPlainText("simple text", textToCopy)
        //clipboard.primaryClip = clip // Kotlin-style doesn't work in SDK 29
        clipboard.setPrimaryClip(clip)
    }

    paste_button.setOnClickListener {
        ...
    }
}

Забавно, что в эмуляторе буфер обмена работает и в Windows. Скопируйте какой-нибудь текст на компьютере и он появится в текстовом поле в эмуляторе.

Дополнительные материалы

Пример для Compose

Реклама