Android zapewnia zaawansowaną platformę opartą na schowku do kopiowania i wklejania. Obsługuje ona proste i złożonych typów danych, w tym ciągów tekstowych, złożonych struktur danych, strumieni tekstowych i binarnych dane i zasoby aplikacji. Proste dane tekstowe są przechowywane bezpośrednio w schowku, a bardziej złożone są przechowywane jako odnośniki, do których aplikacja do wklejania rozwiązuje problem z dostawcą treści. Kopiuję i wklejania, działa zarówno w aplikacji, jak i między aplikacjami, które implementują platformy.
Ponieważ część platformy korzysta z usług dostawców treści, w tym dokumencie zakładamy, interfejsu API dostawcy treści na Androida, który jest opisany w Dostawcy treści.
Użytkownicy oczekują informacji zwrotnych podczas kopiowania treści do schowka, więc oprócz platformy, umożliwia kopiowanie i wklejanie, Android wyświetla użytkownikom domyślny interfejs użytkownika podczas kopiowania w Androidzie 13 (poziom API 33) i wyższe. Dzięki tej funkcji istnieje ryzyko zduplikowania powiadomienia. Więcej informacji na temat: to skrajne przypadki w Unikaj zduplikowanych powiadomień .
Ręcznie przekazuj użytkownikom opinie podczas kopiowania w Androidzie 12L (poziom API 32) lub starszym. Zobacz zalecenia na ten temat.
Struktura schowka
Jeśli używasz platformy schowka, umieść dane w obiekcie klipu, a następnie umieść obiekt klipu w schowku systemowym. Obiekt klipu może mieć jedną z 3 form:
- Tekst
- Ciąg tekstowy. Umieść ciąg znaków bezpośrednio w obiekcie klipu, schowku. Aby wkleić ciąg znaków, pobierz obiekt klipu ze schowka i skopiuj do pamięci aplikacji.
- URI
-
Obiekt
Uri
reprezentujący lub identyfikatora URI. Służy to głównie do kopiowania złożonych danych od dostawcy treści. Do skopiowania danych, umieść obiektUri
w obiekcie klipu i umieść go w obiekcie schowku. Aby wkleić dane, pobierz obiekt klipu, pobierz obiektUri
, i przekazywać je do źródła danych, np. dostawcy treści, i skopiować z usługi do pamięci aplikacji. - Zamiar
-
Intent
. Ten obsługuje kopiowanie skrótów aplikacji. Aby skopiować dane, utwórz:Intent
, umieść go w obiekcie klipu i umieść obiekt klipu w schowku. Aby wkleić dane, pobierz Obiekt klipu, a następnie skopiuj obiektIntent
do obszaru pamięci.
Schowek zawiera tylko 1 obiekt klipu naraz. Gdy aplikacja umieszcza obiekt klipu w tagu poprzedni obiekt klipu znika.
Jeśli chcesz pozwolić użytkownikom wklejać dane do Twojej aplikacji, nie musisz obsługiwać wszystkich typów i skalowalnych danych. Możesz przejrzeć dane w schowku, zanim umożliwisz użytkownikom ich wklejenie. Obiekt klipu nie tylko ma określoną formę danych, lecz także zawiera metadane informujące o typie MIME. i typu tagu. Te metadane pomagają określić, czy aplikacja może zrobić coś przydatnego z danymi ze schowka. Na przykład, jeśli Twoja aplikacja obsługuje głównie tekst, ignorują obiekty klipu, które zawierają identyfikator URI lub intencję.
Możesz też zezwolić użytkownikom na wklejanie tekstu niezależnie od postaci danych w schowku. Do wymuś taki sposób zapisywania danych ze schowka, a następnie wklej ten tekst. To jest opisane w sekcji Konwertowanie schowka na tekst.
Klasy w schowku
W tej sekcji opisano klasy używane przez platformę schowka.
Menedżer schowka
Schowek systemu Android jest reprezentowany przez klucz globalny
ClipboardManager
zajęcia.
Nie twórz bezpośrednio tej klasy. Aby się do niej odwoływać, wywołaj
getSystemService(CLIPBOARD_SERVICE)
ClipData, ClipData.Item i ClipDescription
Aby dodać dane do schowka, utwórz
ClipData
obiekt, który zawiera
opis oraz samych danych. Schowek zawiera jeden element ClipData
w
obecnie się znajdujesz. ClipData
zawiera element
ClipDescription
obiekt
i jeszcze jeden
ClipData.Item
obiektów.
Obiekt ClipDescription
zawiera metadane dotyczące klipu. W szczególności
zawiera tablicę dostępnych typów MIME dla danych klipu. Dodatkowo
w Androidzie 12 (poziom interfejsu API 31) lub nowszym w metadanych znajdują się informacje o tym, czy obiekt
zawiera
stylizowany tekst i informacje o nim
typu tekstu w obiekcie.
Po umieszczeniu klipu w schowku informacje te są dostępne dla aplikacji, które
mogą sprawdzić, czy potrafią obsłużyć dane dotyczące klipu.
Obiekt ClipData.Item
zawiera tekst, identyfikator URI lub dane intencji:
- Tekst
-
CharSequence
. - URI
-
Uri
. Zwykle zawiera on identyfikator URI dostawcy treści, chociaż każdy identyfikator URI to jest dozwolona. Aplikacja udostępniająca dane umieszcza identyfikator URI w schowku. Aplikacje którzy chcą wkleić dane, pobierz identyfikator URI ze schowka i użyj go, aby uzyskać dostęp do treści lub innego źródła danych i pobierać dane. - Zamiar
-
Intent
. Ten typ danych pozwala skopiować skrót aplikacji do sekcji schowku. Użytkownicy mogą wkleić ten skrót do aplikacji, aby użyć go w przyszłości.
Do klipu możesz dodać więcej niż 1 obiekt ClipData.Item
. Dzięki temu użytkownicy mogą kopiować
wklej wiele zaznaczonych elementów jako pojedynczy klip. Jeśli na przykład masz widżet listy, który umożliwia
użytkownik wybierze więcej niż jeden element naraz, możesz skopiować wszystkie elementy do schowka naraz. Do zrobienia
utwórz osobne pola ClipData.Item
dla każdego elementu listy, a następnie dodaj
ClipData.Item
obiektów do obiektu ClipData
.
Wygodne metody ClipData
Klasa ClipData
udostępnia statyczne, wygodne metody tworzenia
ClipData
z jednym obiektem ClipData.Item
i prostym
Obiekt ClipDescription
:
-
newPlainText(label, text)
- Zwraca obiekt
ClipData
, którego pojedynczy obiektClipData.Item
zawiera ciąg tekstowy. Etykieta obiektuClipDescription
jest ustawiona nalabel
Pojedynczy typ MIME w językuClipDescription
toMIMETYPE_TEXT_PLAIN
Użyj
newPlainText()
, aby utworzyć klip na podstawie ciągu tekstowego. -
newUri(resolver, label, URI)
- Zwraca obiekt
ClipData
, którego pojedynczy obiektClipData.Item
zawiera identyfikator URI. Etykieta obiektuClipDescription
jest ustawiona nalabel
Jeśli identyfikator URI jest identyfikatorem URI treści, czyliUri.getScheme()
zwracacontent:
– metoda korzysta z funkcjiContentResolver
można pobrać obiekt wresolver
, aby pobrać dostępne typy MIME z dostawcy treści. Następnie zapisuje je w usłudzeClipDescription
. W przypadku identyfikatora URI, który nie jest identyfikatora URIcontent:
metoda ustawia typ MIME naMIMETYPE_TEXT_URILIST
Użyj
newUri()
, aby utworzyć klip na podstawie identyfikatora URI, a zwłaszcza Identyfikator URIcontent:
. -
newIntent(label, intent)
- Zwraca obiekt
ClipData
, którego pojedynczy obiektClipData.Item
zawieraIntent
. Etykieta obiektuClipDescription
jest ustawiona nalabel
Typ MIME jest ustawiony naMIMETYPE_TEXT_INTENT
Użyj narzędzia
newIntent()
, aby utworzyć klip z obiektuIntent
.
Konwertuj dane ze schowka na tekst
Nawet jeśli aplikacja obsługuje tylko tekst, możesz skopiować ze schowka dane inne niż tekstowe,
konwertując go za pomocą funkcji
ClipData.Item.coerceToText()
.
Ta metoda konwertuje dane w komórce ClipData.Item
na tekst i zwraca
CharSequence
Wartość zwracana przez funkcję ClipData.Item.coerceToText()
zależy od wartości
w formie danych w ClipData.Item
:
- Tekst
-
Jeśli
ClipData.Item
ma wartość tekstową – czyli jeśligetText()
nie ma wartości null – coerceToText() zwraca tekst. - URI
-
Jeśli
ClipData.Item
jest identyfikatorem URI, czyligetUri()
nie ma wartości null –coerceToText()
próbuje użyć go jako identyfikatora URI treści.- Jeśli identyfikator URI to identyfikator URI treści, a dostawca może zwrócić strumień tekstowy,
coerceToText()
zwraca strumień tekstu. - Jeśli identyfikator URI to identyfikator URI treści, ale dostawca nie oferuje strumienia tekstowego,
coerceToText()
zwraca reprezentację identyfikatora URI. Ta reprezentacja jest tyle samo co zwrócone przezUri.toString()
- Jeśli identyfikator URI nie jest identyfikatorem URI treści,
coerceToText()
zwraca reprezentację identyfikator URI. Reprezentacja jest taka sama jak ta zwracana przez funkcjęUri.toString()
- Jeśli identyfikator URI to identyfikator URI treści, a dostawca może zwrócić strumień tekstowy,
- Zamiar
- Jeśli
ClipData.Item
toIntent
– to znaczy,getIntent()
nie ma wartości null –coerceToText()
konwertuje go na identyfikator URI intencji i zwraca go. Reprezentacja jest taka sama jak ta zwracana przez funkcjęIntent.toUri(URI_INTENT_SCHEME)
Struktura schowka została przedstawiona na rys. 2. Aby skopiować dane, aplikacja wstawia
Obiekt ClipData
w globalnym schowku ClipboardManager
.
ClipData
zawiera co najmniej jeden obiekt ClipData.Item
i jeden
ClipDescription
obiekt. Aby wkleić dane, aplikacja pobiera: ClipData
,
pobiera typ MIME z ClipDescription
i pobiera dane z
ClipData.Item
lub od dostawcy treści wskazanego przez
ClipData.Item
Skopiuj do schowka
Aby skopiować dane do schowka, pobierz uchwyt do globalnego obiektu ClipboardManager
,
utwórz obiekt ClipData
oraz dodaj do niego ClipDescription
i co najmniej 1 obiekt
ClipData.Item
obiektów. Następnie dodaj gotowy obiekt ClipData
do
ClipboardManager
obiekt. Zostało to opisane bardziej szczegółowo w tej procedurze:
- Jeśli kopiujesz dane za pomocą identyfikatora URI treści, skonfiguruj dostawcę treści.
- Pobierz schowek systemowy:
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);
-
Skopiuj dane do nowego obiektu
ClipData
:-
Tekst
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!");
-
Identyfikator URI
Ten fragment kodu tworzy identyfikator URI przez kodowanie identyfikatora rekordu w identyfikatorze URI treści dla z usługodawcą. Technikę tę omówiono szczegółowo w Kodowanie identyfikatora w sekcji identyfikatora 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);
-
Zamiar
Ten fragment kodu tworzy obiekt
Intent
dla aplikacji, a następnie umieszcza w nim go w obiekcie klipu: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);
-
Tekst
-
Umieść nowy obiekt klipu w schowku:
Kotlin
// Set the clipboard's primary clip. clipboard.setPrimaryClip(clip)
Java
// Set the clipboard's primary clip. clipboard.setPrimaryClip(clip);
Prześlij opinię podczas kopiowania do schowka
Użytkownicy oczekują wizualnego potwierdzenia, gdy aplikacja skopiuje zawartość do schowka. Gotowe automatycznie w przypadku użytkowników Androida 13 i nowszych, ale trzeba to zrobić ręcznie wersji.
Od Androida 13 system wyświetla standardowe wizualne potwierdzenie dodania treści do schowka. Nowe potwierdzenie wygląda tak:
- Potwierdza, że treść została skopiowana.
- Udostępnia podgląd skopiowanej treści.
W Androidzie 12L (poziom interfejsu API 32) i starszych wersjach użytkownicy mogą nie mieć pewności, czy skopiowali dane ani ich skopiowanych treści. Ta funkcja ustandaryzuje różne powiadomienia wyświetlane przez aplikacje po i daje użytkownikom większą kontrolę nad schowkiem.
Unikaj podwójnych powiadomień
W Androidzie 12L (poziom interfejsu API 32) i starszych zalecamy powiadamianie użytkowników o udanym skopiowaniu
przesyłając opinię w aplikacji za pomocą widżetu takiego jak Toast
lub
Snackbar
po skopiowaniu.
Aby uniknąć powielania informacji, zdecydowanie zalecamy usunięcie powiadomień lub paski powiadomień wyświetlane po tekście w aplikacji na Androida 13 lub nowszego.
.Oto przykład:
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() }
Dodaj poufne treści do schowka
Jeśli Twoja aplikacja umożliwia użytkownikom kopiowanie do schowka treści poufnych, takich jak hasła czy środki,
dane karty, musisz dodać flagę do ClipDescription
w ClipData
zanim zadzwonisz do: ClipboardManager.setPrimaryClip()
. Dodanie tej flagi zapobiega wyświetlaniu poufnych danych
nie pojawiają się w wizualnym potwierdzeniu skopiowanych treści na Androidzie 13 i nowszych.
Aby oznaczyć treści poufne, dodaj do ClipDescription
element logiczny. Wszystkie aplikacje muszą wykonać
niezależnie od docelowego poziomu interfejsu API.
// 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) } }
Wklej ze schowka
Jak już wspomnieliśmy, wklej dane ze schowka, pobierając globalny obiekt schowka pobierz obiekt klipu, przeglądaj jego dane i, jeśli to możliwe, kopiując dane z obiektu klipu do pamięci masowej. W tej sekcji szczegółowo wyjaśniamy, jak wkleić 3 formy schowka i skalowalnych danych.
Wklej zwykły tekst
Aby wkleić zwykły tekst, pobierz schowek globalny i sprawdź, czy może on zwracać zwykły tekst. Potem pobierz
i skopiuj jego tekst do swojej pamięci masowej za pomocą getText()
, jak opisano w
następującą procedurę:
- Pobierz globalny obiekt
ClipboardManager
za pomocągetSystemService(CLIPBOARD_SERVICE)
Zadeklaruj też zmienną globalną zawierającą wklejony tekst:Kotlin
var clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager var pasteData: String = ""
Java
ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE); String pasteData = "";
- Określenie, czy należy włączyć, czy wyłączyć opcję „wklejania”. w bieżącej opcji
działania. Sprawdź, czy schowek zawiera klip i czy możesz obsłużyć ten typ danych
reprezentowane przez klip:
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); }
- Skopiuj dane ze schowka. Ten punkt kodu jest osiągalny tylko wtedy, gdy
„wklej” jest włączona, więc możesz przyjąć, że w schowku znajduje się
tekstu. Nie wiesz jeszcze, czy zawiera ciąg tekstowy lub identyfikator URI wskazujący zwykły tekst.
Możesz to sprawdzić w poniższym fragmencie kodu, ale pokazuje on tylko kod do obsługi zwykłego tekstu:
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; } }
Wklej dane z identyfikatora URI treści
Jeśli obiekt ClipData.Item
zawiera identyfikator URI treści i stwierdzisz, że możesz
obsługuje jeden z jego typów MIME, utwórz ContentResolver
i wywołaj odpowiednią treść
dostawcy, aby pobrać dane.
Poniżej opisano procedurę pobierania danych od dostawcy treści na podstawie identyfikatora URI treści ze schowka. Sprawdza, czy typ MIME, którego aplikacja może użyć, jest dostępny w dostawcy usług.
-
Zadeklaruj zmienną globalną zawierającą typ 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";
- Pobierz globalny schowek. Pobierz też program do rozpoznawania treści, aby mieć dostęp do treści
dostawca:
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();
- Pobierz klip główny ze schowka i pobierz jego zawartość jako identyfikator 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();
- Sprawdź, czy identyfikator URI jest identyfikatorem URI treści, wywołując
getType(Uri)
Ta metoda zwraca wartość null, jeśliUri
nie wskazuje prawidłowego dostawcy treści.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);
- Sprawdź, czy dostawca treści obsługuje typ MIME zrozumiały dla aplikacji. Jeśli
robi, wywołaj
ContentResolver.query()
aby pobrać dane. Zwracana wartość toCursor
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(); } } } }
Wklej intencję
Aby wkleić intencję, najpierw pobierz schowek globalny. Zbadaj obiekt ClipData.Item
aby sprawdzić, czy zawiera on Intent
. Następnie wywołaj getIntent()
, aby skopiować
na Twoją pamięć. Oto przykład:
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. }
Powiadomienie systemowe wyświetlane, gdy aplikacja uzyskuje dostęp do danych schowka
W Androidzie 12 (poziom interfejsu API 31) i nowszych system zwykle wyświetla komunikat ostrzegawczy, gdy aplikacja
połączeń
getPrimaryClip()
Treść wiadomości ma następujący format:
APP pasted from your clipboard
System nie wyświetla komunikatu, gdy aplikacja wykona jedną z tych czynności:
- Dostęp
ClipData
z Twojej aplikacji. - Powtarza dostęp do
ClipData
z poziomu określonej aplikacji. Tost pojawia się tylko wtedy, gdy aplikacja po raz pierwszy uzyskuje dostęp do jej danych. - Pobiera metadane obiektu klipu, na przykład przez wywołanie
getPrimaryClipDescription()
zamiastgetPrimaryClip()
.
Używanie dostawców treści do kopiowania złożonych danych
Dostawcy treści obsługują kopiowanie złożonych danych, takich jak rekordy bazy danych lub strumienie plików. Do skopiowania umieść w schowku identyfikator URI treści. Wklejanie aplikacji, a następnie pobieranie tego identyfikatora URI z i używać go do pobierania danych z bazy danych lub deskryptorów strumienia plików.
Ponieważ aplikacja do wklejania zawiera tylko identyfikator URI zawartości danych, musi wiedzieć, danych do pobrania. Możesz podać te informacje, kodując identyfikator danych lub podać unikalny identyfikator URI zwracający dane, które chcesz skopiować. Który Zależy to od sposobu organizacji danych.
W poniższych sekcjach znajdziesz informacje o tym, jak skonfigurować identyfikatory URI, podać złożone dane i dostarczyć plik strumienie. W opisach zakładamy, że znasz ogólne zasady dostawców treści projektu.
Kodowanie identyfikatora w identyfikatorze URI
Przydatną metodą kopiowania danych do schowka za pomocą identyfikatora URI jest zakodowanie identyfikatora danych bezpośrednio w identyfikatorze URI. Dostawca treści może następnie uzyskać identyfikator z URI i użyć go, aby pobrać dane. Aplikacja do wklejania nie musi wiedzieć, że identyfikator istnieje. it pobierze tylko „odwołanie” – identyfikator URI i identyfikator do schowka, przekazać je dostawcy treści i odzyskać dane.
Zwykle koduje się identyfikator w identyfikatorze URI treści, łącząc go z końcem identyfikatora URI. Załóżmy na przykład, że zdefiniujesz identyfikator URI dostawcy za pomocą następującego ciągu:
"content://com.example.contacts"
Jeśli chcesz zakodować nazwę w tym identyfikatorze URI, użyj tego fragmentu kodu:
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);
Jeśli korzystasz już z usług dostawcy treści, możesz dodać nową ścieżkę URI wskazującą, identyfikator URI jest przeznaczony do kopiowania. Załóżmy na przykład, że masz już następujące ścieżki identyfikatora URI:
"content://com.example.contacts/people" "content://com.example.contacts/people/detail" "content://com.example.contacts/people/images"
Możesz dodać inną ścieżkę do kopiowania identyfikatorów URI:
"content://com.example.contacts/copying"
Dzięki temu można wykryć, że jest to „kopia”, używając identyfikatora URI dopasowującego do wzorca i obsługiwać go za pomocą kodu, który przeznaczone do kopiowania i wklejania.
Zazwyczaj techniki kodowania używasz, jeśli korzystasz już z usług dostawcy treści, wewnętrznym w bazie danych lub tabeli wewnętrznej do porządkowania danych. W takich przypadkach masz wiele danych który chcesz skopiować, i zapewne unikalny identyfikator każdego elementu. W odpowiedzi na zapytanie ze strony i wklejając aplikację, możesz wyszukać dane według identyfikatora i zwrócić je.
Jeśli nie masz wielu danych, raczej nie musisz kodować identyfikatora. Możesz użyć identyfikatora URI, który jest unikalny dla Twojego dostawcy. W odpowiedzi na zapytanie dostawca zwraca błąd które obecnie zawiera.
Kopiowanie struktur danych
skonfigurować dostawcę treści na potrzeby kopiowania i wklejania złożonych danych jako podklasy klasy
ContentProvider
. Zakoduj identyfikator URI, który umieścisz w schowku, tak aby wskazywał dokładnie rekord, który chcesz
i udostępniania. Ponadto weź pod uwagę obecny stan aplikacji:
- Jeśli masz już dostawcę treści, możesz dodać do jego funkcji nowe funkcje. Możesz mieć tylko
musi zmodyfikować swoją metodę
query()
, aby obsługiwała identyfikatory URI pochodzące z aplikacji, i chcesz wkleić dane. Zmodyfikowanie metody obsługi „kopii” Identyfikator URI wzorcem. - Jeśli Twoja aplikacja utrzymuje wewnętrzną bazę danych, można ją przenieść do dostawcy treści w celu ułatwienia ich kopiowania.
- Jeśli nie korzystasz z bazy danych, możesz zaimplementować prostego dostawcę treści, jedynym celem jest oferowanie danych aplikacjom wklejającym dane ze schowka.
W ustawieniach dostawcy treści zastąp przynajmniej te metody:
-
query()
- Podczas wklejania aplikacji zakłada się, że mogą one pobrać Twoje dane za pomocą tej metody z identyfikatorem URI umieść w schowku. Aby umożliwić kopiowanie, ta metoda powinna wykrywać identyfikatory URI zawierające specjalne „kopia” ścieżki konwersji. Aplikacja może wtedy utworzyć „kopię” Identyfikator URI do umieszczenia w schowku zawierającym ścieżkę kopiowania i wskaźnik do dokładnego rekordu, który chcesz skopiować.
-
getType()
- Ta metoda musi zwracać typy MIME danych, które chcesz skopiować. Metoda
newUri()
wywołujegetType()
, aby umieścić typy MIME w nowymClipData
obiektu.Typy MIME w przypadku złożonych danych opisano w Dostawcy treści.
Nie musisz korzystać z żadnej innej metody dostawcy treści, takiej jak
insert()
lub
update()
Aplikacja do wklejania musi tylko pobrać obsługiwane typy MIME i kopiować dane od dostawcy.
Jeśli korzystasz już z tych metod, nie będą zakłócać operacji kopiowania.
Te fragmenty kodu pokazują, jak skonfigurować aplikację do kopiowania złożonych danych:
-
W stałych globalnych w aplikacji zadeklaruj podstawowy ciąg identyfikatora URI i ścieżkę, która wskazuje ciągi URI używane do kopiowania danych. Zadeklaruj też typ MIME dla kopiowanego pliku i skalowalnych danych.
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";
- W aktywności, z której użytkownicy kopiują dane, skonfiguruj kod w celu kopiowania danych do schowka.
W odpowiedzi na żądanie kopiowania umieść identyfikator URI w schowku.
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);
-
W globalnym zakresie dostawcy treści utwórz dopasowania URI i dodaj wzorzec identyfikatora URI, który pasuje do identyfikatorów URI umieszczonych w schowku.
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);
-
Skonfiguruj
query()
. Ta metoda może obsłużyć różne wzorce identyfikatorów URI w zależności od sposobu kodowania, ale tylko pokazuje wzorzec kopiowania do schowka.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. ... }
-
Skonfiguruj metodę
getType()
tak, aby zwracała odpowiedni typ MIME w przypadku skopiowania dane: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); ... } }
W sekcji Wklejanie danych z identyfikatora URI treści opisujemy, jak pobrać identyfikatora URI zawartości ze schowka i używać go do pobierania oraz wklejania danych.
Kopiowanie strumieni danych
Możesz kopiować i wklejać duże ilości danych tekstowych i binarnych jako strumienie. Dane mogą mieć formularze na przykład:
- Pliki przechowywane na rzeczywistym urządzeniu
- Strumienie z gniazd
- Duże ilości danych przechowywanych w bazowym systemie baz danych dostawcy
Dostawca treści dla strumieni danych zapewnia dostęp do danych za pomocą obiektu deskryptora pliku,
na przykład
AssetFileDescriptor
,
zamiast obiektu Cursor
. Aplikacja do wklejania odczytuje strumień danych za pomocą
deskryptor pliku.
Aby skonfigurować aplikację do kopiowania strumienia danych z dostawcą, wykonaj te czynności:
-
Skonfiguruj identyfikator URI treści dla strumienia danych, który umieszczasz w schowku. Opcje
aby to zrobić, m.in.:
- Zakoduj identyfikator strumienia danych w identyfikatorze URI zgodnie z opisem w sekcji Zakoduj identyfikator w sekcji identyfikatora URI, a następnie umieść w sekcji u dostawcy, która zawiera identyfikatory i odpowiadającą im nazwę strumienia.
- Zakoduj nazwę strumienia bezpośrednio w identyfikatorze URI.
- Użyj unikalnego identyfikatora URI, który zawsze zwraca bieżący strumień od dostawcy. Jeśli użyj tej opcji, pamiętaj, żeby zaktualizować dostawcę, tak aby wskazywał inny strumień za każdym razem, gdy skopiujesz strumień do schowka przy użyciu identyfikatora URI.
- Podaj typ MIME dla każdego typu strumienia danych, który planujesz oferować. Wklejam aplikacje potrzebują tych informacji, aby określić, czy mogą wkleić dane do schowka.
- Zaimplementuj jedną z metod
ContentProvider
, która zwraca deskryptor pliku dla w postaci strumienia. Jeśli kodujesz identyfikatory w identyfikatorze URI treści, użyj tej metody, aby określić, które aby otworzyć strumień. - Aby skopiować strumień danych do schowka, utwórz identyfikator URI treści i umieść go w schowku.
Aby wkleić strumień danych, aplikacja pobiera wycinek ze schowka, pobiera identyfikator URI, a następnie używa
w wywołaniu metody deskryptora pliku ContentResolver
, która otwiera strumień.
metoda ContentResolver
wywołuje odpowiednią metodę ContentProvider
,
i przekazywanie jej identyfikatora URI treści. Dostawca zwraca deskryptor pliku do funkcji
Metoda ContentResolver
. Aplikacja do wklejania ma wtedy obowiązek odczytać
ze strumienia.
Na liście poniżej znajdziesz najważniejsze metody deskryptorów plików dla dostawcy treści. Każdy
ma odpowiednią metodę ContentResolver
z ciągiem znaków
„Deskryptor” do nazwy metody. Na przykład ContentResolver
analog z
openAssetFile()
to
openAssetFileDescriptor()
-
openTypedAssetFile()
-
Ta metoda zwraca deskryptor pliku zasobu, ale tylko wtedy, gdy podany typ MIME to obsługiwanych przez dostawcę. Element wywołujący (aplikacja wykonująca wklejanie) udostępnia dla wzorca typu MIME. Dostawca treści aplikacji, który kopiuje identyfikator URI do schowku zwraca uchwyt pliku
AssetFileDescriptor
, jeśli może go podać Typ MIME, a jeśli to niemożliwe, powoduje zgłoszenie wyjątku.Ta metoda obsługuje podsekcje plików. Możesz go używać do odczytywania zasobów, które dostawca treści skopiował się do schowka.
-
openAssetFile()
-
Ta metoda jest bardziej ogólna forma metody
openTypedAssetFile()
. Brak filtrowania , ale może odczytywać podsekcje plików. -
openFile()
-
To bardziej ogólna forma słowa
openAssetFile()
. Nie może odczytać podsekcji .
Opcjonalnie możesz użyć parametru
openPipeHelper()
z deskryptorem pliku. Dzięki temu aplikacja do wklejania odczytuje dane strumienia w
za pomocą rurki. Aby użyć tej metody, zaimplementuj
ContentProvider.PipeDataWriter
za pomocą prostego interfejsu online.
Zaprojektuj skuteczną funkcję kopiowania i wklejania
Aby zaprojektować skuteczną funkcję kopiowania i wklejania w swojej aplikacji, pamiętaj o następujących kwestiach:
- W każdej chwili w schowku znajduje się tylko 1 klip. Nowa operacja kopiowania przez zastąpi poprzedni klip. Użytkownik może nawigować odwrotnej aplikacji i kopiowania przed zwróceniem, nie można przyjąć, zawiera klip, który użytkownik wcześniej skopiował do aplikacji Twoja.
-
Zastosowanie wielu obiektów
ClipData.Item
na klip jest przeznaczone nie obsługują kopiowania i wklejania wielu wybranych elementów, a nie różnych form lub odwołania do pojedynczego zaznaczenia. Zwykle chcesz, aby wszystkie te usługi (ClipData.Item
) obiekty w klipie, aby miały taki sam format. Oznacza to, że muszą składać się z prostego tekstu, treści Identyfikator URI lubIntent
, ale nie miesza się. -
Podczas dostarczania danych możesz oferować różne reprezentacje MIME. Dodaj typy MIME
obsługiwane w
ClipDescription
, a następnie zaimplementuj typy MIME dostawcy treści. -
Gdy pobierasz dane ze schowka, aplikacja odpowiada za sprawdzenie
dostępnych typów MIME i wybierając te, których chcesz użyć. Nawet wtedy, gdy
i klip w schowku, a użytkownik prosi o wklejenie, aplikacja nie jest wymagana
aby wkleić. Wklej ją, jeśli typ MIME jest zgodny. Możesz wymusić na danych
ze schowka na tekst za pomocą funkcji
coerceToText()
. Jeśli aplikacja obsługuje więcej niż jednego z dostępnych typów MIME, możesz pozwolić użytkownikowi wybrać, który ma zostać użyty.