O Android fornece um poderoso framework baseado em área de transferência para os procedimentos de copiar e colar. Oferece suporte a e tipos de dados complexos, incluindo strings de texto, estruturas de dados complexas, texto e fluxo binário dados e ativos de aplicativos. Dados de texto simples são armazenados diretamente na área de transferência, enquanto dados complexos os dados são armazenados como uma referência que o aplicativo de colagem resolve com um provedor de conteúdo. Copiando e a ação de colar funciona dentro de um aplicativo e entre aplicativos que implementam a de análise de dados em nuvem.
Como parte da estrutura usa provedores de conteúdo, este documento pressupõe certa familiaridade com a API do provedor de conteúdo do Android, descrita em Provedores de conteúdo.
Os usuários esperam feedback ao copiar conteúdo para a área de transferência, então, além do framework que permite copiar e colar, o Android mostra uma interface padrão para os usuários ao copiar no Android 13 (nível 33 da API) e superiores. Devido a esse recurso, há o risco de notificação duplicada. Saiba mais sobre neste caso extremo, na Evitar notificações duplicadas nesta seção.
![Uma animação mostrando a notificação da área de transferência do Android 13](https://developer.android.google.cn/static/images/about/versions/13/new-copy-paste-UI.gif?authuser=19&hl=pt)
Envie feedback manualmente aos usuários ao copiar conteúdo no Android 12L (nível 32 da API) e versões anteriores. Consulte recomendações recomendadas neste documento.
O framework da área de transferência
Ao usar o framework da área de transferência, colocar os dados em um objeto de transferência e depois esse objeto na área de transferência do sistema. O objeto de transferência pode assumir uma destas três formas:
- Texto
- Uma string de texto. Coloque a string diretamente no objeto de clipe, que você coloca no área de transferência. Para colar a string, pegue o objeto de transferência na área de transferência e copie o no armazenamento do aplicativo.
- URI
-
Um objeto
Uri
que representa forma de URI. Isso serve principalmente para copiar dados complexos de um provedor de conteúdo. Para copiar dados, coloque um objetoUri
em um objeto de transferência e coloque esse objeto no área de transferência. Para colar os dados, pegue o objeto de transferência, o objetoUri
, resolvê-los para uma fonte de dados, como um provedor de conteúdo, e copiar os dados da no armazenamento do seu aplicativo. - Intent
-
uma
Intent
. Isso oferece suporte à cópia de atalhos de aplicativos. Para copiar dados, crie umIntent
, coloque em um objeto de transferência e coloca na área de transferência. Para colar os dados, acesse o objeto de transferência e copie o objetoIntent
no na área da memória.
A área de transferência mantém apenas um objeto de transferência por vez. Quando um aplicativo coloca um objeto de transferência no área de transferência, o objeto de transferência anterior desaparecerá.
Se você quiser permitir que os usuários colem dados em seu aplicativo, não precisa lidar com todos os tipos de dados. Você pode examinar os dados na área de transferência antes de oferecer aos usuários a opção de colar. Além de ter um certo formulário de dados, o objeto de transferência também contém metadados que informam qual imagem MIME tipos disponíveis. Esses metadados ajudam você a decidir se seu aplicativo pode fazer algo útil com os dados da área de transferência. Por exemplo, se você tiver um aplicativo que lida principalmente com texto, pode ignorar objetos de transferência que contenham um URI ou uma intent.
Você também pode permitir que os usuários colem o texto, independentemente da forma de dados na área de transferência. Para faça isso, force os dados da área de transferência em uma representação de texto e, em seguida, cole esse texto. Isso é descrito na seção Forçar a área de transferência para texto.
Aulas de área de transferência
Esta seção descreve as classes usadas pelo framework da área de transferência.
ClipboardManager
A área de transferência do sistema Android é representada pela imagem
ClipboardManager
.
Não instancie essa classe diretamente. Em vez disso, obtenha uma referência a ele invocando
getSystemService(CLIPBOARD_SERVICE)
:
ClipData, ClipData.Item e ClipDescription
Para adicionar dados à área de transferência, crie um
Objeto ClipData
que contém
uma descrição dos dados e dos dados em si. A área de transferência contém um(a) ClipData
em
tempo de resposta. Um ClipData
contém um
Objeto ClipDescription
e um ou mais
objetos ClipData.Item
.
Um objeto ClipDescription
contém metadados sobre a transferência. Especificamente, ela
contém uma matriz de tipos MIME disponíveis para os dados da transferência. Além disso, em
No Android 12 (nível 31 da API) e versões mais recentes, os metadados incluem informações sobre se o objeto
contém
texto estilizado e sobre a
tipo de texto no objeto.
Quando você coloca um clipe na área de transferência, essa informação fica disponível para os aplicativos de colagem, o que
pode examinar se pode lidar com os dados de clipes.
Um objeto ClipData.Item
contém dados de texto, URI ou intent:
- Texto
-
Um
CharSequence
. - URI
-
Um
Uri
. Ele geralmente contém um URI do provedor de conteúdo, embora qualquer URI seja permitido. O aplicativo que fornece os dados coloca o URI na área de transferência. Aplicativos que desejam colar os dados recebem o URI da área de transferência e usá-lo para acessar o conteúdo ou outra fonte de dados e recuperar os dados. - Intent
-
Uma
Intent
. Este tipo de dados permite que você copie um atalho de aplicativo para o área de transferência. Então, os usuários podem colar o atalho nos aplicativos deles para uso posterior.
Você pode adicionar mais de um objeto ClipData.Item
a uma transferência. Isso permite que os usuários copiem e
e colar várias seleções como um único clipe. Por exemplo, se você tiver um widget de lista que permite
usuário seleciona mais de um item ao mesmo tempo, é possível copiar todos os itens para a área de transferência de uma só vez. Afazeres
isso, crie um ClipData.Item
separado para cada item da lista e adicione o
objetos ClipData.Item
ao objeto ClipData
.
Métodos de conveniência ClipData
A classe ClipData
fornece métodos estáticos de conveniência para criar um
objeto ClipData
com um único objeto ClipData.Item
e um objeto
Objeto ClipDescription
:
-
newPlainText(label, text)
- Retorna um objeto
ClipData
cujo único objetoClipData.Item
. contém uma string de texto. O rótulo do objetoClipDescription
é definido comolabel
. O tipo MIME único emClipDescription
éMIMETYPE_TEXT_PLAIN
.Use
newPlainText()
para criar um clipe a partir de uma string de texto. -
newUri(resolver, label, URI)
- Retorna um objeto
ClipData
cujo único objetoClipData.Item
. contém um URI. O rótulo do objetoClipDescription
é definido comolabel
. Se o URI for de conteúdo, ou seja, seUri.getScheme()
retornacontent:
, e o método usa oContentResolver
fornecido emresolver
para recuperar os tipos MIME disponíveis do provedor de conteúdo. Em seguida, ele as armazena emClipDescription
. Para um URI que não é um URIcontent:
, o método vai definir o tipo MIME comoMIMETYPE_TEXT_URILIST
.Use
newUri()
para criar uma transferência com base em um URI, principalmente em uma URIcontent:
. -
newIntent(label, intent)
- Retorna um objeto
ClipData
cujo único objetoClipData.Item
. contém umIntent
. O rótulo do objetoClipDescription
é definido comolabel
. O tipo MIME é definido comoMIMETYPE_TEXT_INTENT
:Use
newIntent()
para criar uma transferência a partir de um objetoIntent
.
Transformar os dados da área de transferência em texto
Mesmo que seu aplicativo só lide com texto, você pode copiar dados não textuais da área de transferência
convertendo-o com o
ClipData.Item.coerceToText()
.
Esse método converte os dados em ClipData.Item
em texto e retorna uma
CharSequence
. O valor retornado por ClipData.Item.coerceToText()
é baseado
na forma de dados em ClipData.Item
:
- Texto
-
Se
ClipData.Item
for texto, ou seja, segetText()
não é nulo – coerceToText() retorna o texto. - URI
-
Se
ClipData.Item
for um URI, ou seja, segetUri()
não é nulo:coerceToText()
tenta usá-lo como um URI de conteúdo.- Se o URI for de conteúdo e o provedor puder retornar um fluxo de texto,
coerceToText()
retorna um fluxo de texto. - Se o URI for de conteúdo, mas o provedor não oferecer um fluxo de texto,
coerceToText()
retorna uma representação do URI. A representação é igual ao retornadoUri.toString()
: - Se o URI não for de conteúdo,
coerceToText()
retornará uma representação do URI. A representação é a mesma retornada peloUri.toString()
:
- Se o URI for de conteúdo e o provedor puder retornar um fluxo de texto,
- Intent
- Se
ClipData.Item
for umaIntent
, ou seja, segetIntent()
não é nulo:coerceToText()
o converte em um URI de intent e o retorna. A representação é a mesma retornada peloIntent.toUri(URI_INTENT_SCHEME)
:
O framework da área de transferência está resumido na Figura 2. Para copiar dados, um aplicativo coloca uma
Objeto ClipData
na área de transferência global ClipboardManager
. A
ClipData
contém um ou mais objetos ClipData.Item
e um
objeto ClipDescription
. Para colar dados, um aplicativo recebe a ClipData
,
recebe o tipo MIME do objeto ClipDescription
e recebe os dados da
ClipData.Item
ou do provedor de conteúdo referido pelo
ClipData.Item
![Uma imagem mostrando um diagrama de blocos da estrutura de copiar e colar](https://developer.android.google.cn/static/images/ui/clipboard/copy_paste_framework.png?authuser=19&hl=pt)
Copiar para a área de transferência
Para copiar dados para a área de transferência, use um handle para o objeto ClipboardManager
global.
criar um objeto ClipData
e adicionar um ClipDescription
e um ou mais
ClipData.Item
a ele. Em seguida, adicione o objeto ClipData
finalizado ao
objeto ClipboardManager
. Isso é descrito no procedimento a seguir:
- Se você estiver copiando dados usando um URI de conteúdo, configure um provedor de conteúdo.
- Faça o download da área de transferência do 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);
-
Copie os dados para um novo objeto
ClipData
:-
Para texto
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!");
-
Para um URI
Este snippet constrói um URI codificando um ID de registro no URI de conteúdo para o provedor. Essa técnica é abordada com mais detalhes no Codificar um identificador na seção 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);
-
Para uma intent
Esse snippet constrói uma
Intent
para um aplicativo e coloca no objeto de transferência: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);
-
Para texto
-
Coloque o novo objeto de transferência na área de transferência:
Kotlin
// Set the clipboard's primary clip. clipboard.setPrimaryClip(clip)
Java
// Set the clipboard's primary clip. clipboard.setPrimaryClip(clip);
Enviar feedback durante a realização de cópias para a área de transferência
Os usuários esperam feedback visual quando usam um app para copiar conteúdo para a área de transferência. Pronto automaticamente para usuários no Android 13 e versões mais recentes, mas precisa ser implementado mais recentes.
No Android 13 e versões mais recentes, o sistema mostra uma confirmação visual padrão quando um conteúdo é adicionado. para a área de transferência. A nova confirmação faz o seguinte:
- Confirma que o conteúdo foi copiado.
- Oferece uma visualização do conteúdo copiado.
![Uma animação mostrando a notificação da área de transferência do Android 13](https://developer.android.google.cn/static/images/about/versions/13/new-copy-paste-UI.gif?authuser=19&hl=pt)
No Android 12L (nível 32 da API) e versões anteriores, os usuários podem não saber se foram copiados ou o que eles copiaram. Esse recurso padroniza as várias notificações mostradas pelos apps após copiar e oferecer aos usuários mais controle sobre a área de transferência.
Evitar notificações duplicadas
No Android 12L (nível 32 da API) e versões anteriores, recomendamos alertar os usuários quando eles copiarem
emitindo feedback visual no app, usando um widget como Toast
ou
um Snackbar
, depois de ser copiado.
Para evitar exibições duplicadas de informações, recomendamos remover avisos ou snackbars mostradas após uma cópia no app para Android 13 e versões mais recentes.
![Exibir snackbar depois que o usuário cópia conteúdo no app.](https://developer.android.google.cn/static/images/about/versions/13/snackbar-overlap.png?authuser=19&hl=pt)
![Exibir aviso depois que o usuário copia conteúdo no app.](https://developer.android.google.cn/static/images/about/versions/13/toast-overlap.png?authuser=19&hl=pt)
Veja como fazer isso:
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() }
Adicionar conteúdo sensível à área de transferência
Se o app permite que os usuários copiem conteúdo sensível para a área de transferência, como senhas ou créditos
informações do cartão, adicione uma sinalização ao ClipDescription
em ClipData
antes de chamar ClipboardManager.setPrimaryClip()
. Adicionar essa flag evita dados sensíveis
no Android 13 e versões mais recentes do conteúdo copiado.
![Visualização do texto copiado sem sinalização de conteúdo sensível.](https://developer.android.google.cn/static/images/about/versions/13/sensitive-content-before.png?authuser=19&hl=pt)
![Visualização do texto copiado com sinalização de conteúdo sensível.](https://developer.android.google.cn/static/images/about/versions/13/sensitive-content-after.png?authuser=19&hl=pt)
Para sinalizar a presença de conteúdo sensível, adicione um booleano extra à ClipDescription
. Todos os apps precisam fazer
independentemente do nível da API segmentado.
// 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) } }
Colar da área de transferência
Como descrito anteriormente, cole os dados da área de transferência obtendo o objeto da área de transferência global, acessar o objeto de transferência, analisar os dados dele e, se possível, copiar os dados dele. para seu próprio armazenamento. Esta seção explica em detalhes como colar as três formas de área de transferência dados.
Colar texto simples
Para colar texto simples, pegue a área de transferência global e verifique se ela pode retornar texto simples. Em seguida, receba
o objeto de transferência e copiar o texto dele para seu armazenamento usando getText()
, conforme descrito em
o seguinte procedimento:
- Acesse o objeto
ClipboardManager
global usandogetSystemService(CLIPBOARD_SERVICE)
. Além disso, declare uma variável global para conter o texto colado:Kotlin
var clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager var pasteData: String = ""
Java
ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE); String pasteData = "";
- Determine se é preciso ativar ou desativar a opção "colar" na configuração atual
atividades. Verificar se a área de transferência contém um clipe e se você consegue processar o tipo dos dados
representada pelo clipe:
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); }
- Copie os dados da área de transferência. Esse ponto do código só é acessível se o
"colar" está ativado, então você pode presumir que a área de transferência contém texto
em textos. Você ainda não sabe se ele contém uma string de texto ou um URI que aponta para texto simples.
O snippet de código a seguir testa isso, mas só mostra o código para lidar com texto simples:
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; } }
Colar dados de um URI de conteúdo
Se o objeto ClipData.Item
tiver um URI de conteúdo e você determinar que pode
processar um dos tipos MIME dele, criar uma ContentResolver
e chamar o conteúdo
método de provedor para recuperar os dados.
O procedimento a seguir descreve como obter dados de um provedor de conteúdo com base em um URI de conteúdo. na área de transferência. Ele verifica se um tipo MIME que o aplicativo pode usar está disponível no de nuvem.
-
Declare uma variável global para conter o 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";
- Pegue a área de transferência global. Receba também um resolvedor de conteúdo para acessar o provedor
de conteúdo:
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();
- Consiga o clipe principal da área de transferência e receba o conteúdo dele como um 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();
- Teste se o URI é de conteúdo chamando
getType(Uri)
: Esse método retornará nulo seUri
não apontar para um provedor de conteúdo válido.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);
- Teste se o provedor de conteúdo oferece suporte a um tipo MIME que o aplicativo entenda. Se
tem, chame
ContentResolver.query()
para conseguir os dados. O valor de retorno é umCursor
: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(); } } } }
Colar uma intent
Para colar uma intent, primeiro acesse a área de transferência global. Analise o objeto ClipData.Item
para ver se ele contém um Intent
. Em seguida, chame getIntent()
para copiar
para seu próprio armazenamento. O snippet a seguir demonstra isso:
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ção do sistema exibida quando o app acessa dados da área de transferência
No Android 12 (nível 31 da API) e versões mais recentes, o sistema geralmente mostra uma mensagem de aviso quando o app
ligações
getPrimaryClip()
O texto da mensagem contém o formato a seguir:
APP pasted from your clipboard
O sistema não mostra uma mensagem de aviso quando o app realiza uma das seguintes ações:
- Acessos
ClipData
no seu app. - Acessa repetidamente
ClipData
de um app específico. O aviso só aparece quando seu app acessar os dados dele pela primeira vez. - recupera metadados para o objeto de transferência, como ao chamar
getPrimaryClipDescription()
em vez degetPrimaryClip()
.
Usar provedores de conteúdo para copiar dados complexos
Os provedores de conteúdo são compatíveis com a cópia de dados complexos, como registros de banco de dados ou fluxos de arquivos. Para copiar colocar um URI de conteúdo na área de transferência. Os aplicativos de colagem recebem esse URI do área de transferência e usá-la para recuperar dados do banco de dados ou descritores do fluxo de arquivos.
Como o aplicativo de colagem só tem o URI de conteúdo para seus dados, ele precisa saber quais dados a serem recuperados. Você pode fornecer essas informações codificando um identificador para os dados no próprio URI. Também é possível fornecer um URI exclusivo que retorne os dados que você quer copiar. Que técnica que você escolhe depende da organização dos seus dados.
As seções a seguir descrevem como configurar URIs, fornecer dados complexos e fornecer córregos. As descrições presumem que você conhece os princípios gerais do provedor de conteúdo do projeto.
Codificar um identificador no URI
Uma técnica útil para copiar dados para a área de transferência com um URI é codificar um identificador para os dados no próprio URI. Seu provedor de conteúdo pode então receber o identificador do URI e usar para recuperar os dados. O aplicativo de colagem não precisa saber que o identificador existe. Ela basta obter sua "referência", ou seja, o URI mais o identificador, do da área de transferência, dê ao provedor de conteúdo e recupere os dados.
Normalmente, um identificador é codificado em um URI de conteúdo ao concatená-lo no fim do URI. Por exemplo, suponha que você definiu seu URI de provedor como a string a seguir:
"content://com.example.contacts"
Se você quiser codificar um nome para esse URI, use o seguinte snippet de código:
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 você já estiver usando um provedor de conteúdo, convém adicionar um novo caminho de URI que indique o URI é para cópia. Por exemplo, suponha que você já tenha os seguintes caminhos de URI:
"content://com.example.contacts/people" "content://com.example.contacts/people/detail" "content://com.example.contacts/people/images"
Você pode adicionar outro caminho para copiar URIs:
"content://com.example.contacts/copying"
É possível detectar uma "cópia" o URI por correspondência de padrões e processá-lo com um código específico para copiar e colar.
Normalmente, você usa a técnica de codificação se já estiver usando um provedor de conteúdo, ou tabela interna para organizar seus dados. Nesses casos, você tem vários dados que você quer copiar e provavelmente um identificador único para cada parte. Em resposta a uma consulta do colando o aplicativo, você pode procurar os dados pelo identificador e retorná-los.
Se você não tiver vários dados, provavelmente não precisará codificar um identificador. É possível usar um URI exclusivo para seu provedor. Em resposta a uma consulta, o provedor retorna o dados que ele contém atualmente.
Copiar estruturas de dados
Configure um provedor de conteúdo para copiar e colar dados complexos como uma subclasse do
ContentProvider
componente. Codifique o URI colocado na área de transferência para que ele aponte para o registro exato que você quer
que oferecem. Além disso, considere o estado atual do seu aplicativo:
- Se você já tiver um provedor de conteúdo, poderá melhorar a funcionalidade dele. Talvez você só
precisa modificar o método
query()
para lidar com URIs provenientes de aplicativos que para colar os dados. É provável que você queira modificar o método para processar uma "cópia" URI padrão - Caso seu aplicativo mantenha um banco de dados interno, você pode mover esse banco de dados em um provedor de conteúdo para facilitar a cópia a partir dele.
- Se não estiver usando um banco de dados, você pode implementar um provedor de conteúdo simples cujo único objetivo é oferecer dados para aplicativos que estejam colando da área de transferência.
No provedor de conteúdo, modifique pelo menos os seguintes métodos:
-
query()
- Os aplicativos de colagem presumem que podem acessar seus dados usando esse método com o URI que você colocar na área de transferência. Para dar suporte à cópia, faça com que esse método detecte URIs que contenham um "copiar" caminho. Assim, seu aplicativo pode criar uma "cópia" URI para inserir no área de transferência, contendo o caminho da cópia e um ponteiro para o registro exato que você quer copiar.
-
getType()
- Esse método precisa retornar os tipos MIME dos dados que você pretende copiar. Método
newUri()
chamagetType()
para colocar os tipos MIME no novoClipData
objeto.Os tipos MIME para dados complexos são descritos em Provedores de conteúdo.
Você não precisa ter nenhum dos outros métodos de provedor de conteúdo, como
insert()
ou
update()
.
Um aplicativo de colagem só precisa conseguir os tipos MIME compatíveis e copiar os dados do seu provedor.
Se você já tem esses métodos, eles não interferem nas operações de copiar.
Os snippets a seguir demonstram como configurar seu aplicativo para copiar dados complexos:
-
Nas constantes globais do seu aplicativo, declare uma string de URI de base e um caminho que identifica as strings de URI usadas para copiar dados. Declare também um tipo MIME para a instância dados.
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";
- Na atividade da qual os usuários copiam dados, configure o código para copiar dados para a área de transferência.
Coloque o URI na área de transferência em resposta a uma solicitação de cópia.
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);
-
No escopo global do seu provedor de conteúdo, crie uma correspondência de URI e adicione um padrão de URI que corresponde aos URIs colocados na área de transferência.
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);
-
Configure o
query()
. Esse método pode lidar com diferentes padrões de URI, dependendo de como você codificá-lo, mas apenas o padrão da operação de cópia da área de transferência é mostrado.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. ... }
-
Configure o método
getType()
para retornar um tipo MIME adequado para dados: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); ... } }
A seção Colar dados de uma URI de conteúdo descreve como conseguir uma URI de conteúdo da área de transferência e usá-la para recuperar e colar dados.
Copiar fluxos de dados
Você pode copiar e colar grandes quantidades de texto e dados binários como fluxos. Os dados podem ter formulários como o seguinte:
- Arquivos armazenados no próprio dispositivo
- Streams de soquetes
- Grandes quantidades de dados armazenados no sistema de banco de dados de um provedor
Um provedor de conteúdo para fluxos de dados fornece acesso aos dados com um objeto descritor de arquivo,
como
AssetFileDescriptor
,
em vez de um objeto Cursor
. O aplicativo de colagem lê o fluxo de dados usando este
descritor do arquivo.
Para configurar seu aplicativo para copiar um fluxo de dados com um provedor, siga estas etapas:
-
Configure um URI de conteúdo para o fluxo de dados que será colocado na área de transferência. Veja
algumas opções de como fazer isso a seguir:
- Codifique um identificador para o fluxo de dados no URI, conforme descrito nas Codifique um identificador na seção URI e mantenha um no provedor que contém identificadores e o nome do stream correspondente.
- Codifique o nome do fluxo diretamente no URI.
- Use um URI exclusivo que sempre retorne o fluxo atual do provedor. Se você use esta opção, lembre-se de atualizar seu provedor para apontar para um fluxo diferente sempre que você copiar o fluxo para a área de transferência usando o URI.
- Forneça um tipo MIME para cada tipo de fluxo de dados que você pretende oferecer. Colar aplicativos precisam dessas informações para determinar se podem colar os dados na área de transferência.
- Implemente um dos métodos
ContentProvider
que retorna um descritor de arquivo para por um stream. Se você codificar identificadores no URI de conteúdo, use esse método para determinar quais abrir. - Para copiar o fluxo de dados para a área de transferência, construa o URI de conteúdo e coloque-o na área de transferência.
Para colar um fluxo de dados, um aplicativo pega o clipe da área de transferência, consegue o URI e usa
em uma chamada para um método descritor de arquivo ContentResolver
que abre o fluxo. A
O método ContentResolver
chama o método ContentProvider
correspondente,
passando o URI de conteúdo. Seu provedor retorna o descritor de arquivo para a
ContentResolver
. O aplicativo de colagem fica, então, responsável por ler o
os dados do stream.
A lista a seguir mostra os métodos mais importantes do descritor de arquivo de um provedor de conteúdo. Cada
tem um método ContentResolver
correspondente à string
"Descritor" anexado ao nome do método. Por exemplo, o ContentResolver
análogo de
openAssetFile()
é
openAssetFileDescriptor()
-
openTypedAssetFile()
-
Esse método retorna um descritor do arquivo de recurso, mas somente se o tipo MIME fornecido for suportado pelo provedor. O autor da chamada (o aplicativo que faz a colagem) fornece um padrão de tipo MIME. O provedor de conteúdo do aplicativo que copia um URI para o A área de transferência retorna um identificador de arquivo
AssetFileDescriptor
se puder fornecer isso MIME e gera uma exceção caso isso não seja possível.Esse método lida com subseções de arquivos. Ele pode ser usado para ler recursos que o provedor de conteúdo copiou para a área de transferência.
-
openAssetFile()
-
Esse método é uma forma mais geral de
openTypedAssetFile()
. Ele não filtra para tipos MIME permitidos, mas pode ler subseções de arquivos. -
openFile()
-
Essa é uma forma mais geral de
openAssetFile()
. Ele não pode ler subseções .
Você tem a opção de usar
openPipeHelper()
com o método do descritor do arquivo. Isso permite que o aplicativo de colagem leia os dados de stream em um
em segundo plano usando um pipe. Para usar esse método, implemente a
ContentProvider.PipeDataWriter
interface gráfica do usuário.
Criar uma funcionalidade eficaz de copiar e colar
Para projetar uma funcionalidade eficaz de copiar e colar para seu aplicativo, lembre-se destes pontos:
- A qualquer momento, há apenas uma transferência na área de transferência. Uma nova operação de cópia feita por qualquer aplicativo no sistema substitui o clipe anterior. Como o usuário pode navegar do aplicativo e copiar antes de retornar, não é possível presumir que a área de transferência contém o clipe que o usuário copiou anteriormente em seu aplicativo.
-
O objetivo de ter vários objetos
ClipData.Item
por clipe é oferecem suporte a copiar e colar várias seleções, em vez de diferentes formas de referência a uma única seleção. Normalmente, você quer que todos osClipData.Item
objetos em um clipe tenham a mesma forma. Ou seja, todos eles devem ser textos simples, conteúdo URI ouIntent
, e não mistos. -
Ao fornecer dados, você pode oferecer diferentes representações MIME. Adicionar os tipos MIME
você oferece suporte ao
ClipDescription
e, em seguida, implemente os tipos MIME seu provedor de conteúdo. -
Quando você recebe dados da área de transferência, seu aplicativo é responsável por verificar os
tipos MIME disponíveis e decidir qual deles usar. Mesmo se houver
um clipe na área de transferência e o usuário solicita uma colagem, seu aplicativo não é necessário
para colar. Faça a colagem se o tipo MIME for compatível. É possível forçar os dados
na área de transferência para o texto usando
coerceToText()
. Caso seu aplicativo ofereça suporte mais de um dos tipos MIME disponíveis, é possível permitir que o usuário escolha qual deles usar.