Kopieren und einfügen

Das Android-basierte Clipboard-Framework zum Kopieren und Einfügen unterstützt primitive und komplexe Datentypen, darunter:

  • Textstrings
  • 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 Anwendung zum Einfügen mit einem Inhaltsanbieter 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 nutzt, In diesem Dokument wird 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 den in der folgenden Tabelle.

Komponente Text wird kopiert Text einfügen
BasicTextField
TextField
SelectionContainer

Sie können beispielsweise den Text auf der Karte im folgenden Snippet in die Zwischenablage kopieren und ihn dann in TextField einfügen. Sie zeigen das Menü zum Einfügen des Textes mit einer Touch- & Halten Sie das TextField gedrückt oder tippen Sie auf den Cursorziehpunkt.

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 ebenfalls standardmäßig verfügbar. Weitere Informationen finden Sie unter Tastaturaktionen verarbeiten.

Mit ClipboardManager kopieren

Mit ClipboardManager können Sie Text in die Zwischenablage kopieren. Die zugehörige Methode setText() kopiert das übergebene String-Objekt in die Zwischenablage. Mit dem folgenden Snippet wird „Hallo, Zwischenablage“ kopiert. in die Zwischenablage einfügen, 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("Hello, clipboard")
    }
) {
   Text("Click to copy a text")
}

Das folgende Snippet führt dasselbe aus, bietet aber mehr Kontrolle. Ein häufiger Anwendungsfall ist das Kopieren vertraulicher Inhalte, z. B. Passwörter. Mit ClipEntry wird ein Element in der Zwischenablage beschrieben. Es enthält ein ClipData-Objekt, das Daten in der Zwischenablage beschreibt. Die Methode ClipData.newPlainText() ist eine praktische Methode, ein ClipData-Objekt aus einem String-Objekt erstellen. Sie können das erstellte ClipEntry-Objekt in die Zwischenablage kopieren, indem Sie die Methode setClip() über 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

Du kannst auf den in die Zwischenablage kopierten Text zugreifen durch Aufrufen der Methode getText() über ClipboardManager. Die getText()-Methode gibt ein AnnotatedString-Objekt zurück. Ein Text aus der Zwischenablage wird kopiert. Im folgenden Snippet wird der Text in der Zwischenablage an den Text in TextField angehängt.

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

Mit Rich Content arbeiten

Nutzer mögen Bilder, Videos und andere ausdrucksstarke Inhalte. Ihre App kann es Nutzern ermöglichen, Rich Content mit ClipboardManager und ClipEntry. Mit dem Modifikator contentReceiver können Sie das Einfügen von Rich-Content implementieren.

Rich Content kopieren

Ihre App kann keine Rich Content-Inhalte direkt in die Zwischenablage kopieren. Stattdessen übergibt Ihre App ein URI-Objekt an die Zwischenablage und gewährt über ein ContentProvider Zugriff auf die Inhalte. Im folgenden Code-Snippet wird gezeigt, 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 Rich-Content in der geänderten Komponente in BasicTextField einfügen. Mit dem folgenden Code-Snippet wird der eingefügte URI eines Bilddaten-Snippets hinzugefügt. zu einer Liste von Uri-Objekten hinzu.

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

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

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

Ein ClipEntry-Objekt kann mehrere ClipData.Item-Objekte haben, wenn der Nutzer beispielsweise mehrere Bilder auswählt und in die Zwischenablage kopiert. Sie sollten jedes ClipData.Item-Objekt als „verbraucht“ oder „ignoriert“ kennzeichnen und ein TransferableContent mit den ignorierten ClipData.Item-Objekten zurückgeben, damit es vom nächstgelegenen übergeordneten contentReceiver-Modifikator empfangen werden kann.

Mit der Methode TransferableContent.hasMediaType() kannst du feststellen, ob das TransferableContent-Objekt ein Element mit dem Medientyp bereitstellen kann. Der folgende Methodenaufruf gibt beispielsweise true zurück. Das TransferableContent-Objekt kann ein Bild bereitstellen.

transferableContent.hasMediaType(MediaType.Image)

Mit komplexen Daten arbeiten

Sie können komplexe Daten in die Zwischenablage kopieren genauso wie bei Rich Content. Weitere Informationen finden Sie unter Inhaltsanbieter zum Kopieren komplexer Daten verwenden.

Sie können auch das Einfügen komplexer Daten auf die gleiche Weise für Rich Content. Sie können einen URI für die 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

Nutzende erwarten Feedback, wenn sie Inhalte in die Zwischenablage kopieren, Neben dem Framework, das Kopieren und Einfügen ermöglicht, Android zeigt Nutzern beim Kopieren unter Android 13 (API-Level 33) eine Standard-UI an. und höher. Aufgrund dieser Funktion besteht das Risiko, dass Sie doppelte Benachrichtigungen erhalten. Weitere Informationen zu diesem Grenzfall finden Sie unter Doppelte Benachrichtigungen vermeiden.

Eine Animation, die die Benachrichtigung in der Zwischenablage von Android 13 zeigt
Abbildung 1. Benutzeroberfläche, die unter Android angezeigt wird, wenn Inhalte in die Zwischenablage eingefügt werden ab 13 Jahren.

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

Sensible Inhalte

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

In der Vorschau des kopierten Texts werden sensible Inhalte gekennzeichnet.
Abbildung 2: Vorschau von kopiertem Text mit Markierung für sensible Inhalte.

Du musst ClipDescription in ClipData ein Flag hinzufügen, bevor du die setClip()-Methode über das ClipboardManager-Objekt aufrufst:

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