Kopieren und einfügen

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

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

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

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 Texten direkt, wie in der folgenden Tabelle gezeigt.

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

Sie können beispielsweise den Text in der Karte im folgenden Snippet in die Zwischenablage kopieren und den kopierten Text in das TextField einfügen. Sie können das Menü zum Einfügen des Texts aufrufen, indem Sie das TextField gedrückt 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 auch verfügbar. Weitere Informationen finden Sie unter Tastaturaktionen verarbeiten.

Mit ClipboardManager kopieren

Sie können Texte mit ClipboardManager in die Zwischenablage kopieren. Mit der Methode „setText()“ wird das übergebene AnnotatedString Objekt in die Zwischenablage kopiert. Im folgenden Snippet wird „Hallo, Zwischenablage“ 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 macht dasselbe, bietet aber mehr Kontrolle. Ein häufiger Anwendungsfall ist das Kopieren sensibler Inhalte, wie Passwörter. ClipEntry beschreibt ein Element in der Zwischenablage. Es enthält ein ClipData-Objekt, das Daten in der Zwischenablage beschreibt. ClipData.newPlainText() Methode ist eine Hilfsmethode zum Erstellen eines ClipData Objekts aus einem String-Objekt. Sie können das erstellte ClipEntry Objekt in die Zwischenablage einfügen indem Sie die setClip() Methode 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() aufrufen für das ClipboardManager-Objekt. Die Methode getText() gibt ein AnnotatedString-Objekt zurück, wenn ein Text in die Zwischenablage kopiert wird. Im folgenden Snippet wird der Text in der Zwischenablage an den Text im 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. Ihre App kann es Nutzern ermöglichen, Rich Content mit ClipboardManager und ClipEntry zu kopieren. Mit dem contentReceiver Modifikator 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 die Inhalte mit einem ContentProvider. Im folgenden Code-Snippet wird gezeigt, wie Sie ein JPEG-Bild in die Zwischenablage kopieren. 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 contentReceiver-Modifikator können Sie das Einfügen von Rich Content in BasicTextField in der geänderten Komponente verarbeiten. Im folgenden Code-Snippet wird die eingefügte URI eines Bildes 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 in der geänderten Komponente einfügt.

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

Ein ClipEntry-Objekt kann mehrere ClipData.Item-Objekte haben, wenn der Nutzer beispielsweise mehrere Bilder auswählt und in die Zwischenablage kopiert. Sie sollten für jedes ClipData.Item-Objekt „Verbraucht“ oder „Ignoriert“ markieren und ein TransferableContent-Objekt zurückgeben, das die ignorierten ClipData.Item-Objekte enthält, damit der nächste übergeordnete Modifikator contentReceiver es empfangen kann.

Die TransferableContent.hasMediaType()-Methode kann Ihnen helfen, festzustellen , 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 dieselbe Weise wie Rich Content verarbeiten. Sie können eine URI der eingefügten Daten empfangen. Die tatsächlichen Daten können von 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. Zusätzlich zum Framework, das das Kopieren und Einfügen ermöglicht, wird Nutzern in Android 13 (API‑Level 33) und höher eine Standardbenutzeroberfläche angezeigt, wenn sie Inhalte kopieren. Aufgrund dieser Funktion besteht das Risiko doppelter Benachrichtigungen. Weitere Informationen zu diesem Grenzfall 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 gelangen.

Geben Sie Nutzern in Android 12L (API‑Level 32) und niedriger manuell Feedback, wenn sie Inhalte kopieren. Weitere Informationen zur Empfehlung.

Sensible Inhalte

Wenn Sie möchten, dass Nutzer in Ihrer App sensible Inhalte wie Passwörter in die Zwischenablage kopieren können, muss Ihre App das System darüber informieren, damit das System die kopierten sensiblen Inhalte nicht auf der Benutzeroberfläche anzeigt (Abbildung 2).

Vorschau des kopierten Textes, in der sensible Inhalte gekennzeichnet sind.
Abbildung 2. Vorschau des kopierten Texts mit einem Flag 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)
    }
}