Texto no Compose

O texto é uma peça central de qualquer IU, e o Jetpack Compose facilita a exibição ou criação de textos. O Compose usa a composição de elementos básicos, o que significa que não é necessário substituir propriedades e métodos ou estender grandes classes para fazer com que um design e uma lógica que possam ser compostos funcionem da forma que você quer.

Como base, o Compose fornece um BasicText e um BasicTextField, que são as estruturas básicas para exibir o texto e processar a entrada do usuário. Em um nível superior, o Compose fornece o Text e o TextField, que são elementos que podem ser compostos de acordo com as diretrizes do Material Design. É recomendável usar esses elementos, porque eles têm uma aparência familiar para os usuários do Android e incluem outras opções para simplificar a personalização, sem que seja necessário programar muito código.

Como exibir textos

A forma mais básica de exibir textos é usando o elemento Text que pode ser composto com uma String como argumento:

@Composable
fun SimpleText() {
  Text("Hello World")
}

Palavras "Hello World" em texto preto simples

Exibir texto do recurso

Recomendamos que você use recursos de string em vez de valores de Text fixos no código, porque, dessa forma, será possível compartilhar as mesmas strings com suas visualizações do Android e preparar o app para internacionalização:

@Composable
fun StringResourceText() {
  Text(stringResource(R.string.hello_world))
}

Como definir o estilo do texto

O elemento Text que pode ser composto tem vários parâmetros opcionais para definir o estilo do conteúdo. Veja abaixo uma lista dos parâmetros que abrangem os casos de uso mais comuns relacionados a textos. Para ver todos os parâmetros de Text, recomendamos consultar o código-fonte do elemento Text do Compose.

Sempre que você definir um desses parâmetros, o mesmo estilo será aplicado a todo o valor do texto. Caso seja necessário aplicar vários estilos na mesma linha ou parágrafo, consulte a seção sobre vários estilos inline.

Como mudar a cor do texto

@Composable
fun BlueText() {
  Text("Hello World", color = Color.Blue)
}

Palavras "Hello World" em texto azul

Como mudar o tamanho do texto

@Composable
fun BigText() {
  Text("Hello World", fontSize = 30.sp)
}

Palavras "Hello World" em tamanho maior

Como deixar o texto em itálico

@Composable
fun ItalicText() {
  Text("Hello World", fontStyle = FontStyle.Italic)
}

Palavras "Hello World" em itálico

Como deixar o texto em negrito

@Composable
fun BoldText() {
    Text("Hello World", fontWeight = FontWeight.Bold)
}

Palavras "Hello World" em negrito

Alinhamentos de texto

O parâmetro textAlign permite definir o alinhamento do texto em uma área de superfície do elemento Text que pode ser composto.

Por padrão, Text selecionará o alinhamento natural do texto, dependendo do valor do conteúdo:

  • Borda esquerda do contêiner Text para alfabetos que seguem o sentido da esquerda para a direita, como latino, cirílico ou hangul
  • Borda direita do contêiner Text para alfabetos que seguem o sentido da direita para a esquerda, como árabe ou hebraico
@Preview(showBackground = true)
@Composable
fun CenterText() {
    Text("Hello World", textAlign = TextAlign.Center,
                modifier = Modifier.width(150.dp))
}

Palavras "Hello World" centralizadas no elemento contêiner

Caso queira definir manualmente o alinhamento do texto de um elemento Text que pode ser composto, use TextAlign.Start e TextAlign.End, em vez de TextAlign.Left e TextAlign.Right, respectivamente, porque eles se referem à borda correta do Text, que pode ser composto dependendo da orientação preferencial de idioma. Por exemplo, TextAlign.End fica alinhado à direita para textos em francês e à esquerda para textos em árabe, mas TextAlign.Right alinha o texto à direita independentemente do alfabeto usado.

Como trabalhar com fontes

Text tem um parâmetro fontFamily para permitir a configuração da fonte usada no elemento que pode ser composto. Por padrão, as famílias de fontes Serif, Sans Serif, fontes monoespaçadas e cursivas estão incluídas:

@Composable
fun DifferentFonts() {
    Column {
        Text("Hello World", fontFamily = FontFamily.Serif)
        Text("Hello World", fontFamily = FontFamily.SansSerif)
    }
}

Palavras "Hello World" em duas fontes diferentes, com e sem serifas

É possível usar o atributo fontFamily para trabalhar com fontes personalizadas, definidas na pasta res/fonts:

Representação gráfica da pasta "res > font" do ambiente para desenvolvedores

O seguinte exemplo mostra como definir uma fontFamily com base nesses arquivos de fonte:

val firaSansFamily = FontFamily(
        Font(R.font.firasans_light, FontWeight.Light),
        Font(R.font.firasans_regular, FontWeight.Normal),
        Font(R.font.firasans_italic, FontWeight.Normal, FontStyle.Italic),
        Font(R.font.firasans_medium, FontWeight.Medium),
        Font(R.font.firasans_bold, FontWeight.Bold)
)

Por fim, você pode transmitir essa fontFamily para o elemento Text que pode ser composto. Como uma fontFamily pode incluir pesos diferentes, é possível definir o fontWeight manualmente para selecionar o peso certo para o texto:

Column {
    Text(..., fontFamily = firaSansFamily, fontWeight = FontWeight.Light)
    Text(..., fontFamily = firaSansFamily, fontWeight = FontWeight.Normal)
    Text(
        ..., fontFamily = firaSansFamily, fontWeight = FontWeight.Normal,
        fontStyle = FontStyle.Italic
    )
    Text(..., fontFamily = firaSansFamily, fontWeight = FontWeight.Medium)
    Text(..., fontFamily = firaSansFamily, fontWeight = FontWeight.Bold)
}

Palavras "Hello World" em vários tipos de peso e estilo diferentes

Para saber como definir a tipografia no app todo, consulte a documentação sobre temas.

Vários estilos em um texto

Para definir estilos diferentes no mesmo elemento Text, use uma AnnotatedString, string que pode receber anotações de estilos arbitrárias.

AnnotatedString é uma classe de dados contendo:

  • Um valor Text
  • Uma List de SpanStyleRange, equivalente ao estilo inline com intervalo de posições dentro do valor de texto
  • Uma List de ParagraphStyleRange, especificando o alinhamento do texto, a direção do texto, a altura da linha e o estilo de recuo do texto

TextStyle é usado no Text que pode ser composto, ao passo que SpanStyle e ParagraphStyle são usados em AnnotatedString.

A diferença entre SpanStyle e ParagraphStyle é que ParagraphStyle pode ser aplicado a um parágrafo inteiro, enquanto SpanStyle pode ser aplicado no nível do caractere. Depois que uma parte do texto for marcada com um ParagraphStyle, ela ficará separada do restante, como se tivesse feeds de linha no início e no fim.

A AnnotatedString tem um builder de tipo seguro (link em inglês) para facilitar a criação:

@Composable
fun MultipleStylesInText() {
    Text(
        buildAnnotatedString {
            withStyle(style = SpanStyle(color = Color.Blue)) {
                append("H")
            }
            append("ello ")

            withStyle(style = SpanStyle(fontWeight = FontWeight.Bold, color = Color.Red)) {
                append("W")
            }
            append("orld")
        }
    )
}

Palavras "Hello World" com várias mudanças de estilo inline. A letra H está em azul e a letra W está em vermelho e negrito

Podemos definir os estilos de parágrafos da mesma forma:

@Composable
fun ParagraphStyle() {
    Text(
        buildAnnotatedString {
            withStyle(style = ParagraphStyle(lineHeight = 30.sp)) {
                withStyle(style = SpanStyle(color = Color.Blue)) {
                    append("Hello\n")
                }
                withStyle(
                    style = SpanStyle(
                        fontWeight = FontWeight.Bold,
                        color = Color.Red
                    )
                ) {
                    append("World\n")
                }
                append("Compose")
            }
        }
    )
}

Três parágrafos com três estilos diferentes: azul, vermelho e negrito e preto simples

Número máximo de linhas

Para limitar o número de linhas visíveis em um Text que pode ser composto, defina o parâmetro maxLines:

@Composable
fun LongText() {
    Text("hello ".repeat(50), maxLines = 2)
}

Trecho de texto longo truncado após duas linhas

Excesso de texto

Ao limitar um texto longo, é recomendável indicar a existência de texto em excesso, que só será exibido se o texto na tela for truncado. Para fazer isso, defina o parâmetro textOverflow:

@Composable
fun OverflowedText() {
    Text("Hello Compose ".repeat(50), maxLines = 2, overflow = TextOverflow.Ellipsis)
}

Trecho de texto longo truncado após duas linhas, com uma elipse no final

Temas

Para usar o tema do app para definir o estilo de texto, consulte a documentação sobre temas.

Interações do usuário

O Jetpack Compose permite interatividade detalhada no Text. A seleção de texto agora é mais flexível e pode ser feita em layouts que podem ser compostos. As interações do usuário nos textos são diferentes de outros layouts que podem ser compostos, já que não é possível adicionar um modificador a uma parte de um elemento de Text que pode ser composto. Esta seção destaca as diferentes APIs para permitir interações do usuário.

Como selecionar texto

Por padrão, os elementos que podem ser compostos não são selecionáveis, ou seja, os usuários não podem selecionar e copiar textos do app. Para ativar a seleção de texto, é necessário unir os elementos de texto com um elemento SelectionContainer:

@Composable
fun SelectableText() {
    SelectionContainer {
        Text("This text is selectable")
    }
}

Pequeno trecho de texto selecionado pelo usuário.

Você pode desativar a seleção de texto em partes específicas de uma área selecionável. Para fazer isso, é necessário unir a parte não selecionável com um elemento DisableSelection:

@Composable
fun PartiallySelectableText() {
    SelectionContainer {
        Column {
            Text("This text is selectable")
            Text("This one too")
            Text("This one as well")
            DisableSelection {
                Text("But not this one")
                Text("Neither this one")
            }
            Text("But again, you can select this one")
            Text("And this one too")
        }
    }
}

Trecho de texto mais longo. O usuário tentou selecionar a passagem inteira, mas, como duas linhas tinham a opção "DisableSelection" aplicada, elas não foram selecionadas.

Como descobrir a posição de um clique no texto

Para ouvir cliques no Text, adicione o modificador clickable. No entanto, se você quiser descobrir a posição de um clique em um Text que pode ser composto, como no caso em que há ações diferentes baseadas em partes distintas do texto, é necessário usar um ClickableText:

@Composable
fun SimpleClickableText() {
    ClickableText(
        text = AnnotatedString("Click Me"),
        onClick = { offset ->
            Log.d("ClickableText", "$offset -th character is clicked.")
        }
    )
}

Clique com anotação

Quando um usuário clica em um Text que pode ser composto, você pode anexar mais informações a uma parte do valor Text, como um URL anexado a uma palavra específica que será aberto em um navegador. Para fazer isso, você precisa anexar uma anotação, que usa uma tag (String), um item (String) e um intervalo de texto como parâmetros. A partir de uma AnnotatedString, essas anotações podem ser filtradas de acordo com as tags ou intervalos de texto. Veja um exemplo:

@Composable
fun AnnotatedClickableText() {
    val annotatedText = buildAnnotatedString {
        append("Click ")

        // We attach this *URL* annotation to the following content
        // until `pop()` is called
        pushStringAnnotation(tag = "URL",
                             annotation = "https://developer.android.com")
        withStyle(style = SpanStyle(color = Color.Blue,
                                    fontWeight = FontWeight.Bold)) {
            append("here")
        }

        pop()
    }

    ClickableText(
        text = annotatedText,
        onClick = { offset ->
            // We check if there is an *URL* annotation attached to the text
            // at the clicked position
            annotatedText.getStringAnnotations(tag = "URL", start = offset,
                                                    end = offset)
                .firstOrNull()?.let { annotation ->
                    // If yes, we log its value
                    Log.d("Clicked URL", annotation.item)
                }
        }
    )
}

Como inserir e modificar texto

TextField permite que os usuários insiram e modifiquem o texto. Há dois níveis de implementações de TextField:

  1. TextField é a implementação do Material Design. Recomendamos que você escolha essa implementação, que segue as diretrizes do Material Design (link em inglês):
    • O estilo padrão é sólido
    • OutlinedTextField é a versão de estilo delineado.
  2. BasicTextField permite que os usuários editem o texto usando o hardware ou o teclado de software, mas não oferece decorações como dicas ou marcadores.
@Composable
fun SimpleFilledTextFieldSample() {
    var text by remember { mutableStateOf("Hello") }

    TextField(
        value = text,
        onValueChange = { text = it },
        label = { Text("Label") }
    )
}

Campo de texto editável contendo a palavra "Hello". O campo tem o marcador não editável "label".

@Composable
fun SimpleOutlinedTextFieldSample() {
    var text by remember { mutableStateOf("") }

    OutlinedTextField(
        value = text,
        onValueChange = { text = it },
        label = { Text("Label") }
    )
}

Campo de texto editável com borda e marcador roxos.

Como definir o estilo de TextField

TextField e BasicTextField têm vários parâmetros em comum para personalização. A lista completa do TextField está disponível no código-fonte do TextField. Veja uma lista não exaustiva de alguns parâmetros úteis:

  • singleLine
  • maxLines
  • textStyle
@Composable
fun StyledTextField() {
    var value by remember { mutableStateOf("Hello\nWorld\nInvisible") }

    TextField(
        value = value,
        onValueChange = { value = it },
        label = { Text("Enter text") },
        maxLines = 2,
        textStyle = TextStyle(color = Color.Blue, fontWeight = FontWeight.Bold),
        modifier = Modifier.padding(20.dp)
    )
}

Um TextField multilinha, com duas linhas editáveis e um marcador

Recomendamos o uso de TextField em vez de BasicTextField quando o design chama um TextFieldField ou OutlineTextField do Material Design. No entanto, BasicTextField precisa ser usado ao criar designs que não precisam das decorações da especificação do Material Design.

Opções do teclado

O TextField permite definir opções de configuração do teclado, como o layout do teclado, ou ativar a correção automática, se compatível. Algumas opções podem não ser garantidas se o teclado de software não estiver de acordo com as opções apresentadas aqui. Veja a lista de opções de teclado compatíveis:

  • capitalization
  • autoCorrect
  • keyboardType
  • imeAction

Formatação

O TextField permite definir uma formatação visual para o valor de entrada, como substituir caracteres por * para senhas ou inserir hifens a cada quatro dígitos para um número de cartão de crédito:

@Composable
fun PasswordTextField() {
    var password by rememberSaveable { mutableStateOf("") }

    TextField(
        value = password,
        onValueChange = { password = it },
        label = { Text("Enter password") },
        visualTransformation = PasswordVisualTransformation(),
        keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password)
    )
}

Campo de entrada de senha, com o texto ocultado

Mais exemplos estão disponíveis no código-fonte do VisualTransformSamples (link em inglês).