Android bietet ein leistungsstarkes Clipboard-basiertes Framework zum Kopieren und Einfügen. Es unterstützt einfache und komplexe Datentypen, einschließlich Textstrings, komplexer 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 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 für einen Teil des Frameworks Contentanbieter verwendet werden, wird in diesem Dokument vorausgesetzt, dass Sie mit der Android Content Provider API vertraut sind. Diese wird im Abschnitt Contentanbieter beschrieben.
Nutzer erwarten Feedback beim Kopieren von Inhalten in die Zwischenablage. Daher zeigt Android neben dem Framework, das das Kopieren und Einfügen unterstützt, Nutzern unter Android 13 (API-Level 33) und höher eine Standard-Benutzeroberfläche an. Aufgrund dieser Funktion besteht das Risiko, dass Sie doppelte Benachrichtigungen erhalten. Weitere Informationen zu diesem Sonderfall finden Sie im Abschnitt Duplizierte Benachrichtigungen vermeiden.
Geben Sie Nutzern beim Kopieren unter Android 12L (API-Level 32) und niedriger manuell Feedback. Empfehlungen dazu finden Sie in diesem Dokument.
Zwischenablage-Framework
Wenn Sie das Clipboard-Framework verwenden, legen Sie Daten in ein Clip-Objekt und dann das Clip-Objekt in die systemweite Zwischenablage. Das Clip-Objekt kann eine von drei Formen haben:
- Text
- Einen Textstring. Fügen Sie den String direkt in das Clip-Objekt ein, das Sie dann in die Zwischenablage kopieren. Wenn Sie den String einfügen möchten, rufen Sie das Clip-Objekt aus der Zwischenablage ab und kopieren Sie den String in den Speicher Ihrer Anwendung.
- URI
-
Ein
Uri
-Objekt, das jede Form eines URI darstellt. Diese Funktion dient hauptsächlich zum Kopieren komplexer Daten von einem Contentanbieter. Wenn Sie Daten kopieren möchten, platzieren Sie einUri
-Objekt in einem Clip-Objekt und legen Sie das Clip-Objekt in die Zwischenablage. Wenn du die Daten einfügen möchtest, musst du das Clip-Objekt und dasUri
-Objekt abrufen, in eine Datenquelle wie einen Inhaltsanbieter auflösen und die Daten aus der Quelle in den Speicher deiner Anwendung kopieren. - Intent
-
Ein
Intent
. Das Kopieren von Anwendungsverknüpfungen wird unterstützt. Wenn Sie Daten kopieren möchten, erstellen Sie einIntent
, fügen Sie es in ein Clip-Objekt ein und legen Sie das Clip-Objekt in die Zwischenablage. Wenn Sie die Daten einfügen möchten, rufen Sie das Clip-Objekt ab und kopieren Sie dasIntent
-Objekt in den Speicherbereich Ihrer Anwendung.
Die Zwischenablage kann jeweils nur ein Clipobjekt enthalten. Wenn eine Anwendung ein Clipobjekt in die Zwischenablage kopiert, verschwindet das vorherige Clipobjekt.
Wenn Sie Nutzern erlauben möchten, Daten in Ihre Anwendung einzufügen, müssen Sie nicht alle Datentypen verarbeiten. Sie können die Daten in der Zwischenablage prüfen, bevor Sie Nutzern die Möglichkeit zum Einfügen geben. Neben einem bestimmten Datenformat enthält das Clip-Objekt auch Metadaten, aus denen hervorgeht, welche MIME-Typen verfügbar sind. Anhand dieser Metadaten können Sie entscheiden, ob Ihre Anwendung mit den Zwischenablagedaten etwas Nützliches tun kann. Wenn Sie beispielsweise eine Anwendung haben, die hauptsächlich Text verarbeitet, können Sie Clip-Objekte mit einem URI oder einer Absicht ignorieren.
Sie können Nutzern auch erlauben, Text unabhängig von der Datenform in der Zwischenablage einzufügen. Dazu müssen Sie die Zwischenablagedaten in eine Textdarstellung umwandeln und dann einfügen. Weitere Informationen finden Sie im Abschnitt Zwischenablage in Text umwandeln.
Zwischenablageklassen
In diesem Abschnitt werden die vom Zwischenablage-Framework verwendeten Klassen beschrieben.
ClipboardManager
Die Android-System-Zwischenablage wird durch die globale Klasse ClipboardManager
dargestellt.
Diese Klasse darf nicht direkt instanziiert werden. Rufen Sie stattdessen getSystemService(CLIPBOARD_SERVICE)
auf, um einen Verweis darauf zu erhalten.
ClipData, ClipData.Item und ClipDescription
Wenn Sie der Zwischenablage Daten hinzufügen möchten, erstellen Sie ein ClipData
-Objekt, das eine Beschreibung der Daten und die Daten selbst enthält. Die Zwischenablage kann jeweils nur einen ClipData
aufnehmen. 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 es eine Reihe von 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 zum Texttyp im Objekt.
Wenn Sie einen Clip in die Zwischenablage legen, sind diese Informationen für Anwendungen zum Einfügen verfügbar, die prüfen können, ob sie die Clipdaten verarbeiten können.
Ein ClipData.Item
-Objekt enthält den Text, den URI oder die Intent-Daten:
- Text
-
A
CharSequence
. - URI
-
A
Uri
. Dieser enthält normalerweise einen URI des Inhaltsanbieters, aber jeder URI ist zulässig. Die Anwendung, die die Daten bereitstellt, kopiert den URI in die Zwischenablage. Anwendungen, die die Daten einfügen möchten, rufen den URI aus der Zwischenablage ab und verwenden ihn, um auf den Inhaltsanbieter oder eine andere Datenquelle zuzugreifen und die Daten abzurufen. - Intent
-
Ein
Intent
. Mit diesem Datentyp können Sie einen Anwendungs-Hotkey 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 mehrere Auswahlen als einzelnen 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 gleichzeitig in die Zwischenablage kopieren. Erstellen Sie dazu für jedes Listenelement ein separates ClipData.Item
und fügen Sie die ClipData.Item
-Objekte dem ClipData
-Objekt hinzu.
ClipData-Verfahren
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
.Mit
newPlainText()
kannst du einen Clip aus einem Textstring 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, d. h., wennUri.getScheme()
content:
zurückgibt, verwendet die Methode das inresolver
bereitgestellteContentResolver
-Objekt, um die verfügbaren MIME-Typen vom Inhaltsanbieter abzurufen. Diese werden dann inClipDescription
gespeichert. Bei einem URI, der keincontent:
-URI ist, wird der MIME-Typ von der Methode aufMIMETYPE_TEXT_URILIST
festgelegt.Mit
newUri()
kannst du einen Clip aus einem URI erstellen, insbesondere aus 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.
Zwischenablagedaten in Text umwandeln
Auch wenn Ihre Anwendung nur Text verarbeitet, können Sie nicht textbasierte Daten aus der Zwischenablage kopieren, indem Sie sie mit der Methode ClipData.Item.coerceToText()
konvertieren.
Diese Methode wandelt die Daten in ClipData.Item
in Text um und gibt eine CharSequence
zurück. Der von ClipData.Item.coerceToText()
zurückgegebene Wert basiert auf der Datenform in ClipData.Item
:
- Text
-
Wenn
ClipData.Item
ein Text ist, d. h. wenngetText()
nicht null ist, gibt coerceToText() den Text zurück. - URI
-
Wenn
ClipData.Item
ein URI ist, d. h. wenngetUri()
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, wird von
coerceToText()
ein Textstream zurückgegeben. - 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. - Wenn der URI kein Inhalts-URI ist, gibt
coerceToText()
eine Darstellung des URI zurück. Die Darstellung entspricht der vonUri.toString()
zurückgegebenen.
- Wenn der URI ein Inhalts-URI ist und der Anbieter einen Textstream zurückgeben kann, wird von
- Intent
- Wenn
ClipData.Item
eineIntent
ist, d. h. wenngetIntent()
nicht null ist, wandeltcoerceToText()
sie in einen Intent-URI um und gibt ihn zurück. Die Darstellung entspricht der vonIntent.toUri(URI_INTENT_SCHEME)
zurückgegebenen.
Das Zwischenablage-Framework ist in Abbildung 2 zusammengefasst. Zum Kopieren von Daten fügt eine Anwendung ein ClipData
-Objekt in die globale Zwischenablage ClipboardManager
ein. Das ClipData
enthält ein oder mehrere ClipData.Item
-Objekte und ein ClipDescription
-Objekt. Zum Einfügen von Daten ruft eine Anwendung die ClipData
ab, erhält den MIME-Typ von der ClipDescription
und die Daten von der ClipData.Item
oder vom Inhaltsanbieter, auf den ClipData.Item
verweist.
In die Zwischenablage kopieren
Wenn Sie Daten in die Zwischenablage kopieren möchten, rufen Sie einen Handle für das globale ClipboardManager
-Objekt ab, erstellen Sie ein ClipData
-Objekt und fügen Sie ihm ein ClipDescription
-Objekt und ein oder mehrere ClipData.Item
-Objekte hinzu. Fügen Sie dann das fertige ClipData
-Objekt dem ClipboardManager
-Objekt hinzu. Das wird im folgenden Verfahren näher beschrieben:
- Wenn Sie Daten mit einem Inhalts-URI kopieren, richten Sie einen Contentanbieter ein.
- Rufen Sie die Zwischenablage des Systems ab:
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 Methode wird im Abschnitt Kennzeichnung 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);
-
Für eine Absicht
In diesem Snippet wird ein
Intent
für eine Anwendung erstellt und dann in das Clip-Objekt eingefügt: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
-
Lege das neue Clip-Objekt in die Zwischenablage:
Kotlin
// Set the clipboard's primary clip. clipboard.setPrimaryClip(clip)
Java
// Set the clipboard's primary clip. clipboard.setPrimaryClip(clip);
Feedback beim Kopieren in die Zwischenablage geben
Nutzer erwarten visuelles Feedback, wenn eine App Inhalte in die Zwischenablage kopiert. Bei Nutzern mit Android 13 und höher geschieht dies automatisch. In früheren Versionen muss es manuell implementiert werden.
Ab Android 13 zeigt das System eine standardmäßige visuelle Bestätigung an, wenn Inhalte der Zwischenablage hinzugefügt werden. Die neue Bestätigung hat folgende Auswirkungen:
- Bestätigt, dass die Inhalte erfolgreich kopiert wurden.
- Eine Vorschau der kopierten Inhalte.
Unter Android 12L (API-Level 32) und niedriger sind Nutzer möglicherweise nicht sicher, ob sie Inhalte erfolgreich kopiert haben oder was sie kopiert haben. Mit dieser Funktion werden die verschiedenen Benachrichtigungen standardisiert, die von Apps nach dem Kopieren angezeigt werden, und Nutzer haben mehr Kontrolle über die Zwischenablage.
Doppelte Benachrichtigungen vermeiden
Unter Android 12L (API-Level 32) und niedriger empfehlen wir, Nutzer nach dem Kopieren durch visuelles In-App-Feedback mit einem Widget wie einem Toast
oder einem Snackbar
darüber zu informieren, dass die Aktion erfolgreich war.
Um doppelte Informationen zu vermeiden, empfehlen wir dringend, Toasts oder Snackbars zu entfernen, die nach einer In-App-Kopie für Android 13 und höher angezeigt werden.
Hier 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 App 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. Wenn Sie dieses Flag hinzufügen, werden sensible Inhalte in Android 13 und höher nicht in der visuellen Bestätigung für kopierte Inhalte angezeigt.
Wenn du sensible Inhalte kennzeichnen möchtest, füge der ClipDescription
ein boolesches Extra hinzu. Das gilt für alle Apps 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
Wie bereits beschrieben, können Sie Daten aus der Zwischenablage einfügen, indem Sie das globale Zwischenablageobjekt und das Clipobjekt abrufen, sich die Daten ansehen und die Daten nach Möglichkeit aus dem Clipobjekt in Ihren eigenen Speicher kopieren. In diesem Abschnitt wird ausführlich beschrieben, wie Sie die drei Formen von Zwischenablagedaten einfügen.
Nur-Text einfügen
Wenn Sie nur einfachen Text einfügen möchten, rufen Sie die globale Zwischenablage auf und prüfen Sie, ob sie nur einfachen Text zurückgeben kann. Rufen Sie dann das Clip-Objekt ab und kopieren Sie den Text mit getText()
in Ihren eigenen Speicher, wie in der folgenden Anleitung beschrieben:
- Rufe 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 = "";
- Legen Sie fest, ob Sie die Option „Einfügen“ in der aktuellen Aktivität aktivieren oder deaktivieren möchten. Prüfen Sie, ob die Zwischenablage einen Clip enthält und ob Sie den Datentyp verarbeiten können, der durch den Clip dargestellt wird:
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 einfachen Text enthält. Sie wissen noch nicht, ob er einen Textstring oder einen URI enthält, der auf Nur-Text verweist.
Das folgende Code-Snippet testet dies, zeigt aber nur den Code für die Verarbeitung von Nur-Text an:
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 du feststellst, dass du einen seiner MIME-Typen verarbeiten kannst, erstelle ein ContentResolver
und rufe die entsprechende Methode des Inhaltsanbieters auf, um die Daten abzurufen.
Im Folgenden wird beschrieben, wie Sie Daten von einem Inhaltsanbieter basierend auf einem Inhalts-URI in der Zwischenablage abrufen. Es wird geprüft, ob ein MIME-Typ, den die Anwendung verwenden kann, vom Anbieter verfügbar ist.
-
Deklarieren Sie eine globale Variable, 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";
- Globale Zwischenablage abrufen Sie benötigen außerdem einen Content Resolver, damit Sie auf den Contentanbieter zugreifen können:
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();
- Rufe den primären Clip aus der Zwischenablage ab und hole den 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();
- Prüfe, ob es sich bei dem URI um einen Inhalts-URI handelt, indem du
getType(Uri)
aufrufst. Diese Methode gibt „null“ zurück, wennUri
nicht auf einen gültigen Inhaltsanbieter 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);
- Prüfen Sie, ob der Inhaltsanbieter einen MIME-Typ unterstützt, den die Anwendung versteht. Wenn das der Fall ist, rufe
ContentResolver.query()
auf, um die Daten abzurufen. Der Rückgabewert ist eineCursor
.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 eine Intent einfügen möchten, müssen Sie zuerst die globale Zwischenablage abrufen. Prüfen Sie, ob das ClipData.Item
-Objekt eine Intent
enthält. Rufen Sie dann getIntent()
auf, um die Intent-Anfrage 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 Ihre App auf Zwischenablagedaten zugreift
Unter Android 12 (API-Level 31) und höher zeigt das System normalerweise eine Toast-Mitteilung an, wenn Ihre App getPrimaryClip()
aufruft.
Der Text in der Nachricht hat folgendes Format:
APP pasted from your clipboard
Das System zeigt keine Toast-Mitteilung an, wenn Ihre App eine der folgenden Aktionen ausführt:
- Zugriffe auf
ClipData
über Ihre eigene App - Ihre App greift wiederholt über eine bestimmte App auf
ClipData
zu. Die Benachrichtigung wird nur angezeigt, wenn Ihre App zum ersten Mal auf die Daten dieser App zugreift. - Ruft Metadaten für das Clip-Objekt ab, z. B. durch Aufrufen von
getPrimaryClipDescription()
anstelle vongetPrimaryClip()
.
Inhaltsanbieter zum Kopieren komplexer Daten verwenden
Contentanbieter unterstützen das Kopieren komplexer Daten wie Datenbankeinträge oder Dateistreams. Wenn Sie die Daten kopieren möchten, legen Sie einen Inhalts-URI in die Zwischenablage. Anwendungen zum Einfügen rufen diesen URI dann aus der Zwischenablage ab und verwenden ihn, um Datenbankdaten oder Dateistream-Beschreibungen abzurufen.
Da die App zum Einfügen nur den Inhalts-URI für Ihre Daten hat, muss sie wissen, welche Daten abgerufen werden sollen. Sie können diese Informationen angeben, indem Sie eine Kennung für die Daten im URI selbst codieren. Sie können auch einen eindeutigen URI angeben, der die Daten zurückgibt, die Sie kopieren möchten. Welche Methode Sie wählen, hängt von der Organisation Ihrer Daten ab.
In den folgenden Abschnitten wird beschrieben, wie Sie URIs einrichten, komplexe Daten bereitstellen und Dateistreams bereitstellen. In den Beschreibungen wird davon ausgegangen, dass Sie mit den allgemeinen Designprinzipien von Contentanbietern vertraut sind.
Eine Kennung im URI codieren
Eine nützliche Methode zum Kopieren von Daten mit einem URI in die Zwischenablage besteht darin, eine Kennung für die Daten im URI selbst zu codieren. Der Inhaltsanbieter kann dann die Kennung aus dem URI abrufen und damit die Daten abrufen. Die App, in die eingefügt wird, muss nicht wissen, dass die Kennung vorhanden ist. Es muss nur die „Referenz“ – den URI und die Kennung – aus der Zwischenablage abrufen, an den Inhaltsanbieter weitergeben und die Daten zurückgeben.
Normalerweise wird eine Kennung in einen Inhalts-URI codiert, indem sie an das Ende des URI angehängt wird. Angenommen, Sie definieren den Anbieter-URI als folgenden String:
"content://com.example.contacts"
Wenn Sie einen Namen in 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, sollten Sie einen neuen URI-Pfad hinzufügen, der angibt, dass der URI zum Kopieren dient. 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 „Kopie“-URI durch Musterabgleich erkennen und mit Code verarbeiten, der speziell für das Kopieren und Einfügen vorgesehen ist.
Normalerweise verwenden Sie die Codierungstechnik, wenn Sie bereits einen Contentanbieter, eine interne Datenbank oder eine interne Tabelle zum Organisieren Ihrer Daten verwenden. In diesen Fällen haben Sie mehrere Daten, die Sie kopieren möchten, und vermutlich eine eindeutige Kennung für jedes Datenelement. Als Antwort auf eine Abfrage der App zum Einfügen können Sie die Daten anhand ihrer Kennung suchen und zurückgeben.
Wenn Sie nicht mehrere Daten haben, müssen Sie wahrscheinlich keine Kennung codieren. Sie können einen URI verwenden, der für Ihren Anbieter eindeutig ist. Als Antwort auf eine Abfrage gibt Ihr Anbieter die aktuell enthaltenen Daten zurück.
Datenstrukturen kopieren
Richten Sie einen Inhaltsanbieter zum Kopieren und Einfügen komplexer Daten als Unterklasse der ContentProvider
-Komponente ein. Codieren Sie den URI, den Sie in die Zwischenablage kopiert haben, so, dass er genau auf den Datensatz verweist, den Sie angeben möchten. Berücksichtigen Sie außerdem den aktuellen Status Ihrer Anwendung:
- Wenn Sie bereits einen Inhaltsanbieter haben, können Sie dessen Funktionen erweitern. Möglicherweise müssen Sie nur die
query()
-Methode ändern, um URIs von Anwendungen zu verarbeiten, die Daten einfügen möchten. Sie sollten die Methode wahrscheinlich so ändern, dass sie mit einem URI-Muster für „copy“ (Kopie) umgeht. - Wenn Ihre Anwendung eine interne Datenbank verwaltet, sollten Sie diese Datenbank in einen Contentanbieter verschieben, um das Kopieren aus der Datenbank zu erleichtern.
- Wenn Sie keine Datenbank verwenden, können Sie einen einfachen Contentanbieter implementieren, dessen einziger Zweck darin besteht, Daten für Anwendungen bereitzustellen, die Inhalte aus der Zwischenablage einfügen.
Überschreiben Sie im Contentanbieter mindestens die folgenden Methoden:
-
query()
- Anwendungen zum Einfügen gehen davon aus, dass sie Ihre Daten mit dieser Methode und dem URI abrufen können, den Sie in die Zwischenablage kopiert haben. Damit das Kopieren unterstützt wird, muss diese Methode URIs erkennen, die einen speziellen Pfad „copy“ enthalten. Ihre Anwendung kann dann einen „Kopie“-URI erstellen, der in die Zwischenablage kopiert wird. Er enthält den Kopierpfad und einen Verweis auf den genauen Datensatz, den Sie kopieren möchten.
-
getType()
- Diese Methode muss die MIME-Typen für die Daten zurückgeben, 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.
Du benötigst keine der anderen Methoden für Contentanbieter wie insert()
oder update()
.
Eine Anwendung zum Einfügen muss nur die von Ihnen unterstützten MIME-Typen abrufen und Daten von Ihrem Anbieter kopieren.
Wenn Sie diese Methoden bereits verwenden, wirken sie sich nicht auf Kopiervorgänge aus.
Die folgenden Snippets zeigen, wie Sie Ihre Anwendung zum Kopieren komplexer Daten einrichten:
-
Deklarieren Sie in den globalen Konstanten Ihrer Anwendung einen Basis-URI-String und einen Pfad, der URI-Strings identifiziert, die Sie zum Kopieren von Daten verwenden. Deklarieren Sie auch 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 die Daten in die Zwischenablage kopiert werden.
Füge als Antwort auf eine Kopieranfrage den URI 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);
-
Erstelle im globalen Umfang deines Inhaltsanbieters einen URI-Abgleich und füge ein URI-Muster hinzu, das mit URIs übereinstimmt, die du in die Zwischenablage kopierst.
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 das Kopieren 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 die kopierten 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 zum Abrufen und Einfügen von Daten verwenden.
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 Contentanbieter für Datenstreams stellt den Zugriff auf seine Daten über ein Dateideskriptorobjekt wie AssetFileDescriptor
anstelle eines Cursor
-Objekts bereit. Die Anwendung zum Einfügen liest den Datenstream mit diesem Dateideskriptor.
So richten Sie Ihre Anwendung so ein, dass ein Datenstream mit einem Anbieter kopiert wird:
-
Richten Sie einen Content-URI für den Datenstream ein, den Sie in die Zwischenablage kopieren. Dazu haben Sie folgende Möglichkeiten:
- Codieren Sie eine Kennung für den Datenstream in den URI, wie im Abschnitt Kennung im URI codieren beschrieben, und verwalten Sie dann eine Tabelle bei Ihrem Anbieter, die Kennungen und den entsprechenden Streamnamen enthält.
- Codiere den Streamnamen direkt im URI.
- Verwenden Sie einen eindeutigen URI, der immer den aktuellen Stream vom Anbieter zurückgibt. Wenn du diese Option verwendest, musst du deinen Anbieter jedes Mal aktualisieren, wenn du den Stream über den URI in die Zwischenablage kopierst, damit er auf einen anderen Stream verweist.
- Geben Sie für jeden Datenstream, den Sie anbieten möchten, einen MIME-Typ an. Anwendungen zum Einfügen benötigen diese Informationen, um zu ermitteln, ob sie die Daten in die Zwischenablage einfügen können.
- Implementieren Sie eine der
ContentProvider
-Methoden, die einen Dateideskriptor für einen Stream zurückgibt. Wenn du IDs im Inhalts-URI codierst, kannst du 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 die Zwischenablage.
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 ContentResolver
-Dateiendpunktmethode, die den Stream öffnet. Die Methode ContentResolver
ruft die entsprechende Methode ContentProvider
auf und übergibt ihr den Inhalts-URI. Ihr Anbieter gibt den Dateideskriptor an die ContentResolver
-Methode zurück. Die Anwendung, in die die Daten eingefügt werden, ist dann dafür verantwortlich, die Daten aus dem Stream zu lesen.
In der folgenden Liste sind die wichtigsten Methoden für Dateideskriptoren für einen Inhaltsanbieter aufgeführt. Jede davon hat eine entsprechende ContentResolver
-Methode, an den Methodennamen ist der String „Descriptor“ angehängt. Beispiel: Das ContentResolver
-Analogon von openAssetFile()
ist openAssetFileDescriptor()
.
-
openTypedAssetFile()
-
Diese Methode gibt einen Asset-Dateideskriptor zurück, aber nur, wenn der angegebene MIME-Typ vom Anbieter unterstützt wird. Der Aufrufer – die Anwendung, die das Einfügen ausführt – stellt ein MIME-Typmuster bereit. Der Inhaltsanbieter der Anwendung, die einen URI in die Zwischenablage kopiert, gibt einen
AssetFileDescriptor
-Datei-Handle zurück, wenn er diesen MIME-Typ angeben kann, und wirft eine Ausnahme, wenn dies nicht möglich ist.Mit dieser Methode werden Teilbereiche von Dateien verarbeitet. Du kannst damit Assets lesen, die der Inhaltsanbieter in die Zwischenablage kopiert hat.
-
openAssetFile()
-
Diese Methode ist eine allgemeinere Form von
openTypedAssetFile()
. Es wird nicht nach zulässigen MIME-Typen gefiltert, aber es kann Teilbereiche von Dateien lesen. -
openFile()
-
Dies ist eine allgemeinere Form von
openAssetFile()
. Es können keine Teilbereiche von Dateien gelesen werden.
Optional können Sie die Methode openPipeHelper()
mit Ihrer Methode für den Dateideskriptor verwenden. So kann die App zum Einfügen die Streamdaten in einem Hintergrund-Thread über eine Pipe lesen. Wenn Sie diese Methode verwenden möchten, müssen Sie die ContentProvider.PipeDataWriter
-Oberfläche implementieren.
Effektive Kopier- und Einfügefunktionen entwerfen
Beachten Sie die folgenden Punkte, um eine effektive Funktion zum Kopieren und Einfügen für Ihre Anwendung zu entwerfen:
- In der Zwischenablage kann sich immer nur ein Clip befinden. Bei einem neuen Kopiervorgang durch eine beliebige Anwendung im System wird der vorherige Clip überschrieben. Da der Nutzer möglicherweise Ihre Anwendung verlässt und etwas 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.
-
Mehrere
ClipData.Item
-Objekte pro Clip sollen das Kopieren und Einfügen mehrerer Auswahlen unterstützen, nicht verschiedene Formen der Verweisung auf eine einzelne Auswahl. Normalerweise sollten alleClipData.Item
-Objekte in einem Clip dieselbe Form haben. Das heißt, sie müssen alle aus einfachem Text, einem Inhalts-URI oderIntent
bestehen und dürfen nicht gemischt werden. -
Sie können verschiedene MIME-Darstellungen bereitstellen. Füge die von dir unterstützten MIME-Typen der
ClipDescription
hinzu und implementiere sie dann bei deinem Contentanbieter. -
Wenn Sie Daten aus der Zwischenablage abrufen, ist Ihre Anwendung dafür verantwortlich, die verfügbaren MIME-Typen zu prüfen und dann zu entscheiden, ob und welcher verwendet werden soll. Auch wenn sich ein Clip in der Zwischenablage befindet und der Nutzer das Einfügen anfordert, muss Ihre App das Einfügen nicht ausführen. Führen Sie die Einfügung aus, wenn der MIME-Typ kompatibel ist. Mit
coerceToText()
können Sie die Daten in der Zwischenablage in Text umwandeln. Wenn Ihre Anwendung mehrere der verfügbaren MIME-Typen unterstützt, können Sie dem Nutzer die Auswahl überlassen.