Kopieren und einfügen

Das Android-Framework für die Zwischenablage zum Kopieren und Einfügen unterstützt primitive und komplexe Datentypen, darunter:

  • Text strings
  • Komplexe Datenstrukturen
  • Text- und Binärstreamdaten
  • Anwendungs-Assets

Einfache Textdaten werden direkt in der Zwischenablage gespeichert, während komplexe Daten als Referenz gespeichert werden, die die einfügende Anwendung mit einem Contentanbieter auflöst.

Das Kopieren und Einfügen funktioniert sowohl innerhalb einer Anwendung als auch zwischen Anwendungen, die das Framework implementieren.

Da ein Teil des Frameworks Contentanbieter verwendet, wird in diesem Dokument davon ausgegangen, dass Sie mit der Android Content Provider API vertraut sind.

Mit Text arbeiten

Einige Komponenten unterstützen das Kopieren und Einfügen von Text, wie in der folgenden Tabelle dargestellt.

Komponente Text kopieren Text einfügen
BasicTextField ✅ ✅
TextField ✅ ✅
SelectionContainer ✅

Sie können beispielsweise den Text auf der Karte im folgenden Snippet in die Zwischenablage kopieren und den kopierten Text in TextField einfügen. Sie können das Menü zum Einfügen des Texts aufrufen, indem Sie die TextField berühren und halten oder auf den Cursorgriff tippen.

val textFieldState = rememberTextFieldState()

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

Sie können den Text mit der folgenden Tastenkombination einfügen: Strg + V . Die Tastenkombination ist standardmäßig verfügbar. Weitere Informationen finden Sie unter Tastaturaktionen verarbeiten.

Mit ClipboardManager kopieren

Mit ClipboardManager können Sie Texte in die Zwischenablage kopieren. Die Methode setText() kopiert das übergebene AnnotatedString-Objekt in die Zwischenablage. Im folgenden Snippet wird „Hello, clipboard“ in die Zwischenablage kopiert, wenn der Nutzer auf die Schaltfläche klickt.

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

Das folgende Snippet tut dasselbe, bietet Ihnen aber mehr Kontrolle. Ein häufiger Anwendungsfall ist das Kopieren vertraulicher Inhalte wie Passwörter. ClipEntry beschreibt ein Element in der Zwischenablage. Es enthält ein ClipData-Objekt, das Daten in der Zwischenablage beschreibt. Die Methode ClipData.newPlainText() ist eine praktische Methode, um ein ClipData-Objekt aus einem String-Objekt zu erstellen. Sie können das erstellte ClipEntry-Objekt in die Zwischenablage kopieren, indem Sie die Methode setClip() für das ClipboardManager-Objekt aufrufen.

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

Mit ClipboardManager einfügen

Sie können auf den in die Zwischenablage kopierten Text zugreifen, indem Sie die Methode getText() für ClipboardManager aufrufen. Die Methode getText() gibt ein AnnotatedString-Objekt zurück, wenn ein Text in die Zwischenablage kopiert wird. Mit dem folgenden Snippet wird Text in der Zwischenablage an den Text in TextField angehängt.

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

Mit Rich Content arbeiten

Nutzer lieben Bilder, Videos und andere ausdrucksstarke Inhalte. In Ihrer App können Nutzer Rich Content mit ClipboardManager und ClipEntry kopieren. Mit dem Modifikator contentReceiver können Sie das Einfügen von Rich Content implementieren.

Rich Content kopieren

Ihre App kann Rich Content nicht direkt in die Zwischenablage kopieren. Stattdessen übergibt Ihre App ein URI-Objekt an die Zwischenablage und ermöglicht den Zugriff auf den Inhalt mit einem ContentProvider. Das folgende Code-Snippet zeigt, wie ein JPEG-Bild in die Zwischenablage kopiert wird. Weitere Informationen finden Sie unter Datenstreams kopieren.

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

Rich Content einfügen

Mit dem Modifikator contentReceiver können Sie das Einfügen von Rich Content in BasicTextField in der geänderten Komponente verarbeiten. Im folgenden Code-Snippet wird der eingefügte URI von Bilddaten einer Liste von Uri-Objekten hinzugefügt.

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

Der Modifikator contentReceiver verwendet ein ReceiveContentListener-Objekt als Argument und ruft die Methode onReceive des übergebenen Objekts auf, wenn der Nutzer Daten in das BasicTextField innerhalb der geänderten Komponente einfügt.

Ein TransferableContent-Objekt wird an die onReceive-Methode übergeben. Es beschreibt die Daten, die in diesem Fall durch Einfügen zwischen Apps übertragen werden können. Sie können auf das ClipEntry-Objekt zugreifen, indem Sie auf das Attribut clipEntry verweisen.

Ein ClipEntry-Objekt kann mehrere ClipData.Item-Objekte enthalten, wenn der Nutzer beispielsweise mehrere Bilder auswählt und in die Zwischenablage kopiert. Sie sollten jedes ClipData.Item-Objekt als verwendet oder ignoriert markieren und ein TransferableContent mit den ignorierten ClipData.Item-Objekten zurückgeben, damit der nächste übergeordnete contentReceiver-Modifikator es empfangen kann.

Mit der Methode TransferableContent.hasMediaType() können Sie feststellen, ob das TransferableContent-Objekt ein Element mit dem Medientyp bereitstellen kann. Der folgende Methodenaufruf gibt beispielsweise true zurück, wenn das TransferableContent-Objekt ein Bild bereitstellen kann.

transferableContent.hasMediaType(MediaType.Image)

Mit komplexen Daten arbeiten

Sie können komplexe Daten auf dieselbe Weise in die Zwischenablage kopieren wie Rich Content. Weitere Informationen finden Sie unter Contentanbieter zum Kopieren komplexer Daten verwenden.

Sie können das Einfügen komplexer Daten auch auf die gleiche Weise für Rich Content handhaben. Sie können einen URI der eingefügten Daten erhalten. Die tatsächlichen Daten können aus einem ContentProvider abgerufen werden. Weitere Informationen finden Sie unter Daten vom Anbieter abrufen.

Feedback zum Kopieren von Inhalten

Nutzer erwarten Feedback, wenn sie Inhalte in die Zwischenablage kopieren. Daher zeigt Android unter Android 13 (API-Level 33) und höher zusätzlich zum Framework, das Kopieren und Einfügen ermöglicht, eine Standard-UI an, wenn Nutzer Inhalte kopieren. Aufgrund dieser Funktion besteht die Gefahr, dass Benachrichtigungen doppelt gesendet werden. Weitere Informationen zu diesem Sonderfall finden Sie unter Doppelte Benachrichtigungen vermeiden.

Eine Animation, die die Benachrichtigung zur Zwischenablage in Android 13 zeigt
Abbildung 1. Benutzeroberfläche, die angezeigt wird, wenn Inhalte in Android 13 und höher in die Zwischenablage kopiert werden.

Gib Nutzern beim Kopieren in Android 12L (API-Level 32) und niedriger manuell Feedback. Weitere Informationen finden Sie in der Empfehlung.

Sensible Inhalte

Wenn Sie zulassen, dass Nutzer vertrauliche Inhalte wie Passwörter in die Zwischenablage kopieren, muss Ihre App das System darüber informieren, damit die kopierten vertraulichen Inhalte nicht in der Benutzeroberfläche angezeigt werden (Abbildung 2).

Vorschau des kopierten Textes, in der sensible Inhalte gekennzeichnet sind.
Abbildung 2. Vorschau des kopierten Textes mit einer Kennzeichnung für sensible Inhalte.

Sie müssen ClipDescription in ClipData ein Flag hinzufügen, bevor Sie die Methode setClip() für das Objekt ClipboardManager aufrufen:

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