Copiar e colar

O framework do Android baseado na área de transferência para copiar e colar. oferece suporte a tipos de dados primitivos e complexos, incluindo:

  • Strings de texto
  • Estruturas de dados complexas
  • Dados de fluxo de texto e binário
  • Recursos do aplicativo

Dados de texto simples são armazenados diretamente na área de transferência, enquanto dados complexos são armazenados como referência que o aplicativo de colagem resolve com um provedor de conteúdo.

O recurso de copiar e colar funciona dentro e entre aplicativos que implementam a estrutura.

Como parte do framework usa provedores de conteúdo, este documento pressupõe certa familiaridade com a API Android Content Provider.

Trabalhar com texto

Alguns componentes oferecem suporte à cópia e colagem de texto fora da caixa, conforme mostrado na tabela a seguir.

Componente Copiar texto Colando texto
Campo de texto básico
Campo de texto
Contêiner de seleção

Por exemplo, você pode copiar o texto do card para a área de transferência no snippet a seguir e colar o texto copiado no TextField. Para mostrar o menu e colar o texto, toque e pressione o TextField ou toque na alça do cursor.

val textFieldState = rememberTextFieldState()

Column {
    Card {
        SelectionContainer {
            Text("You can copy this text")
        }
    }
    BasicTextField(state = textFieldState)
}

Você pode colar o texto com o seguinte atalho de teclado: Ctrl+V . O atalho do teclado também está disponível por padrão. Consulte Processar ações do teclado para mais detalhes.

Copiar com ClipboardManager

É possível copiar textos para a área de transferência com o ClipboardManager. O método setText() copia o objeto String transmitido para a área de transferência. O snippet a seguir copia "Hello, clipboard" para a área de transferência quando o usuário clica no botão.

// Retrieve a ClipboardManager object
val clipboardManager = LocalClipboardManager.current

Button(
    onClick = {
        // Copy "Hello, clipboard" to the clipboard
        clipboardManager.setText("Hello, clipboard")
    }
) {
   Text("Click to copy a text")
}

O snippet a seguir faz o mesmo, mas oferece um controle mais granular. Um caso de uso comum é a cópia de conteúdo sensível, como a senha. ClipEntry descreve um item na área de transferência. Ele contém um objeto ClipData que descreve os dados na área de transferência. ClipData.newPlainText() é um método conveniente para criar um objeto ClipData com base em um objeto String. É possível definir o objeto ClipEntry criado na área de transferência chamando o método setClip() sobre o objeto ClipboardManager.

// Retrieve a ClipboardManager object
val clipboardManager = LocalClipboardManager.current

Button(
    onClick = {
        val clipData = ClipData.newPlainText("plain text", "Hello, clipboard")
        val clipEntry = ClipEntry(clipData)
        clipboardManager.setClip(clipEntry)
    }
) {
   Text("Click to copy a text")
}

Colar com o ClipboardManager

É possível acessar o texto copiado para a área de transferência chamando o método getText() no ClipboardManager. O método getText() retorna um objeto AnnotatedString quando um texto é copiado na área de transferência. O snippet a seguir anexa texto na área de transferência ao texto na TextField.

var textFieldState = rememberTextFieldState()

Column {
    TextField(state = textFieldState)

    Button(
        onClick = {
            // The getText method returns an AnnotatedString object or null
            val annotatedString = clipboardManager.getText()
            if(annotatedString != null) {
                // The pasted text is placed on the tail of the TextField
                textFieldState.edit {
                    append(text.toString())
                }
            }
        }
    ) {
        Text("Click to paste the text in the clipboard")
    }
}

Trabalhar com conteúdo avançado

Os usuários adoram imagens, vídeos e outros conteúdos expressivos. O app pode permitir que o usuário copie conteúdo rico com ClipboardManager e ClipEntry. O modificador contentReceiver ajuda a implementar a colagem de conteúdo avançado.

Copiar conteúdo avançado

O app não pode copiar conteúdo rico diretamente para a área de transferência. Em vez disso, o app transmite um objeto URI para a área de transferência. e fornece acesso ao conteúdo com um ContentProvider. O snippet de código a seguir mostra como copiar uma imagem JPEG para a área de transferência. Consulte Copiar fluxos de dados para mais detalhes.

// Get a reference to the context
val context = LocalContext.current

Button(
    onClick = {
        // URI of the copied JPEG data
        val uri = Uri.parse("content://your.app.authority/0.jpg")
        // Create a ClipData object from the URI value
        // A ContentResolver finds a proper ContentProvider so that ClipData.newUri can set appropriate MIME type to the given URI
        val clipData = ClipData.newUri(context.contentResolver, "Copied", uri)
        // Create a ClipEntry object from the clipData value
        val clipEntry = ClipEntry(clipData)
        // Copy the JPEG data to the clipboard
        clipboardManager.setClip(clipEntry)
    }
) {
    Text("Copy a JPEG data")
}

Colar um conteúdo avançado

Com o modificador contentReceiver, é possível colar conteúdo rico em BasicTextField no componente modificado. O snippet de código a seguir adiciona o URI colado de dados de imagem. a uma lista de objetos Uri.

// A URI list of images
val imageList by remember{ mutableListOf<Uri>() }

// Remember the ReceiveContentListener object as it is created inside a Composable scope
val receiveContentListener = remember {
    ReceiveContentListener { transferableContent ->
        // Handle the pasted data if it is image data
        when {
            // Check if the pasted data is an image or not
            transferableContent.hasMediaType(MediaType.Image)) -> {
                // Handle for each ClipData.Item object
                // The consume() method returns a new TransferableContent object containging ignored ClipData.Item objects
                transferableContent.consume { item ->
                    val uri = item.uri
                    if (uri != null) {
                        imageList.add(uri)
                    }
                   // Mark the ClipData.Item object consumed when the retrieved URI is not null
                    uri != null
                }
            }
            // Return the given transferableContent when the pasted data is not an image
            else -> transferableContent
        }
    }
}

val textFieldState = rememberTextFieldState()

BasicTextField(
    state = textFieldState,
    modifier = Modifier
        .contentReceiver(receiveContentListener)
        .fillMaxWidth()
        .height(48.dp)
)

O modificador contentReceiver usa um objeto ReceiveContentListener como argumento e chama o método onReceive do objeto transmitido quando o usuário cola dados no BasicTextField dentro do componente modificado.

Um objeto TransferableContent é transmitido para o método onReceber, que descreve os dados que podem ser transferidos entre apps colando neste caso. Você pode acessar o objeto ClipEntry referindo-se ao atributo clipEntry.

Um objeto ClipEntry pode ter vários objetos ClipData.Item. quando o usuário seleciona várias imagens e as copia para a área de transferência. por exemplo. Marque cada objeto ClipData.Item como consumido ou ignorado. e retornar uma TransferableContent contendo os objetos ClipData.Item ignorados para que o modificador contentReceiver do ancestral mais próximo possa recebê-lo.

O método TransferableContent.hasMediaType() pode ajudar a determinar se o objeto TransferableContent pode fornecer um item com o tipo de mídia. Por exemplo, a chamada de método a seguir retorna true se o objeto TransferableContent puder fornecer uma imagem.

transferableContent.hasMediaType(MediaType.Image)

Trabalhar com dados complexos

É possível copiar dados complexos para a área de transferência da mesma forma que você faz para o conteúdo rico. Consulte Usar provedores de conteúdo para copiar dados complexos para mais detalhes.

Também é possível processar as colagens de dados complexos da mesma maneira para o conteúdo rico. Você pode receber um URI dos dados colados. Os dados reais podem ser recuperados de um ContentProvider. Consulte Recuperar dados do provedor para mais informações.

Feedback sobre a cópia de conteúdo

Os usuários esperam feedback ao copiar conteúdo para a área de transferência. Portanto, além do framework usado para copiar e colar, O Android mostra uma interface padrão para os usuários quando eles fazem cópias no Android 13 (nível 33 da API) e superiores. Devido a esse recurso, há o risco de notificação duplicada. Saiba mais sobre esse caso extremo em Evitar notificações duplicadas.

Uma animação mostrando a notificação da área de transferência do Android 13
Figura 1. Interface mostrada quando o conteúdo é inserido na área de transferência no Android 13 ou mais.

Enviar feedback manualmente aos usuários ao copiar no Android 12L (nível 32 da API) e versões anteriores. Consulte a recomendação.

Conteúdo sensível

Se você permitir que o usuário copie conteúdo sensível para a área de transferência, como senhas, o app precisa informar ao sistema para que o sistema evite a exibição do conteúdo sensível copiado na interface (Figura 2).

Visualização do texto copiado com sinalização de conteúdo sensível.
Figura 2. Visualização do texto copiado com uma flag de conteúdo sensível.

É necessário adicionar uma flag a ClipDescription em ClipData antes de chamar o método setClip() sobre o objeto ClipboardManager:

// 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)
    }
}