Android offre un potente framework basato sugli appunti per copiare e incollare. Supporta semplici e tipi di dati complessi, tra cui stringhe di testo, strutture di dati complesse, testo e flussi binari e asset dell'applicazione. Semplici dati di testo vengono memorizzati direttamente negli appunti, mentre i dati complessi vengono archiviati come riferimento che l'applicazione incollata viene risolta con un fornitore di contenuti. Copia in corso... e incolla funziona sia all'interno di un'applicazione che tra applicazioni che implementano il modello di machine learning.
Poiché parte del framework utilizza fornitori di contenuti, questo documento presuppone una certa familiarità con l'API Android Content Provider, descritta in Fornitori di contenuti.
Gli utenti si aspettano un feedback quando copiano i contenuti negli appunti, quindi oltre al framework che consente la copia e incolla, Android mostra agli utenti una UI predefinita durante la copia in Android 13 (livello API 33) e superiori. A causa di questa funzionalità, esiste il rischio di una notifica duplicata. Puoi scoprire di più su questo caso limite nel Evitare di ricevere notifiche duplicate .
![Un'animazione che mostra la notifica relativa agli appunti di Android 13](https://developer.android.com/static/images/about/versions/13/new-copy-paste-UI.gif?authuser=0000&hl=it)
Fornisci un feedback manualmente agli utenti durante la copia in Android 12L (livello API 32) e versioni precedenti. Consulta consigli in merito nel presente documento.
La struttura degli appunti
Quando utilizzi il framework degli appunti, inserisci i dati in un oggetto clip e poi inserisci l'oggetto clip negli appunti a livello di sistema. L'oggetto clip può avere tre forme:
- Testo
- Una stringa di testo. Inserisci la stringa direttamente nell'oggetto clip, che poi inserisci nella appunti. Per incollare la stringa, recupera l'oggetto clip dagli appunti e copia nello spazio di archiviazione dell'applicazione.
- URI
-
Un oggetto
Uri
che rappresenta qualsiasi di URI. Ciò avviene principalmente per copiare dati complessi da un fornitore di contenuti. Per copiare dati, inserisci un oggettoUri
in un oggetto clip e poi inserisci l'oggetto clip appunti. Per incollare i dati, recupera l'oggetto clip, recupera l'oggettoUri
, a un'origine dati, come un fornitore di contenuti, e copia i dati nello spazio di archiviazione dell'applicazione. - Intenzione
-
Un
Intent
. Questo supporta la copia delle scorciatoie delle applicazioni. Per copiare i dati, crea unIntent
, metti in un oggetto clip e inserirlo negli appunti. Per incollare i dati, ottieni l'oggetto clip e quindi copia l'oggettoIntent
nel file l'area di memoria.
Gli appunti contengono un solo oggetto clip alla volta. Quando un'applicazione inserisce un oggetto clip appunti, l'oggetto clip precedente scompare.
Se vuoi consentire agli utenti di incollare dati nella tua applicazione, non devi gestire tutti i tipi di e i dati di Google Cloud. Puoi esaminare i dati negli appunti prima di offrire agli utenti l'opzione per incollarli. Oltre ad avere un determinato modulo dati, l'oggetto clip contiene anche metadati che indicano quale disponibili. Questi metadati ti aiutano a decidere se la tua applicazione può fare qualcosa di utile con i dati degli appunti. Ad esempio, se hai un'applicazione che gestisce principalmente il testo, potrebbe voler ignorare gli oggetti clip che contengono un URI o un intent.
Potresti anche consentire agli utenti di incollare il testo a prescindere dalla forma dei dati negli appunti. A forzare l'inserimento dei dati degli appunti in una rappresentazione testuale, quindi incollare il testo. Questo è descritto nella sezione Convertire gli appunti in testo.
Lezioni per appunti
Questa sezione descrive i corsi utilizzati dal framework degli appunti.
Gestione appunti
Gli appunti del sistema Android sono rappresentati dal menu
ClipboardManager
corso.
Non creare un'istanza direttamente per questo corso. Per farvi riferimento, richiama
getSystemService(CLIPBOARD_SERVICE)
.
ClipData, ClipData.Item e ClipDescription
Per aggiungere dati agli appunti, crea un
ClipData
oggetto che contiene
una descrizione dei dati e dei dati stessi. Gli appunti contengono un ClipData
per
nel tempo. Un ClipData
contiene un
Oggetto ClipDescription
e uno o più
ClipData.Item
oggetti.
Un oggetto ClipDescription
contiene metadati relativi al clip. In particolare,
contiene un array di tipi MIME disponibili per i dati del clip. Inoltre, su
In Android 12 (livello API 31) e versioni successive, i metadati includono informazioni che indicano se l'oggetto
contiene
testo stilizzato e informazioni
tipo di testo nell'oggetto.
Quando inserisci un clip negli appunti, questa informazione è disponibile per incollare le applicazioni,
esaminare se sono in grado di gestire i dati dei clip.
Un oggetto ClipData.Item
contiene il testo, l'URI o i dati di intent:
- Testo
-
Un
CharSequence
. - URI
-
Un
Uri
. In genere contiene l'URI di un fornitore di contenuti, anche se qualsiasi URI è consentito. L'applicazione che fornisce i dati inserisce l'URI negli appunti. Applicazioni che vogliono incollare i dati, recupera l'URI dagli appunti e lo utilizzi per accedere ai contenuti o un'altra origine dati e recuperarli. - Intenzione
-
Un
Intent
. Questo tipo di dati ti consente di copiare una scorciatoia applicazione nel appunti. Gli utenti potranno quindi incollare la scorciatoia nelle proprie applicazioni per utilizzarla in un secondo momento.
Puoi aggiungere più di un oggetto ClipData.Item
a un clip. Questo consente agli utenti di copiare
incolla più selezioni come un singolo clip. Ad esempio, se si dispone di un widget elenco che consente ai
se selezioni più di un elemento alla volta, puoi copiare tutti gli elementi negli appunti contemporaneamente. Da fare
crea un valore ClipData.Item
distinto per ogni elemento dell'elenco, quindi aggiungi
ClipData.Item
all'oggetto ClipData
.
Metodi di praticità di ClipData
La classe ClipData
fornisce metodi statici per creare una
ClipData
oggetto con un singolo oggetto ClipData.Item
e un semplice
Oggetto ClipDescription
:
-
newPlainText(label, text)
- Restituisci un oggetto
ClipData
il cui singolo oggettoClipData.Item
contiene una stringa di testo. L'etichetta dell'oggettoClipDescription
è impostata sulabel
. L'unico tipo MIME inClipDescription
èMIMETYPE_TEXT_PLAIN
.Usa
newPlainText()
per creare un clip da una stringa di testo. -
newUri(resolver, label, URI)
- Restituisci un oggetto
ClipData
il cui singolo oggettoClipData.Item
contiene un URI. L'etichetta dell'oggettoClipDescription
è impostata sulabel
. Se l'URI è un URI dei contenuti, ovveroUri.getScheme()
restituiscecontent:
: il metodo utilizzaContentResolver
fornito inresolver
per recuperare i tipi MIME disponibili dal fornitore di contenuti. Poi le archivia inClipDescription
. Per un URI diverso da un URIcontent:
, il metodo imposta il tipo MIME suMIMETYPE_TEXT_URILIST
.Usa
newUri()
per creare un clip da un URI, in particolare una URIcontent:
. -
newIntent(label, intent)
- Restituisci un oggetto
ClipData
il cui singolo oggettoClipData.Item
contiene unIntent
. L'etichetta dell'oggettoClipDescription
è impostata sulabel
. Il tipo MIME è impostato suMIMETYPE_TEXT_INTENT
.Usa
newIntent()
per creare un clip da un oggettoIntent
.
Converti i dati degli appunti in testo
Anche se la tua applicazione gestisce solo il testo, puoi copiare dagli appunti i dati non testuali
convertendolo con
ClipData.Item.coerceToText()
.
Questo metodo converte i dati in ClipData.Item
in testo e restituisce un
CharSequence
. Il valore restituito da ClipData.Item.coerceToText()
è basato
sotto forma di dati in ClipData.Item
:
- Testo
-
Se
ClipData.Item
è testo, ovvero segetText()
non è nullo: CoerceToText() restituisce il testo. - URI
-
Se
ClipData.Item
è un URI, ovvero segetUri()
non è nullo:coerceToText()
cerca di utilizzarlo come URI dei contenuti.- Se l'URI è un URI di contenuti e il provider può restituire uno stream di testo,
coerceToText()
restituisce uno stream di testo. - Se l'URI è un URI di contenuti, ma il provider non offre uno stream di testo,
coerceToText()
restituisce una rappresentazione dell'URI. La rappresentazione è uguale a quello restituitoUri.toString()
. - Se l'URI non è un URI dei contenuti,
coerceToText()
restituisce una rappresentazione di l'URI. La rappresentazione è la stessa di quella restituita daUri.toString()
.
- Se l'URI è un URI di contenuti e il provider può restituire uno stream di testo,
- Intenzione
- Se
ClipData.Item
è unIntent
, ovvero segetIntent()
non è nullo:coerceToText()
lo converte in un URI di intent e lo restituisce. La rappresentazione è la stessa di quella restituita daIntent.toUri(URI_INTENT_SCHEME)
.
Il framework degli appunti è riassunto nella figura 2. Per copiare i dati, un'applicazione inserisce
Oggetto ClipData
negli appunti globali ClipboardManager
. La
ClipData
contiene uno o più oggetti ClipData.Item
e uno
ClipDescription
oggetto. Per incollare i dati, un'applicazione riceve ClipData
,
ottiene il tipo MIME dal ClipDescription
e recupera i dati dal
ClipData.Item
o dal fornitore di contenuti indicato da
ClipData.Item
.
![Un'immagine che mostra un diagramma a blocchi del framework di copia e incolla](https://developer.android.com/static/images/ui/clipboard/copy_paste_framework.png?authuser=0000&hl=it)
Copia negli appunti
Per copiare i dati negli appunti, ottieni un handle nell'oggetto ClipboardManager
globale,
crea un oggetto ClipData
e aggiungi un ClipDescription
e uno o più
ClipData.Item
oggetti. Quindi, aggiungi l'oggetto ClipData
completato al
Oggetto ClipboardManager
. Questa procedura viene descritta in modo più approfondito nella procedura seguente:
- Se copi i dati utilizzando un URI dei contenuti, configura un fornitore di contenuti.
- Scarica gli appunti di sistema:
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);
-
Copia i dati in un nuovo oggetto
ClipData
:-
Per il testo
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!");
-
Per un URI
Questo snippet costruisce un URI codificando un ID record nell'URI dei contenuti per il fornitore. Questa tecnica viene trattata più in dettaglio nei Codifica di un identificatore nell'URI.
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);
-
Per intenzione
Questo snippet crea un valore
Intent
per un'applicazione e inserisce nell'oggetto clip: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);
-
Per il testo
-
Inserisci il nuovo oggetto clip negli appunti:
Kotlin
// Set the clipboard's primary clip. clipboard.setPrimaryClip(clip)
Java
// Set the clipboard's primary clip. clipboard.setPrimaryClip(clip);
Fornisci un feedback durante la copia negli appunti
Gli utenti si aspettano un feedback visivo quando un'app copia i contenuti negli appunti. Fatto automaticamente per gli utenti con Android 13 e versioni successive, ma deve essere implementato manualmente nelle versioni precedenti e versioni successive.
A partire da Android 13, il sistema mostra una conferma visiva standard quando vengono aggiunti contenuti negli appunti. La nuova conferma comporta quanto segue:
- Conferma che i contenuti sono stati copiati correttamente.
- Fornisce un'anteprima dei contenuti copiati.
![Un'animazione che mostra la notifica relativa agli appunti di Android 13](https://developer.android.com/static/images/about/versions/13/new-copy-paste-UI.gif?authuser=0000&hl=it)
In Android 12L (livello API 32) e versioni precedenti, gli utenti potrebbero non essere sicuri di aver copiato correttamente contenuti o ciò che hanno copiato. Questa funzionalità standardizza le varie notifiche mostrate dalle app dopo copia e offre agli utenti un maggiore controllo sugli appunti.
Evita le notifiche duplicate
In Android 12L (livello API 32) e versioni precedenti, consigliamo di avvisare gli utenti quando copiano correttamente
inviando un feedback visivo in-app, usando un widget come Toast
o
un Snackbar
, dopo la copia.
Per evitare la visualizzazione duplicata di informazioni, consigliamo vivamente di rimuovere i toast. o snackbar mostrate dopo un testo in-app per Android 13 e versioni successive.
![Pubblica snackbar dopo una copia in-app.](https://developer.android.com/static/images/about/versions/13/snackbar-overlap.png?authuser=0000&hl=it)
![Pubblicare toast dopo una copia in-app.](https://developer.android.com/static/images/about/versions/13/toast-overlap.png?authuser=0000&hl=it)
Ecco un esempio di come implementare questa funzionalità:
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() }
Aggiungi contenuti sensibili agli appunti
Se l'app consente agli utenti di copiare negli appunti contenuti sensibili, come password o credito
dati della carta, devi aggiungere un flag a ClipDescription
in ClipData
prima di chiamare ClipboardManager.setPrimaryClip()
. L'aggiunta di questo flag impedisce le
che i contenuti vengano mostrati nella conferma visiva di contenuti copiati in Android 13 e versioni successive.
![Anteprima del testo copiata senza segnalare i contenuti sensibili](https://developer.android.com/static/images/about/versions/13/sensitive-content-before.png?authuser=0000&hl=it)
![Anteprima del testo copiato che segnala contenuti sensibili.](https://developer.android.com/static/images/about/versions/13/sensitive-content-after.png?authuser=0000&hl=it)
Per segnalare contenuti sensibili, aggiungi un extra booleano alla ClipDescription
. Tutte le app devono fare
a prescindere dal livello API target.
// 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) } }
Incolla dagli appunti
Come descritto in precedenza, incolla i dati dagli appunti recuperando l'oggetto Appunti globale, recuperando l'oggetto clip, esaminandone i dati e, se possibile, copiandoli dall'oggetto clip spazio di archiviazione a tua disposizione. Questa sezione spiega in dettaglio come incollare i tre tipi di appunti e i dati di Google Cloud.
Incolla testo normale
Per incollare testo normale, scarica gli appunti globali e verifica che sia in grado di restituire testo normale. Quindi ottieni
l'oggetto clip e copiane il testo nel tuo spazio di archiviazione utilizzando getText()
, come descritto in
la seguente procedura:
- Ottieni l'oggetto
ClipboardManager
globale utilizzandogetSystemService(CLIPBOARD_SERVICE)
. Inoltre, dichiara che una variabile globale il testo incollato:Kotlin
var clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager var pasteData: String = ""
Java
ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE); String pasteData = "";
- Determina se è necessario abilitare o disabilitare la funzione "Incolla" nel modello
attività. Verifica che gli appunti contengano un clip e che sia possibile gestire il tipo di dati
rappresentato dal clip:
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); }
- Copia i dati dagli appunti. Questo punto del codice è raggiungibile solo se
"Incolla" la voce di menu è attiva, quindi puoi presumere che gli appunti contengano
testo. Non sai ancora se contiene una stringa di testo o un URI che rimanda al testo normale.
Lo snippet di codice riportato di seguito verifica questo aspetto, ma mostra solo il codice per la gestione del testo normale:
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; } }
Incolla i dati da un URI dei contenuti
Se l'oggetto ClipData.Item
contiene un URI dei contenuti e stabilisci che puoi
gestire uno dei relativi tipi MIME, creare un ContentResolver
e richiamare i contenuti appropriati
provider per recuperare i dati.
La seguente procedura descrive come ottenere i dati da un fornitore di contenuti in base a un URI dei contenuti negli appunti. Verifica se un tipo MIME che l'applicazione può utilizzare è disponibile dalla o il provider di servizi di terze parti.
-
Dichiara che una variabile globale contiene il tipo MIME:
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";
- Scarica gli appunti globali. Richiedi anche un resolver di contenuti per poter accedere ai contenuti
fornitore:
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();
- Scarica il clip principale dagli appunti e recupera i contenuti come URI:
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();
- Verifica se l'URI è un URI dei contenuti richiamando
getType(Uri)
. Questo metodo restituisce un valore nullo seUri
non punta a un fornitore di contenuti valido.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);
- Verifica se il fornitore di contenuti supporta un tipo MIME riconosciuto dall'applicazione. Se
chiama
ContentResolver.query()
per ottenere i dati. Il valore restituito èCursor
.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(); } } } }
Incolla un intent
Per incollare un intent, devi prima recuperare gli appunti globali. Esamina l'oggetto ClipData.Item
per vedere se contiene un Intent
. Quindi chiama getIntent()
per copiare
per il tuo spazio di archiviazione. Lo snippet seguente dimostra che:
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. }
Notifica di sistema mostrata quando l'app accede ai dati degli appunti
Su Android 12 (livello API 31) e versioni successive, in genere il sistema mostra un messaggio toast quando la tua app
chiamate
getPrimaryClip()
Il testo all'interno del messaggio ha il seguente formato:
APP pasted from your clipboard
Il sistema non mostra un messaggio toast quando l'app esegue una delle seguenti operazioni:
- Accessi
ClipData
dalla tua app. - Accede ripetutamente a
ClipData
da un'app specifica. L'avviso popup viene visualizzato solo l'app accede ai dati dell'app per la prima volta. - Recupera i metadati per l'oggetto clip, ad esempio richiamando
getPrimaryClipDescription()
anzichégetPrimaryClip()
.
Utilizzare fornitori di contenuti per copiare dati complessi
I fornitori di contenuti supportano la copia di dati complessi come record di database o stream di file. Per copiare inserisci un URI dei contenuti negli appunti. Le applicazioni incollate riceveranno questo URI dal negli appunti e utilizzarlo per recuperare i dati del database o i descrittori dei flussi di file.
Poiché l'applicazione Incolla dispone solo dell'URI dei contenuti per i dati, deve sapere quale dati da recuperare. Puoi fornire queste informazioni codificando un identificatore per i dati sull'URI stesso oppure puoi fornire un URI univoco che restituisca i dati da copiare. Quale tecnica che scegli dipende dall'organizzazione dei tuoi dati.
Le seguenti sezioni descrivono come impostare gli URI, fornire dati complessi e fornire i flussi di dati. Le descrizioni presuppongono che tu abbia familiarità con i principi generali dei fornitori di contenuti la progettazione.
Codifica un identificatore sull'URI
Una tecnica utile per copiare i dati negli appunti con un URI è la codifica di un identificatore i dati sull'URI stesso. Il fornitore di contenuti può quindi ottenere l'identificatore dall'URI e utilizzare per recuperare i dati. L'applicazione per incollare non deve necessariamente sapere che l'identificatore esiste. it deve recuperare il "riferimento", ovvero l'URI più l'identificatore, appunti, consegnalo al tuo fornitore di contenuti e recupera i dati.
In genere codifichi un identificatore su un URI di contenuti concatenandolo alla fine dell'URI. Ad esempio, supponiamo che tu definisca l'URI del provider come la seguente stringa:
"content://com.example.contacts"
Se vuoi codificare un nome in questo URI, utilizza il seguente snippet di codice:
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);
Se utilizzi già un fornitore di contenuti, ti consigliamo di aggiungere un nuovo percorso URI che indichi l'URI serve per la copia. Ad esempio, supponiamo che tu abbia già i seguenti percorsi URI:
"content://com.example.contacts/people" "content://com.example.contacts/people/detail" "content://com.example.contacts/people/images"
Puoi aggiungere un altro percorso per la copia degli URI:
"content://com.example.contacts/copying"
Puoi quindi rilevare una "copia" tramite pattern-matching e gestisce l'URI con il codice specifici per copiare e incollare.
Normalmente utilizzi la tecnica di codifica se già utilizzi un fornitore di contenuti, un database o una tabella interna per organizzare i dati. In questi casi, hai più dati che desideri copiare e presumibilmente un identificatore univoco per ogni articolo. In risposta a una query da parte di incollando l'applicazione, puoi cercare i dati in base al relativo identificatore e restituirli.
Se non disponi di più dati, probabilmente non è necessario codificare un identificatore. Puoi utilizzare un URI univoco del tuo provider. In risposta a una query, il tuo provider restituisce che contiene.
Copia strutture di dati
Imposta un fornitore di contenuti per copiare e incollare dati complessi come una sottoclasse del
ContentProvider
di strumento di authoring. Codifica l'URI che inserisci negli appunti in modo che rimandi al record esatto che vuoi
che forniscono. Inoltre, considera lo stato esistente della tua applicazione:
- Se hai già un fornitore di contenuti, puoi aumentare le sue funzionalità. Puoi solo
deve modificare il metodo
query()
per gestire gli URI provenienti dalle applicazioni che vuoi incollare i dati. Probabilmente vorrai modificare il metodo per gestire una "copia" URI pattern. - Se la tua applicazione gestisce un database interno, ti consigliamo di spostarlo a un fornitore di contenuti per facilitarne la copia.
- Se non utilizzi un database, puoi implementare un semplice fornitore di contenuti il cui il solo scopo è offrire dati alle applicazioni che incollano dati dagli appunti.
Nel fornitore di contenuti, sostituisci almeno i seguenti metodi:
-
query()
- Le applicazioni incollate presuppongono che possano ottenere i dati utilizzando questo metodo con l'URI mettere negli appunti. Per supportare la copia, fai in modo che questo metodo rilevi gli URI contenenti un tag "copia" del tuo percorso di apprendimento. L'applicazione può quindi creare una "copia" URI da inserire , contenenti il percorso di copia e un puntatore al record esatto da copiare.
-
getType()
- Questo metodo deve restituire i tipi MIME dei dati che intendi copiare. Il metodo
newUri()
chiamagetType()
per inserire i tipi MIME nel nuovoClipData
.I tipi MIME per i dati complessi sono descritti in Fornitori di contenuti.
Non è necessario disporre di altri metodi per i fornitori di contenuti, come
insert()
o
update()
.
Un'applicazione che consente di incollare solo gli elementi è sufficiente per recuperare i tipi MIME supportati e copiare i dati dal provider.
Se utilizzi già questi metodi, non interferiranno con le operazioni di copia.
I seguenti snippet mostrano come configurare la tua applicazione per copiare dati complessi:
-
Nelle costanti globali dell'applicazione, dichiara una stringa URI di base e un percorso identifica le stringhe URI che utilizzi per copiare i dati. Dichiara anche un tipo MIME per il file e i dati di Google Cloud.
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";
- Nell'attività da cui gli utenti copiano i dati, configura il codice per copiare i dati negli appunti.
In risposta a una richiesta di copia, inserisci l'URI negli appunti.
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);
-
Nell'ambito globale del tuo fornitore di contenuti, crea un matcher URI e aggiungi un pattern URI che corrisponde agli URI che hai inserito negli appunti.
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);
-
Imposta
query()
. Questo metodo può gestire diversi pattern URI, a seconda di come lo codifichi, ma viene visualizzata la sequenza dell'operazione di copia degli appunti.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. ... }
-
Configura il metodo
getType()
in modo che restituisca un tipo MIME appropriato per la copia dati: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); ... } }
La sezione Incolla i dati da un URI dei contenuti descrive come ottenere un l'URI dei contenuti contenuto negli appunti e utilizzarlo per recuperare e incollare i dati.
Copia stream di dati
Puoi copiare e incollare grandi quantità di testo e dati binari come flussi. I dati possono avere formati ad esempio:
- I file archiviati sul dispositivo reale
- Flussi da socket
- Grandi quantità di dati archiviati nel sistema di database sottostante di un provider
Un fornitore di contenuti per stream di dati fornisce l'accesso ai suoi dati con un oggetto descrittore di file,
come
AssetFileDescriptor
,
anziché un oggetto Cursor
. L'applicazione Incolla legge lo stream di dati utilizzando
come descrittore del file.
Per configurare l'applicazione in modo che copi uno stream di dati con un provider, segui questi passaggi:
-
Configura un URI dei contenuti per lo stream di dati che stai inserendo negli appunti. Opzioni
a tal fine:
- Codifica un identificatore per lo stream di dati nell'URI, come descritto in Codifica un identificatore nell'URI sezione, quindi mantieni un nel provider che contiene gli identificatori e il nome dello stream corrispondente.
- Codifica il nome del flusso direttamente sull'URI.
- Utilizza un URI univoco che restituisca sempre il flusso attuale del provider. Se usa questa opzione, ricordati di aggiornare il provider in modo che rimandi a uno stream diverso ogni volta che copi il flusso negli appunti utilizzando l'URI.
- Fornisci un tipo MIME per ogni tipo di stream di dati che intendi offrire. Incollamento applicazioni queste informazioni servono per stabilire se è possibile incollare i dati negli appunti.
- Implementa uno dei metodi
ContentProvider
che restituisce un descrittore di file per un flusso. Se codifichi gli identificatori sull'URI dei contenuti, utilizza questo metodo per determinare quali aprire lo stream. - Per copiare lo stream di dati negli appunti, crea l'URI dei contenuti e posizionalo nella appunti.
Per incollare uno stream di dati, un'applicazione ottiene il clip dagli appunti, recupera l'URI e utilizza
in una chiamata a un metodo descrittore di file ContentResolver
che apre il flusso. La
Il metodo ContentResolver
chiama il metodo ContentProvider
corrispondente,
passando l'URI del contenuto. Il tuo provider restituisce il descrittore del file
ContentResolver
. L'applicazione che consente di incollare ha quindi la responsabilità di leggere
i dati dal flusso.
Il seguente elenco mostra i metodi più importanti del descrittore di file per un fornitore di contenuti. Ciascuna
di questi ha un metodo ContentResolver
corrispondente con la stringa
"Descrittore" aggiunto al nome del metodo. Ad esempio, ContentResolver
analogico di
openAssetFile()
sono
openAssetFileDescriptor()
.
-
openTypedAssetFile()
-
Questo metodo restituisce un descrittore del file di asset, ma solo se il tipo MIME fornito è supportate dal provider. Il chiamante, ovvero l'applicazione che esegue l'operazione di incollare, fornisce un pattern di tipo MIME. Il fornitore di contenuti dell'applicazione che copia un URI nella riga gli appunti restituiscono un handle di file
AssetFileDescriptor
se può fornire questo MIME e genera un'eccezione se non riesce.Questo metodo gestisce le sottosezioni dei file. Puoi utilizzarlo per leggere gli asset che il fornitore di contenuti ha copiato negli appunti.
-
openAssetFile()
-
Questo metodo è una forma più generica di
openTypedAssetFile()
. Non filtra per i tipi MIME consentiti, ma può leggere le sottosezioni dei file. -
openFile()
-
Questa è una forma più generica di
openAssetFile()
. Non può leggere le sottosezioni di .
In via facoltativa, puoi utilizzare
openPipeHelper()
con il metodo descrittore del file. Ciò consente all'applicazione Incolla di leggere i dati del flusso in un
utilizzando una barra verticale. Per utilizzare questo metodo, implementa il metodo
ContentProvider.PipeDataWriter
a riga di comando.
Progetta funzionalità di copia e incolla efficaci
Per progettare una funzionalità di copia e incolla efficace per la tua applicazione, ricorda i seguenti punti:
- C'è un solo clip negli appunti in qualsiasi momento. Una nuova operazione di copia da parte di l'applicazione nel sistema sovrascrive il clip precedente. Dato che l'utente potrebbe navigare dall'applicazione e copiarlo prima di tornare, non puoi usare gli appunti contiene il clip che l'utente ha precedentemente copiato nella tua applicazione.
-
Lo scopo previsto di più oggetti
ClipData.Item
per clip è supportano il copia e incolla di più selezioni anziché forme diverse di riferimento a una singola selezione. In genere vuoi tutti gliClipData.Item
in un clip per avere la stessa forma. Ovvero, devono essere tutti testo semplice, URI oIntent
e non è misto. -
Quando fornisci i dati, puoi offrire rappresentazioni MIME diverse. Aggiungere i tipi MIME
che supporti
ClipDescription
e poi implementi i tipi MIME in il tuo fornitore di contenuti. -
Quando recupera i dati dagli appunti, l'applicazione è responsabile di controllare lo
tipi MIME disponibili e poi decidere quale utilizzare. Anche se è presente
clip negli appunti e l'utente richiede di incollarlo, l'applicazione non è necessaria
per incollare. Esegui la copia se il tipo MIME è compatibile. Potresti forzare i dati
negli appunti per convertire il testo utilizzando
coerceToText()
. Se la tua applicazione supporta più di uno dei tipi MIME disponibili, puoi consentire all'utente di scegliere quale utilizzare.