Android bietet ein leistungsstarkes auf der Zwischenablage basierendes Framework zum Kopieren und Einfügen. Sie unterstützt einfache und komplexe Datentypen wie Textstrings, komplexe Datenstrukturen, Text- und Binärstreamdaten sowie Anwendungs-Assets. Einfache Textdaten werden direkt in der Zwischenablage gespeichert, während komplexe Daten als Referenz gespeichert werden, die von der Einfügen-Anwendung mit einem Inhaltsanbieter aufgelöst wird. Das Kopieren und Einfügen funktioniert sowohl innerhalb einer Anwendung als auch zwischen Anwendungen, die das Framework implementieren.
Da im Rahmen des Frameworks Contentanbieter verwendet werden, wird eine gewisse Vertrautheit mit der Android Content Provider API vorausgesetzt, die unter Contentanbieter beschrieben wird.
Nutzer erwarten beim Kopieren von Inhalten in die Zwischenablage Feedback. Daher wird neben dem Framework, das das Kopieren und Einfügen ermöglicht, in Android beim Kopieren unter Android 13 (API-Level 33) und höher eine Standard-UI angezeigt. Aufgrund dieser Funktion besteht die Gefahr, dass Benachrichtigungen doppelt angezeigt werden. Weitere Informationen zu diesem Grenzfall finden Sie im Abschnitt Doppelte Benachrichtigungen vermeiden.
![Eine Animation, die die Benachrichtigung in der Zwischenablage von Android 13 zeigt](https://developer.android.com/static/images/about/versions/13/new-copy-paste-UI.gif?authuser=6&hl=de)
Nutzern beim Kopieren in Android 12L (API-Level 32) und niedriger manuell Feedback geben. Siehe Empfehlungen dazu in diesem Dokument.
Das Klemmbrett-Framework
Wenn Sie das Zwischenablage-Framework verwenden, fügen Sie Daten in ein Clipobjekt ein und platzieren Sie das Clipobjekt dann in der systemweiten Zwischenablage. Das Clip-Objekt kann eine von drei Formen annehmen:
- Text
- Ein Textstring. Fügen Sie den String direkt in das Clipobjekt ein, das Sie dann in die Zwischenablage einfügen. Um den String einzufügen, rufen Sie das Clipobjekt aus der Zwischenablage ab und kopieren Sie den String in den Speicher Ihrer Anwendung.
- URI
-
Ein
Uri
-Objekt, das eine beliebige Form von URI darstellt. Dies dient hauptsächlich zum Kopieren komplexer Daten von einem Contentanbieter. Zum Kopieren von Daten legen Sie einUri
-Objekt in ein Clip-Objekt und das Clip-Objekt in die Zwischenablage. Wenn Sie die Daten einfügen möchten, rufen Sie das Clipobjekt und dasUri
-Objekt ab, lösen Sie es in einer Datenquelle wie einem Contentanbieter auf und kopieren die Daten aus der Quelle in den Speicher Ihrer Anwendung. - Intent
-
Ein
Intent
. Dadurch wird das Kopieren von Anwendungsverknüpfungen unterstützt. Wenn Sie Daten kopieren möchten, erstellen Sie einIntent
, fügen Sie es in ein Clipobjekt ein und legen Sie das Clipobjekt in der Zwischenablage ab. Zum Einfügen der Daten rufen Sie das Clip-Objekt ab und kopieren dann dasIntent
-Objekt in den Arbeitsspeicherbereich Ihrer Anwendung.
In der Zwischenablage ist jeweils nur ein Clipobjekt enthalten. Wenn eine Anwendung ein Clipobjekt in der Zwischenablage platziert, verschwindet das vorherige Clipobjekt.
Wenn Sie es Nutzern ermöglichen möchten, Daten in Ihre Anwendung einzufügen, müssen Sie nicht alle Datentypen verarbeiten. Sie können die Daten in der Zwischenablage überprüfen, bevor Sie Nutzenden die Möglichkeit geben, sie einzufügen. Neben einem bestimmten Datenformat enthält das Clipobjekt auch Metadaten, die Aufschluss über die verfügbaren MIME-Typen geben. Anhand dieser Metadaten können Sie entscheiden, ob Ihre Anwendung mit den Daten in der Zwischenablage sinnvolle Aktionen ausführen kann. Wenn Sie beispielsweise eine Anwendung haben, die hauptsächlich Text verarbeitet, möchten Sie möglicherweise Clipobjekte ignorieren, die einen URI oder Intent enthalten.
Vielleicht möchten Sie Nutzern auch erlauben, Text unabhängig von der Form der Daten in der Zwischenablage einzufügen. Erzwingen Sie dazu die Daten in der Zwischenablage als Textdarstellung und fügen Sie diesen Text dann ein. Dies wird im Abschnitt Zwischenablage in Text erzwingen beschrieben.
Clipboard-Kurse
In diesem Abschnitt werden die Klassen beschrieben, die vom Framework für die Zwischenablage verwendet werden.
Zwischenablage-Manager
Die Android-Systemzwischenablage wird durch die globale Klasse ClipboardManager
dargestellt.
Instanziieren Sie diese Klasse nicht direkt. Rufen Sie stattdessen einen Verweis darauf ab, indem Sie getSystemService(CLIPBOARD_SERVICE)
aufrufen.
ClipData, ClipData.Item und ClipDescription
Um der Zwischenablage Daten hinzuzufügen, erstellen Sie ein ClipData
-Objekt, das eine Beschreibung der Daten und der Daten selbst enthält. Die Zwischenablage enthält jeweils ein ClipData
. Ein ClipData
enthält ein ClipDescription
-Objekt und ein oder mehrere ClipData.Item
-Objekte.
Ein ClipDescription
-Objekt enthält Metadaten zum Clip. Insbesondere enthält er ein Array der verfügbaren MIME-Typen für die Daten des Clips. Unter Android 12 (API-Level 31) und höher enthalten die Metadaten außerdem Informationen dazu, ob das Objekt stilisierten Text enthält und ob es sich um den Texttyp im Objekt handelt.
Wenn Sie einen Clip in die Zwischenablage einfügen, stehen diese Informationen für Einfügen-Anwendungen zur Verfügung, die prüfen können, ob die Clip-Daten verarbeitet werden können.
Ein ClipData.Item
-Objekt enthält die Text-, URI- oder Intent-Daten:
- Text
-
Ein
CharSequence
. - URI
-
Ein
Uri
. Es enthält normalerweise einen Inhaltsanbieter-URI, obwohl ein beliebiger URI zulässig ist. Von der Anwendung, die die Daten bereitstellt, wird der URI in die Zwischenablage eingefügt. Anwendungen, in denen die Daten eingefügt werden sollen, erhalten den URI aus der Zwischenablage und können damit auf den Contentanbieter oder eine andere Datenquelle zugreifen und die Daten abrufen. - Intent
-
Ein
Intent
. Mit diesem Datentyp können Sie eine Anwendungsverknüpfung in die Zwischenablage kopieren. Nutzer können die Verknüpfung dann zur späteren Verwendung in ihre Anwendungen einfügen.
Du kannst einem Clip mehrere ClipData.Item
-Objekte hinzufügen. So können Nutzer Mehrfachauswahlen
in einem einzigen Clip kopieren und einfügen. Wenn Sie beispielsweise ein Listen-Widget haben, mit dem Nutzer mehrere Elemente gleichzeitig auswählen können, können Sie alle Elemente auf einmal in die Zwischenablage kopieren. Erstellen Sie dazu eine separate ClipData.Item
für jedes Listenelement und fügen Sie dann die ClipData.Item
-Objekte dem ClipData
-Objekt hinzu.
ClipData-Convenience-Methoden
Die Klasse ClipData
bietet statische Methoden zum Erstellen eines ClipData
-Objekts mit einem einzelnen ClipData.Item
-Objekt und einem einfachen ClipDescription
-Objekt:
-
newPlainText(label, text)
- Gibt ein
ClipData
-Objekt zurück, dessen einzelnesClipData.Item
-Objekt einen Textstring enthält. Das Label desClipDescription
-Objekts ist auflabel
festgelegt. Der einzige MIME-Typ inClipDescription
istMIMETYPE_TEXT_PLAIN
.Verwenden Sie
newPlainText()
, um einen Clip aus einer Textzeichenfolge zu erstellen. -
newUri(resolver, label, URI)
- Gibt ein
ClipData
-Objekt zurück, dessen einzelnesClipData.Item
-Objekt einen URI enthält. Das Label desClipDescription
-Objekts ist auflabel
festgelegt. Wenn der URI ein Inhalts-URI ist undUri.getScheme()
alsocontent:
zurückgibt, verwendet die Methode das inresolver
bereitgestellteContentResolver
-Objekt, um die verfügbaren MIME-Typen vom Contentanbieter abzurufen. Sie werden dann inClipDescription
gespeichert. Bei einem URI, der keincontent:
-URI ist, legt die Methode den MIME-Typ aufMIMETYPE_TEXT_URILIST
fest.Verwenden Sie
newUri()
, um einen Clip aus einem URI zu erstellen, insbesondere einemcontent:
-URI. -
newIntent(label, intent)
- Gibt ein
ClipData
-Objekt zurück, dessen einzelnesClipData.Item
-Objekt einIntent
enthält. Das Label desClipDescription
-Objekts ist auflabel
festgelegt. Der MIME-Typ ist aufMIMETYPE_TEXT_INTENT
festgelegt.Mit
newIntent()
kannst du einen Clip aus einemIntent
-Objekt erstellen.
Daten aus der Zwischenablage in Text umwandeln
Auch wenn Ihre Anwendung nur Text verarbeitet, können Sie Nicht-Textdaten aus der Zwischenablage kopieren, indem Sie sie mit der Methode ClipData.Item.coerceToText()
konvertieren.
Diese Methode konvertiert die Daten in ClipData.Item
in Text und gibt eine CharSequence
zurück. Der von ClipData.Item.coerceToText()
zurückgegebene Wert basiert auf der Form der Daten in ClipData.Item
:
- Text
-
Wenn es sich bei
ClipData.Item
um Text handelt, also wenngetText()
nicht null ist, gibt CoerceToText() den Text zurück. - URI
-
Wenn
ClipData.Item
ein URI ist undgetUri()
nicht null ist, versuchtcoerceToText()
, ihn als Inhalts-URI zu verwenden.- Wenn der URI ein Inhalts-URI ist und der Anbieter einen Textstream zurückgeben kann, gibt
coerceToText()
einen Textstream zurück. - Wenn der URI ein Inhalts-URI ist, der Anbieter aber keinen Textstream anbietet, gibt
coerceToText()
eine Darstellung des URI zurück. Die Darstellung entspricht der vonUri.toString()
zurückgegebenen Darstellung. - Wenn der URI kein Inhalts-URI ist, gibt
coerceToText()
eine Darstellung des URI zurück. Die Darstellung entspricht der, die vonUri.toString()
zurückgegeben wird.
- Wenn der URI ein Inhalts-URI ist und der Anbieter einen Textstream zurückgeben kann, gibt
- Intent
- Wenn
ClipData.Item
eineIntent
ist, alsogetIntent()
nicht null ist, wird sie voncoerceToText()
in einen Intent-URI umgewandelt und zurückgegeben. Die Darstellung entspricht der, die vonIntent.toUri(URI_INTENT_SCHEME)
zurückgegeben wird.
Das Zwischenablage-Framework ist in Abbildung 2 zusammengefasst. Zum Kopieren von Daten legt eine Anwendung ein ClipData
-Objekt in die globale Zwischenablage des Typs ClipboardManager
ab. Das ClipData
-Objekt enthält ein oder mehrere ClipData.Item
-Objekte und ein ClipDescription
-Objekt. Zum Einfügen von Daten ruft eine Anwendung den ClipData
und den MIME-Typ aus dem ClipDescription
ab. Außerdem ruft sie die Daten vom ClipData.Item
oder vom Contentanbieter ab, auf den ClipData.Item
verweist.
![Ein Bild, das ein Blockdiagramm des Frameworks zum Kopieren und Einfügen zeigt](https://developer.android.com/static/images/ui/clipboard/copy_paste_framework.png?authuser=6&hl=de)
In die Zwischenablage kopieren
Wenn Sie Daten in die Zwischenablage kopieren möchten, rufen Sie einen Handle zum globalen ClipboardManager
-Objekt ab, erstellen Sie ein ClipData
-Objekt und fügen Sie ihm ein ClipDescription
sowie ein oder mehrere ClipData.Item
-Objekte hinzu. Fügen Sie dann das fertige ClipData
-Objekt dem ClipboardManager
-Objekt hinzu. Dies wird im Folgenden näher beschrieben:
- Wenn Sie Daten mithilfe eines Inhalts-URI kopieren möchten, richten Sie einen Contentanbieter ein.
- Systemzwischenablage abrufen:
Kotlin
when(menuItem.itemId) { ... R.id.menu_copy -> { // if the user selects copy // Gets a handle to the clipboard service. val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager } }
Java
... // If the user selects copy. case R.id.menu_copy: // Gets a handle to the clipboard service. ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
-
Kopieren Sie die Daten in ein neues
ClipData
-Objekt:-
Für Text
Kotlin
// Creates a new text clip to put on the clipboard. val clip: ClipData = ClipData.newPlainText("simple text", "Hello, World!")
Java
// Creates a new text clip to put on the clipboard. ClipData clip = ClipData.newPlainText("simple text", "Hello, World!");
-
Für einen URI
Dieses Snippet erstellt einen URI, indem eine Datensatz-ID in den Inhalts-URI für den Anbieter codiert wird. Diese Technik wird im Abschnitt Kennung im URI codieren ausführlicher beschrieben.
Kotlin
// Creates a Uri using a base Uri and a record ID based on the contact's last // name. Declares the base URI string. const val CONTACTS = "content://com.example.contacts" // Declares a path string for URIs, used to copy data. const val COPY_PATH = "/copy" // Declares the Uri to paste to the clipboard. val copyUri: Uri = Uri.parse("$CONTACTS$COPY_PATH/$lastName") ... // Creates a new URI clip object. The system uses the anonymous // getContentResolver() object to get MIME types from provider. The clip object's // label is "URI", and its data is the Uri previously created. val clip: ClipData = ClipData.newUri(contentResolver, "URI", copyUri)
Java
// Creates a Uri using a base Uri and a record ID based on the contact's last // name. Declares the base URI string. private static final String CONTACTS = "content://com.example.contacts"; // Declares a path string for URIs, used to copy data. private static final String COPY_PATH = "/copy"; // Declares the Uri to paste to the clipboard. Uri copyUri = Uri.parse(CONTACTS + COPY_PATH + "/" + lastName); ... // Creates a new URI clip object. The system uses the anonymous // getContentResolver() object to get MIME types from provider. The clip object's // label is "URI", and its data is the Uri previously created. ClipData clip = ClipData.newUri(getContentResolver(), "URI", copyUri);
-
Gezielte Absichten
Dieses Snippet erstellt ein
Intent
für eine Anwendung und fügt es dann im Clipobjekt ein:Kotlin
// Creates the Intent. val appIntent = Intent(this, com.example.demo.myapplication::class.java) ... // Creates a clip object with the Intent in it. Its label is "Intent" // and its data is the Intent object created previously. val clip: ClipData = ClipData.newIntent("Intent", appIntent)
Java
// Creates the Intent. Intent appIntent = new Intent(this, com.example.demo.myapplication.class); ... // Creates a clip object with the Intent in it. Its label is "Intent" // and its data is the Intent object created previously. ClipData clip = ClipData.newIntent("Intent", appIntent);
-
Für Text
-
Fügen Sie das neue Clipobjekt in die Zwischenablage ein:
Kotlin
// Set the clipboard's primary clip. clipboard.setPrimaryClip(clip)
Java
// Set the clipboard's primary clip. clipboard.setPrimaryClip(clip);
Beim Kopieren in die Zwischenablage Feedback geben
Nutzer erwarten visuelles Feedback, wenn eine App Inhalte in die Zwischenablage kopiert. Für Nutzer von Android 13 und höher erfolgt dies automatisch, muss in früheren Versionen aber manuell implementiert werden.
Ab Android 13 zeigt das System eine standardmäßige visuelle Bestätigung an, wenn Inhalte in die Zwischenablage eingefügt werden. Die neue Bestätigung hat folgende Auswirkungen:
- Bestätigt, dass der Inhalt erfolgreich kopiert wurde.
- Zeigt eine Vorschau des kopierten Inhalts an.
![Eine Animation, die die Benachrichtigung in der Zwischenablage von Android 13 zeigt](https://developer.android.com/static/images/about/versions/13/new-copy-paste-UI.gif?authuser=6&hl=de)
In Android 12L (API-Level 32) und niedriger sind Nutzer möglicherweise nicht sicher, ob sie die Inhalte erfolgreich kopiert haben. Diese Funktion standardisiert die verschiedenen Benachrichtigungen, die Apps nach dem Kopieren anzeigen, und bietet Nutzern mehr Kontrolle über die Zwischenablage.
Doppelte Benachrichtigungen vermeiden
In Android 12L (API-Level 32) und niedriger empfehlen wir, Nutzer beim erfolgreichen Kopieren durch visuelles In-App-Feedback mit einem Widget wie Toast
oder Snackbar
nach dem Kopieren zu benachrichtigen.
Um zu vermeiden, dass Informationen doppelt angezeigt werden, empfehlen wir für Android 13 und höher dringend, Pop-ups oder Snackbars nach einem In-App-Text zu entfernen.
![Eine Snackbar nach einem In-App-Text posten.](https://developer.android.com/static/images/about/versions/13/snackbar-overlap.png?authuser=6&hl=de)
![Nach einer In-App-Kopie Toast posten.](https://developer.android.com/static/images/about/versions/13/toast-overlap.png?authuser=6&hl=de)
Hier ist ein Beispiel für die Implementierung:
fun textCopyThenPost(textCopied:String) { val clipboardManager = getSystemService(CLIPBOARD_SERVICE) as ClipboardManager // When setting the clipboard text. clipboardManager.setPrimaryClip(ClipData.newPlainText ("", textCopied)) // Only show a toast for Android 12 and lower. if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.S_V2) Toast.makeText(context, “Copied”, Toast.LENGTH_SHORT).show() }
Sensible Inhalte in die Zwischenablage einfügen
Wenn Nutzer in Ihrer Anwendung vertrauliche Inhalte wie Passwörter oder Kreditkartendaten in die Zwischenablage kopieren können, müssen Sie ClipDescription
in ClipData
ein Flag hinzufügen, bevor Sie ClipboardManager.setPrimaryClip()
aufrufen. Durch das Hinzufügen dieses Flags wird verhindert, dass sensible Inhalte in der visuellen Bestätigung von kopierten Inhalten in Android 13 und höher angezeigt werden.
![Vorschau von kopiertem Text, ohne sensible Inhalte zu kennzeichnen](https://developer.android.com/static/images/about/versions/13/sensitive-content-before.png?authuser=6&hl=de)
![Vorschau von kopiertem Text, in der sensible Inhalte gemeldet werden.](https://developer.android.com/static/images/about/versions/13/sensitive-content-after.png?authuser=6&hl=de)
Fügen Sie dem ClipDescription
ein boolesches Extra hinzu, um sensible Inhalte zu kennzeichnen. Das müssen alle Anwendungen tun, unabhängig vom Ziel-API-Level.
// 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) } }
Aus der Zwischenablage einfügen
Fügen Sie wie oben beschrieben Daten aus der Zwischenablage ein. Rufen Sie dazu das globale Zwischenablage-Objekt ab, rufen Sie das Clip-Objekt ab, sehen Sie sich die zugehörigen Daten an und kopieren Sie die Daten nach Möglichkeit aus dem Clip-Objekt in Ihren eigenen Speicher. In diesem Abschnitt wird ausführlich erläutert, wie Sie die drei Arten von Daten in der Zwischenablage einfügen.
Nur Text einfügen
Wenn Sie Nur-Text einfügen möchten, rufen Sie die globale Zwischenablage ab und prüfen Sie, ob nur Text zurückgegeben werden kann. Rufen Sie dann das Clipobjekt ab und kopieren Sie seinen Text mithilfe von getText()
in Ihren eigenen Speicher, wie in der folgenden Anleitung beschrieben:
- Rufen Sie das globale
ClipboardManager
-Objekt mitgetSystemService(CLIPBOARD_SERVICE)
ab. Deklarieren Sie außerdem eine globale Variable, die den eingefügten Text enthält:Kotlin
var clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager var pasteData: String = ""
Java
ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE); String pasteData = "";
- Überlegen Sie, ob Sie die Option „Einfügen“ in der aktuellen Aktivität aktivieren oder deaktivieren müssen. Prüfen Sie, ob die Zwischenablage einen Clip enthält und ob Sie den durch den Clip dargestellten Datentyp verarbeiten können:
Kotlin
// Gets the ID of the "paste" menu item. val pasteItem: MenuItem = menu.findItem(R.id.menu_paste) // If the clipboard doesn't contain data, disable the paste menu item. // If it does contain data, decide whether you can handle the data. pasteItem.isEnabled = when { !clipboard.hasPrimaryClip() -> { false } !(clipboard.primaryClipDescription.hasMimeType(MIMETYPE_TEXT_PLAIN)) -> { // Disables the paste menu item, since the clipboard has data but it // isn't plain text. false } else -> { // Enables the paste menu item, since the clipboard contains plain text. true } }
Java
// Gets the ID of the "paste" menu item. MenuItem pasteItem = menu.findItem(R.id.menu_paste); // If the clipboard doesn't contain data, disable the paste menu item. // If it does contain data, decide whether you can handle the data. if (!(clipboard.hasPrimaryClip())) { pasteItem.setEnabled(false); } else if (!(clipboard.getPrimaryClipDescription().hasMimeType(MIMETYPE_TEXT_PLAIN))) { // Disables the paste menu item, since the clipboard has data but // it isn't plain text. pasteItem.setEnabled(false); } else { // Enables the paste menu item, since the clipboard contains plain text. pasteItem.setEnabled(true); }
- Kopieren Sie die Daten aus der Zwischenablage. Dieser Punkt im Code ist nur erreichbar, wenn der Menüpunkt „Einfügen“ aktiviert ist. Sie können also davon ausgehen, dass die Zwischenablage nur Text enthält. Sie wissen noch nicht, ob sie eine Textzeichenfolge oder einen URI enthält, der auf Nur-Text verweist.
Das folgende Code-Snippet testet dies, es wird jedoch nur der Code für die Verarbeitung von Nur-Text angezeigt:
Kotlin
when (menuItem.itemId) { ... R.id.menu_paste -> { // Responds to the user selecting "paste". // Examines the item on the clipboard. If getText() doesn't return null, // the clip item contains the text. Assumes that this application can only // handle one item at a time. val item = clipboard.primaryClip.getItemAt(0) // Gets the clipboard as text. pasteData = item.text return if (pasteData != null) { // If the string contains data, then the paste operation is done. true } else { // The clipboard doesn't contain text. If it contains a URI, // attempts to get data from it. val pasteUri: Uri? = item.uri if (pasteUri != null) { // If the URI contains something, try to get text from it. // Calls a routine to resolve the URI and get data from it. // This routine isn't presented here. pasteData = resolveUri(pasteUri) true } else { // Something is wrong. The MIME type was plain text, but the // clipboard doesn't contain text or a Uri. Report an error. Log.e(TAG,"Clipboard contains an invalid data type") false } } } }
Java
// Responds to the user selecting "paste". case R.id.menu_paste: // Examines the item on the clipboard. If getText() does not return null, // the clip item contains the text. Assumes that this application can only // handle one item at a time. ClipData.Item item = clipboard.getPrimaryClip().getItemAt(0); // Gets the clipboard as text. pasteData = item.getText(); // If the string contains data, then the paste operation is done. if (pasteData != null) { return true; // The clipboard doesn't contain text. If it contains a URI, attempts to get // data from it. } else { Uri pasteUri = item.getUri(); // If the URI contains something, try to get text from it. if (pasteUri != null) { // Calls a routine to resolve the URI and get data from it. // This routine isn't presented here. pasteData = resolveUri(Uri); return true; } else { // Something is wrong. The MIME type is plain text, but the // clipboard doesn't contain text or a Uri. Report an error. Log.e(TAG, "Clipboard contains an invalid data type"); return false; } }
Daten aus einem Inhalts-URI einfügen
Wenn das ClipData.Item
-Objekt einen Inhalts-URI enthält und Sie feststellen, dass Sie einen seiner MIME-Typen verarbeiten können, erstellen Sie eine ContentResolver
und rufen Sie die entsprechende Methode des Contentanbieters auf, um die Daten abzurufen.
Im Folgenden wird beschrieben, wie Sie Daten von einem Inhaltsanbieter anhand eines Inhalts-URI in der Zwischenablage abrufen können. Er prüft, ob ein MIME-Typ, den die Anwendung verwenden kann, beim Anbieter verfügbar ist.
-
Geben Sie eine globale Variable an, die den MIME-Typ enthält:
Kotlin
// Declares a MIME type constant to match against the MIME types offered // by the provider. const val MIME_TYPE_CONTACT = "vnd.android.cursor.item/vnd.example.contact"
Java
// Declares a MIME type constant to match against the MIME types offered by // the provider. public static final String MIME_TYPE_CONTACT = "vnd.android.cursor.item/vnd.example.contact";
- Rufen Sie die globale Zwischenablage ab. Besorge dir außerdem einen Content Resolver, damit du auf den Contentanbieter zugreifen kannst:
Kotlin
// Gets a handle to the Clipboard Manager. val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager // Gets a content resolver instance. val cr = contentResolver
Java
// Gets a handle to the Clipboard Manager. ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE); // Gets a content resolver instance. ContentResolver cr = getContentResolver();
- Rufen Sie den primären Clip aus der Zwischenablage ab und rufen Sie seinen Inhalt als URI ab:
Kotlin
// Gets the clipboard data from the clipboard. val clip: ClipData? = clipboard.primaryClip clip?.run { // Gets the first item from the clipboard data. val item: ClipData.Item = getItemAt(0) // Tries to get the item's contents as a URI. val pasteUri: Uri? = item.uri
Java
// Gets the clipboard data from the clipboard. ClipData clip = clipboard.getPrimaryClip(); if (clip != null) { // Gets the first item from the clipboard data. ClipData.Item item = clip.getItemAt(0); // Tries to get the item's contents as a URI. Uri pasteUri = item.getUri();
- Testen Sie, ob der URI ein Inhalts-URI ist, indem Sie
getType(Uri)
aufrufen. Diese Methode gibt null zurück, wennUri
nicht auf einen gültigen Contentanbieter verweist.Kotlin
// If the clipboard contains a URI reference... pasteUri?.let { // ...is this a content URI? val uriMimeType: String? = cr.getType(it)
Java
// If the clipboard contains a URI reference... if (pasteUri != null) { // ...is this a content URI? String uriMimeType = cr.getType(pasteUri);
- Testen Sie, ob der Contentanbieter einen MIME-Typ unterstützt, den die Anwendung versteht. Ist dies der Fall, rufen Sie
ContentResolver.query()
auf, um die Daten abzurufen. Der Rückgabewert ist einCursor
.Kotlin
// If the return value isn't null, the Uri is a content Uri. uriMimeType?.takeIf { // Does the content provider offer a MIME type that the current // application can use? it == MIME_TYPE_CONTACT }?.apply { // Get the data from the content provider. cr.query(pasteUri, null, null, null, null)?.use { pasteCursor -> // If the Cursor contains data, move to the first record. if (pasteCursor.moveToFirst()) { // Get the data from the Cursor here. // The code varies according to the format of the data model. } // Kotlin `use` automatically closes the Cursor. } } } }
Java
// If the return value isn't null, the Uri is a content Uri. if (uriMimeType != null) { // Does the content provider offer a MIME type that the current // application can use? if (uriMimeType.equals(MIME_TYPE_CONTACT)) { // Get the data from the content provider. Cursor pasteCursor = cr.query(uri, null, null, null, null); // If the Cursor contains data, move to the first record. if (pasteCursor != null) { if (pasteCursor.moveToFirst()) { // Get the data from the Cursor here. // The code varies according to the format of the data model. } } // Close the Cursor. pasteCursor.close(); } } } }
Intent einfügen
Wenn Sie einen Intent einfügen möchten, rufen Sie zuerst die globale Zwischenablage ab. Prüfen Sie, ob das Objekt ClipData.Item
ein Intent
enthält. Rufen Sie dann getIntent()
auf, um den Intent in Ihren eigenen Speicher zu kopieren. Das folgende Snippet veranschaulicht dies:
Kotlin
// Gets a handle to the Clipboard Manager. val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager // Checks whether the clip item contains an Intent by testing whether // getIntent() returns null. val pasteIntent: Intent? = clipboard.primaryClip?.getItemAt(0)?.intent if (pasteIntent != null) { // Handle the Intent. } else { // Ignore the clipboard, or issue an error if // you expect an Intent to be on the clipboard. }
Java
// Gets a handle to the Clipboard Manager. ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE); // Checks whether the clip item contains an Intent, by testing whether // getIntent() returns null. Intent pasteIntent = clipboard.getPrimaryClip().getItemAt(0).getIntent(); if (pasteIntent != null) { // Handle the Intent. } else { // Ignore the clipboard, or issue an error if // you expect an Intent to be on the clipboard. }
Systembenachrichtigung, die angezeigt wird, wenn deine App auf Daten in der Zwischenablage zugreift
Unter Android 12 (API-Level 31) und höher zeigt das System normalerweise eine Toast-Nachricht an, wenn deine App getPrimaryClip()
aufruft.
Der Text innerhalb der Nachricht hat das folgende Format:
APP pasted from your clipboard
Das System zeigt keine Toast-Meldung an, wenn Ihre App eine der folgenden Aktionen ausführt:
- Greift über Ihre eigene Anwendung auf
ClipData
zu. - greift wiederholt von einer bestimmten App aus auf
ClipData
zu. Der Toast wird nur angezeigt, wenn die App zum ersten Mal auf die Daten dieser App zugreift. - Ruft Metadaten für das Clipobjekt ab, z. B. durch Aufrufen von
getPrimaryClipDescription()
anstelle vongetPrimaryClip()
.
Mit Contentanbietern komplexe Daten kopieren
Contentanbieter unterstützen das Kopieren komplexer Daten wie Datenbankeinträge oder Dateistreams. Zum Kopieren der Daten fügen Sie einen Inhalts-URI in die Zwischenablage ein. Beim Einfügen von Anwendungen wird dieser URI dann aus der Zwischenablage abgerufen und zum Abrufen von Datenbankdaten oder Dateistream-Deskriptoren verwendet.
Da die Anwendung zum Einfügen nur den Inhalts-URI für Ihre Daten hat, muss sie wissen, welche Daten abgerufen werden sollen. Dazu können Sie eine Kennung für die Daten im URI selbst codieren oder einen eindeutigen URI angeben, mit dem die zu kopierenden Daten zurückgegeben werden. Welche Methode Sie wählen, hängt von der Organisation Ihrer Daten ab.
In den folgenden Abschnitten wird beschrieben, wie Sie URIs einrichten sowie komplexe Daten und Dateistreams bereitstellen. In den Beschreibungen wird davon ausgegangen, dass Sie mit den allgemeinen Prinzipien des Inhaltsanbieterdesigns vertraut sind.
Kennung im URI codieren
Eine nützliche Methode zum Kopieren von Daten in die Zwischenablage mit einem URI besteht darin, eine Kennung für die Daten im URI selbst zu codieren. Ihr Contentanbieter kann dann die Kennung aus dem URI abrufen und damit die Daten abrufen. Die Anwendung zum Einfügen muss nicht wissen, dass die ID existiert. Er benötigt nur Ihre Referenz – den URI und die Kennung – aus der Zwischenablage, gibt Ihren Contentanbieter an und ruft die Daten ab.
Normalerweise codieren Sie eine Kennung für einen Inhalts-URI, indem Sie sie am Ende des URI verketten. Angenommen, Sie definieren den Anbieter-URI als folgenden String:
"content://com.example.contacts"
Wenn Sie einen Namen für diesen URI codieren möchten, verwenden Sie das folgende Code-Snippet:
Kotlin
val uriString = "content://com.example.contacts/Smith" // uriString now contains content://com.example.contacts/Smith. // Generates a uri object from the string representation. val copyUri = Uri.parse(uriString)
Java
String uriString = "content://com.example.contacts" + "/" + "Smith"; // uriString now contains content://com.example.contacts/Smith. // Generates a uri object from the string representation. Uri copyUri = Uri.parse(uriString);
Wenn Sie bereits einen Contentanbieter verwenden, können Sie einen neuen URI-Pfad hinzufügen, der angibt, dass der URI zum Kopieren vorgesehen ist. Angenommen, Sie haben bereits die folgenden URI-Pfade:
"content://com.example.contacts/people" "content://com.example.contacts/people/detail" "content://com.example.contacts/people/images"
Sie können einen weiteren Pfad zum Kopieren von URIs hinzufügen:
"content://com.example.contacts/copying"
Sie können dann einen Kopier-URI durch einen Musterabgleich erkennen und ihn mit Code verarbeiten, der für das Kopieren und Einfügen spezifisch ist.
Die Codierung wird normalerweise verwendet, wenn Sie zum Organisieren Ihrer Daten bereits einen Inhaltsanbieter, eine interne Datenbank oder eine interne Tabelle nutzen. In diesen Fällen haben Sie mehrere Datenelemente, die Sie kopieren möchten, und vermutlich eine eindeutige Kennung für jedes Element. Als Antwort auf eine Abfrage der Einfügen-Anwendung können Sie die Daten anhand ihrer Kennung suchen und zurückgeben.
Wenn Sie nicht mehrere Daten haben, müssen Sie wahrscheinlich keine ID codieren. Sie können einen für Ihren Anbieter eindeutigen URI verwenden. Ihr Anbieter gibt als Antwort auf eine Abfrage die aktuell enthaltenen Daten zurück.
Datenstrukturen kopieren
Richten Sie einen Inhaltsanbieter zum Kopieren und Einfügen komplexer Daten als abgeleitete Klasse der Komponente ContentProvider
ein. Codieren Sie den URI aus der Zwischenablage so, dass er genau auf den Eintrag verweist, den Sie bereitstellen möchten. Berücksichtigen Sie außerdem den aktuellen Status Ihrer Anwendung:
- Wenn Sie bereits einen Contentanbieter haben, können Sie seine Funktionen erweitern. Möglicherweise müssen Sie nur die Methode
query()
ändern, um URIs zu verarbeiten, die von Anwendungen stammen, in denen Daten eingefügt werden sollen. Sie sollten die Methode so ändern, dass ein Kopier-URI-Muster verarbeitet wird. - Wenn Ihre Anwendung eine interne Datenbank hat, sollten Sie diese Datenbank zu einem Contentanbieter verschieben, um das Kopieren aus ihr zu vereinfachen.
- Wenn Sie keine Datenbank verwenden, können Sie einen einfachen Inhaltsanbieter implementieren, der nur dazu dient, Anwendungen Daten zum Einfügen aus der Zwischenablage bereitzustellen.
Überschreiben Sie im Contentanbieter mindestens die folgenden Methoden:
-
query()
- Beim Einfügen von Anwendungen wird davon ausgegangen, dass sie Ihre Daten über diese Methode mit dem URI abrufen können, den Sie in die Zwischenablage eingefügt haben. Damit das Kopieren unterstützt wird, lassen Sie diese Methode URIs erkennen, die einen speziellen Kopierpfad enthalten. Ihre Anwendung kann dann einen Kopier-URI erstellen und in die Zwischenablage einfügen, der den Kopierpfad und einen Verweis auf den zu kopierenden Eintrag enthält.
-
getType()
- Mit dieser Methode müssen die MIME-Typen für die Daten zurückgegeben werden, die Sie kopieren möchten. Die Methode
newUri()
ruftgetType()
auf, um die MIME-Typen in das neueClipData
-Objekt einzufügen.MIME-Typen für komplexe Daten werden unter Contentanbieter beschrieben.
Andere Contentanbietermethoden wie insert()
oder update()
sind nicht erforderlich.
Eine Anwendung zum Einfügen muss nur Ihre unterstützten MIME-Typen abrufen und Daten von Ihrem Anbieter kopieren.
Wenn Sie diese Methoden bereits haben, beeinträchtigen sie die Kopiervorgänge nicht.
Die folgenden Snippets veranschaulichen, wie Sie Ihre Anwendung zum Kopieren komplexer Daten einrichten:
-
Deklarieren Sie in den globalen Konstanten für Ihre Anwendung einen Basis-URI-String und einen Pfad, der URI-Strings identifiziert, die Sie zum Kopieren von Daten verwenden. Deklarieren Sie außerdem einen MIME-Typ für die kopierten Daten.
Kotlin
// Declares the base URI string. private const val CONTACTS = "content://com.example.contacts" // Declares a path string for URIs that you use to copy data. private const val COPY_PATH = "/copy" // Declares a MIME type for the copied data. const val MIME_TYPE_CONTACT = "vnd.android.cursor.item/vnd.example.contact"
Java
// Declares the base URI string. private static final String CONTACTS = "content://com.example.contacts"; // Declares a path string for URIs that you use to copy data. private static final String COPY_PATH = "/copy"; // Declares a MIME type for the copied data. public static final String MIME_TYPE_CONTACT = "vnd.android.cursor.item/vnd.example.contact";
- Richten Sie in der Aktivität, aus der Nutzer Daten kopieren, den Code so ein, dass Daten in die Zwischenablage kopiert werden.
Fügen Sie den URI als Antwort auf eine Kopieranfrage in die Zwischenablage ein.
Kotlin
class MyCopyActivity : Activity() { ... when(item.itemId) { R.id.menu_copy -> { // The user has selected a name and is requesting a copy. // Appends the last name to the base URI. // The name is stored in "lastName". uriString = "$CONTACTS$COPY_PATH/$lastName" // Parses the string into a URI. val copyUri: Uri? = Uri.parse(uriString) // Gets a handle to the clipboard service. val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager val clip: ClipData = ClipData.newUri(contentResolver, "URI", copyUri) // Sets the clipboard's primary clip. clipboard.setPrimaryClip(clip) } }
Java
public class MyCopyActivity extends Activity { ... // The user has selected a name and is requesting a copy. case R.id.menu_copy: // Appends the last name to the base URI. // The name is stored in "lastName". uriString = CONTACTS + COPY_PATH + "/" + lastName; // Parses the string into a URI. Uri copyUri = Uri.parse(uriString); // Gets a handle to the clipboard service. ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE); ClipData clip = ClipData.newUri(getContentResolver(), "URI", copyUri); // Sets the clipboard's primary clip. clipboard.setPrimaryClip(clip);
-
Erstellen Sie im globalen Bereich Ihres Contentanbieters einen URI-Abgleichstool und fügen Sie ein URI-Muster hinzu, das den URIs aus der Zwischenablage entspricht.
Kotlin
// A Uri Match object that simplifies matching content URIs to patterns. private val sUriMatcher = UriMatcher(UriMatcher.NO_MATCH).apply { // Adds a matcher for the content URI. It matches. // "content://com.example.contacts/copy/*" addURI(CONTACTS, "names/*", GET_SINGLE_CONTACT) } // An integer to use in switching based on the incoming URI pattern. private const val GET_SINGLE_CONTACT = 0 ... class MyCopyProvider : ContentProvider() { ... }
Java
public class MyCopyProvider extends ContentProvider { ... // A Uri Match object that simplifies matching content URIs to patterns. private static final UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH); // An integer to use in switching based on the incoming URI pattern. private static final int GET_SINGLE_CONTACT = 0; ... // Adds a matcher for the content URI. It matches // "content://com.example.contacts/copy/*" sUriMatcher.addURI(CONTACTS, "names/*", GET_SINGLE_CONTACT);
-
Richten Sie die Methode
query()
ein. Diese Methode kann je nach Codierung unterschiedliche URI-Muster verarbeiten, aber nur das Muster für den Kopiervorgang in die Zwischenablage wird angezeigt.Kotlin
// Sets up your provider's query() method. override fun query( uri: Uri, projection: Array<out String>?, selection: String?, selectionArgs: Array<out String>?, sortOrder: String? ): Cursor? { ... // When based on the incoming content URI: when(sUriMatcher.match(uri)) { GET_SINGLE_CONTACT -> { // Queries and returns the contact for the requested name. Decodes // the incoming URI, queries the data model based on the last name, // and returns the result as a Cursor. } } ... }
Java
// Sets up your provider's query() method. public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { ... // Switch based on the incoming content URI. switch (sUriMatcher.match(uri)) { case GET_SINGLE_CONTACT: // Queries and returns the contact for the requested name. Decodes the // incoming URI, queries the data model based on the last name, and // returns the result as a Cursor. ... }
-
Richten Sie die Methode
getType()
so ein, dass ein geeigneter MIME-Typ für kopierte Daten zurückgegeben wird:Kotlin
// Sets up your provider's getType() method. override fun getType(uri: Uri): String? { ... return when(sUriMatcher.match(uri)) { GET_SINGLE_CONTACT -> MIME_TYPE_CONTACT ... } }
Java
// Sets up your provider's getType() method. public String getType(Uri uri) { ... switch (sUriMatcher.match(uri)) { case GET_SINGLE_CONTACT: return (MIME_TYPE_CONTACT); ... } }
Im Abschnitt Daten aus einem Inhalts-URI einfügen wird beschrieben, wie Sie einen Inhalts-URI aus der Zwischenablage abrufen und damit Daten abrufen und einfügen können.
Datenstreams kopieren
Sie können große Mengen an Text und Binärdaten als Streams kopieren und einfügen. Die Daten können folgende Formen haben:
- Auf dem Gerät gespeicherte Dateien
- Streams von Sockets
- Große Datenmengen, die im zugrunde liegenden Datenbanksystem eines Anbieters gespeichert sind
Ein Inhaltsanbieter für Datenstreams bietet Zugriff auf seine Daten mit einem Dateideskriptorobjekt wie AssetFileDescriptor
anstelle eines Cursor
-Objekts. Die Einfügen-Anwendung liest den Datenstream mit diesem Dateideskriptor.
So richten Sie Ihre Anwendung so ein, dass ein Datenstream mit einem Anbieter kopiert wird:
-
Richten Sie einen Inhalts-URI für den Datenstream ein, den Sie in die Zwischenablage einfügen. Dazu gehören:
- Codieren Sie eine Kennung für den Datenstream im URI, wie im Abschnitt Kennung im URI codieren beschrieben. Pflegen Sie dann eine Tabelle bei Ihrem Anbieter, die Kennungen und den entsprechenden Streamnamen enthält.
- Codieren Sie den Streamnamen direkt auf dem URI.
- Verwenden Sie einen eindeutigen URI, der immer den aktuellen Stream vom Anbieter zurückgibt. Wenn Sie diese Option verwenden, müssen Sie Ihren Anbieter so aktualisieren, dass er immer dann auf einen anderen Stream verweist, wenn Sie den Stream mithilfe des URI in die Zwischenablage kopieren.
- Geben Sie für jeden Datenstream-Typ, den Sie anbieten möchten, einen MIME-Typ an. Diese Informationen werden für das Einfügen von Anwendungen benötigt, um zu ermitteln, ob die Daten in der Zwischenablage eingefügt werden können.
- Implementieren Sie eine der
ContentProvider
-Methoden, die einen Dateideskriptor für einen Stream zurückgibt. Wenn Sie Kennungen im Inhalts-URI codieren, können Sie mit dieser Methode bestimmen, welcher Stream geöffnet werden soll. - Wenn Sie den Datenstream in die Zwischenablage kopieren möchten, erstellen Sie den Inhalts-URI und legen Sie ihn in der Zwischenablage ab.
Zum Einfügen eines Datenstreams ruft eine Anwendung den Clip aus der Zwischenablage ab, ruft den URI ab und verwendet ihn in einem Aufruf einer Dateideskriptormethode ContentResolver
, die den Stream öffnet. Die Methode ContentResolver
ruft die entsprechende ContentProvider
-Methode auf und übergibt ihr den Inhalts-URI. Der Anbieter gibt den Dateideskriptor an die Methode ContentResolver
zurück. Die eingefügte Anwendung ist dann dafür verantwortlich, die Daten aus dem Stream zu lesen.
Die folgende Liste zeigt die wichtigsten Methoden zur Dateibeschreibung für einen Inhaltsanbieter. Jede Methode hat eine entsprechende ContentResolver
-Methode, wobei der String "Descriptor" an den Methodennamen angehängt ist. Das ContentResolver
-Analog zu openAssetFile()
ist beispielsweise openAssetFileDescriptor()
.
-
openTypedAssetFile()
-
Diese Methode gibt einen Asset-Dateideskriptor zurück, allerdings nur, wenn der angegebene MIME-Typ vom Anbieter unterstützt wird. Der Aufrufer – die Anwendung, die das Einfügen vornimmt – stellt ein MIME-Typ-Muster bereit. Der Contentanbieter der Anwendung, die einen URI in die Zwischenablage kopiert, gibt ein
AssetFileDescriptor
-Datei-Handle zurück, wenn er diesen MIME-Typ angeben kann, und löst eine Ausnahme aus, wenn dies nicht möglich ist.Diese Methode verarbeitet Unterabschnitte von Dateien. Sie können damit Assets lesen, die der Contentanbieter in die Zwischenablage kopiert hat.
-
openAssetFile()
-
Diese Methode ist eine allgemeinere Form von
openTypedAssetFile()
. Die Anwendung filtert nicht nach zulässigen MIME-Typen, kann jedoch Unterabschnitte von Dateien lesen. -
openFile()
-
Dies ist eine allgemeinere Form von
openAssetFile()
. Sie kann keine Unterabschnitte von Dateien lesen.
Optional können Sie die Methode openPipeHelper()
mit der Dateideskriptormethode verwenden. Dadurch kann die Einfügen-Anwendung die Streamdaten in einem Hintergrundthread mithilfe einer Pipe lesen. Implementieren Sie die Schnittstelle ContentProvider.PipeDataWriter
, um diese Methode zu verwenden.
Effektive Funktionen zum Kopieren und Einfügen entwerfen
Beachten Sie die folgenden Punkte, um effektive Kopier- und Einfügefunktionen für Ihre Anwendung zu entwerfen:
- Es befindet sich jeweils nur ein Clip in der Zwischenablage. Der vorherige Clip wird durch einen neuen Kopiervorgang von einer Anwendung im System überschrieben. Da der Nutzer die Anwendung möglicherweise verlässt und sie kopiert, bevor er zurückkehrt, können Sie nicht davon ausgehen, dass die Zwischenablage den Clip enthält, den der Nutzer zuvor in Ihrer Anwendung kopiert hat.
-
Der Zweck von mehreren
ClipData.Item
-Objekten pro Clip besteht darin, das Kopieren und Einfügen mehrerer Auswahlen zu unterstützen und nicht verschiedene Formen der Referenz auf eine einzelne Auswahl. Normalerweise sollten alleClipData.Item
-Objekte in einem Clip das gleiche Format haben. Das heißt, sie müssen alle einfachen Text, Inhalts-URIs oderIntent
sein und dürfen nicht kombiniert werden. -
Wenn Sie Daten angeben, können Sie verschiedene MIME-Darstellungen anbieten. Fügen Sie die von Ihnen unterstützten MIME-Typen dem
ClipDescription
hinzu und implementieren Sie die MIME-Typen dann bei Ihrem Contentanbieter. -
Wenn Sie Daten aus der Zwischenablage abrufen, muss Ihre Anwendung die verfügbaren MIME-Typen prüfen und dann entscheiden, welcher Typ verwendet werden soll. Auch wenn sich in der Zwischenablage ein Clip befindet und der Nutzer das Einfügen fordert, muss die Anwendung das Einfügen nicht ausführen. Fügen Sie den Text ein, wenn der MIME-Typ kompatibel ist. Sie können die Daten aus der Zwischenablage mit
coerceToText()
in Text umwandeln. Wenn Ihre Anwendung mehr als einen der verfügbaren MIME-Typen unterstützt, können Sie den Nutzern die Auswahl überlassen.