Kopiuj i wklej

Platforma do kopiowania i wklejania danych na podstawie schowka Androida obsługuje proste i złożone typy danych, w tym:

  • Teksty
  • Złożone struktury danych
  • Dane strumieniowe tekstowe i binarne
  • Komponenty z linkiem do aplikacji

Proste dane tekstowe są przechowywane bezpośrednio w schowku, a dane złożone są przechowywane jako odwołanie, które aplikacja do wklejania rozwiązuje za pomocą dostawcy treści.

Kopiowanie i wklejanie działa zarówno w aplikacji, jak i między aplikacjami którzy biorą udział w programie.

Ponieważ w ramach naszego systemu korzystamy z usług dostawców treści, zakładamy, że zna on interfejs API dostawcy treści na Androida.

Praca z tekstem

Niektóre komponenty obsługują kopiowanie i wklejanie tekstu, jak pokazano w tabeli poniżej.

Komponent Kopiuję tekst Wklejanie tekstu
BasicTextField
Pole tekstowe
Kontener wyboru

Możesz na przykład skopiować tekst z karty do schowka w tym fragmencie kodu i wklejć go do pola TextField. W menu możesz wkleić tekst przez dotknij & przytrzymaj TextField lub dotknij uchwytu kursora.

val textFieldState = rememberTextFieldState()

Column {
    Card {
        SelectionContainer {
            Text("You can copy this text")
        }
    }
    BasicTextField(state = textFieldState)
}

Możesz wkleić tekst, korzystając z tego skrótu klawiszowego: Ctrl+V . Dostępny jest też domyślnie skrót klawiszowy. Więcej informacji znajdziesz w artykule Obsługa działań klawiatury.

Kopiuj z ClipboardManager

Możesz skopiować tekst do schowka za pomocą ClipboardManager. Metoda setText() kopiuje przekazany obiekt String do schowka. Ten fragment to „Cześć, schowek” do schowka, gdy użytkownik kliknie przycisk.

// 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")
}

Poniższy fragment kodu działa tak samo, ale daje Ci większą kontrolę. Typowym przypadkiem użycia jest kopiowanie treści poufnych, takich jak hasło. ClipEntry opisuje element na schowku. Zawiera obiekt ClipData opisujący dane w schowku. ClipData.newPlainText() to wygodna metoda do tworzenia obiektu ClipData na podstawie obiektu String. Możesz umieścić utworzony obiekt ClipEntry w schowku przez wywołanie metody setClip() nad obiektem 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")
}

Wklej za pomocą narzędzia ClipboardManager

Możesz uzyskać dostęp do tekstu skopiowanego do schowka wywołując metodę getText() w okresie ClipboardManager. Jego metoda getText() zwraca obiekt AnnotatedString gdy tekst jest kopiowany do schowka. Ten fragment dołącza tekst w schowku do tekstu w tabeli 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")
    }
}

Praca z treściami szczegółowymi

Użytkownicy uwielbiają zdjęcia, filmy i inne treści ekspresyjne. Aplikacja może umożliwiać użytkownikowi kopiowanie treści multimedialnych za pomocą funkcji ClipboardManagerClipEntry. Modyfikator contentReceiver ułatwia wklejanie treści sformatowanych.

Kopiowanie szczegółowych treści

Aplikacja nie może kopiować szczegółowych treści bezpośrednio do schowka. Zamiast tego aplikacja przekazuje obiekt URI do schowka i zapewnia dostęp do treści za pomocą komponentu ContentProvider. Ten fragment kodu pokazuje, jak skopiować obraz JPEG do schowka. Więcej informacji znajdziesz w artykule Kopiowanie strumieni danych.

// 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")
}

Wklejanie szczegółowych treści

Dzięki modyfikatorowi contentReceiver możesz wklejać treści sformatowane do BasicTextField w zmodyfikowanym komponencie. Następujący fragment kodu dodaje wklejony identyfikator URI danych obrazu do listy obiektów 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)
)

Modyfikator contentReceiver przyjmuje obiekt ReceiveContentListener jako argument i wywołuje funkcję onReceive metody przekazywanego obiektu, gdy użytkownik wkleja dane do BasicTextField w zmodyfikowanym komponencie.

Do metody onReceive przekazywany jest obiekt TransferableContent, który opisuje dane, które można przenosić między aplikacjami, w tym przypadku przez wklejanie. Dostęp do obiektu ClipEntry możesz uzyskać, korzystając z atrybutu clipEntry.

Obiekt ClipEntry może zawierać wiele obiektów ClipData.Item, gdy użytkownik wybierze kilka obrazów i skopiuje je do schowka. Należy oznaczyć jako wykorzystane lub zignorowane dla każdego obiektu ClipData.Item, i zwrócą TransferableContent zawierający ignorowane obiekty ClipData.Item aby mógł ją odebrać najbliższy modyfikator nadrzędny contentReceiver.

Metoda TransferableContent.hasMediaType() może pomóc Ci określić, czy obiekt TransferableContent może dostarczyć element z typem nośnika. Na przykład wywołanie metody zwraca true, jeśli obiekt TransferableContent może dostarczyć obraz.

transferableContent.hasMediaType(MediaType.Image)

Praca ze złożonymi danymi

Możesz kopiować złożone dane do schowka w taki sam sposób jak w przypadku treści osadzonych. Więcej informacji znajdziesz w artykule Kopiowanie złożonych danych za pomocą dostawców treści.

W taki sam sposób możesz też wklejać złożone dane w przypadku treści osadzonych. Możesz otrzymać identyfikator URI wklejonych danych. Prawdziwe dane można pobrać z ContentProvider. Więcej informacji znajdziesz w artykule Pobieranie danych od dostawcy.

Opinie na temat kopiowania treści

Użytkownicy oczekują informacji po skopiowaniu treści do schowka, więc oprócz platformy, na której opiera się kopiowanie i wklejanie, Android wyświetla użytkownikom domyślny interfejs podczas kopiowania w Androidzie 13 (poziom API 33) i wyższe. Dzięki tej funkcji istnieje ryzyko zduplikowania powiadomienia. Więcej informacji o tym szczególnym przypadku znajdziesz w artykule Unikanie zduplikowanych powiadomień.

Animacja pokazująca powiadomienie ze schowka na Androidzie 13
Rysunek 1. Interfejs Androida widoczny, gdy treść znajduje się w schowku 13 lub więcej.

Ręczne przekazywanie opinii użytkownikom podczas kopiowania w Androidzie 12L (poziom API 32) i starszym. Zobacz rekomendację.

Treści drażliwe

Jeśli chcesz, aby aplikacja umożliwiała użytkownikowi kopiowanie treści poufnych, takich jak hasła, do schowka, musisz poinformować o tym system, aby nie wyświetlał skopiowanych treści poufnych w interfejsie użytkownika (rysunek 2).

Podgląd tekstu ze zgłoszeniem treści poufnych został skopiowany.
Rysunek 2. Skopiowany podgląd tekstu z oznaczeniem treści poufnych.

Musisz dodać flagę do: ClipDescription w: ClipData przed wywołaniem metody setClip() za pomocą obiektu 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)
    }
}