Botões de ícone

Os botões de ícone mostram as ações que os usuários podem realizar. Os botões de ícone precisam usar um ícone com um significado claro e geralmente representam ações comuns ou usadas com frequência.

Há dois tipos de botões de ícone:

  • Padrão: esses botões podem abrir outros elementos, como um menu ou uma pesquisa.
  • Alternância: esses botões podem representar ações binárias que podem ser ativadas ou desativadas, como "favorito" ou "marcador".
Cinco botões com ícones diferentes (configurações, mais etc.). Alguns estão preenchidos, indicando que foram selecionados, e outros estão contornados.
Figura 1. Botões de ícone, alguns preenchidos (indicando seleção) e contornados.

Superfície da API

Use o elemento combinável IconButton para implementar botões de ícone padrão. Para criar estilos visuais diferentes, como preenchido, preenchido com tonalidade ou contornado, use FilledIconButton, FilledTonalIconButton e OutlinedIconButton, respectivamente.

Os principais parâmetros para IconButton incluem:

  • onClick: uma função lambda que é executada quando o usuário toca no botão de ícone.
  • enabled: um booleano que controla o estado de ativação do botão. Quando false, o botão não responde à entrada do usuário.
  • content: o conteúdo combinável dentro do botão, normalmente um Icon.

Exemplo básico: botão de ícone de alternância

Este exemplo mostra como implementar um botão de ícone de alternância. Um botão de ícone de alternância muda de aparência dependendo se está selecionado ou não.

@Preview
@Composable
fun ToggleIconButtonExample() {
    // isToggled initial value should be read from a view model or persistent storage.
    var isToggled by rememberSaveable { mutableStateOf(false) }

    IconButton(
        onClick = { isToggled = !isToggled }
    ) {
        Icon(
            painter = if (isToggled) painterResource(R.drawable.favorite_filled) else painterResource(R.drawable.favorite),
            contentDescription = if (isToggled) "Selected icon button" else "Unselected icon button."
        )
    }
}

Pontos principais sobre o código

  • O elemento combinável ToggleIconButtonExample define um IconButton que pode ser ativado ou desativado.
    • mutableStateOf(false) cria um objeto MutableState que contém um valor booleano, inicialmente false. Isso torna isToggled um detentor de estado, o que significa que o Compose recompõe a interface sempre que o valor muda.
    • rememberSaveable garante que o estado de isToggled persista em mudanças de configuração, como a rotação da tela.
  • A lambda onClick do IconButton define o comportamento do botão quando clicado, alternando o estado entre true e false.
  • O parâmetro painter do elemento combinável Icon carrega condicionalmente um painterResource diferente com base no estado de isToggled. Isso muda a aparência visual do ícone.
    • Se isToggled for true, ele vai carregar o drawable de coração preenchido.
    • Se isToggled for false, ele vai carregar o drawable de coração contornado.
  • O contentDescription do Icon também é atualizado com base no estado do isToggled para fornecer informações de acessibilidade adequadas.

Resultado

A imagem a seguir mostra o botão de alternância do snippet anterior no estado não selecionado:

Um botão de alternância de favoritos (um coração) no estado não selecionado (não preenchido).
Figura 2. Um botão de alternância "favorito" no estado não selecionado.

Exemplo avançado: ações repetidas ao pressionar

Nesta seção, mostramos como criar botões de ícone que acionam continuamente uma ação enquanto o usuário os toca e pressiona, em vez de acionar apenas uma vez por clique.

@Composable
fun MomentaryIconButton(
    unselectedImage: Int,
    selectedImage: Int,
    contentDescription: String,
    modifier: Modifier = Modifier,
    stepDelay: Long = 100L, // Minimum value is 1L milliseconds.
    onClick: () -> Unit
) {
    val interactionSource = remember { MutableInteractionSource() }
    val isPressed by interactionSource.collectIsPressedAsState()
    val pressedListener by rememberUpdatedState(onClick)

    LaunchedEffect(isPressed) {
        while (isPressed) {
            delay(stepDelay.coerceIn(1L, Long.MAX_VALUE))
            pressedListener()
        }
    }

    IconButton(
        modifier = modifier,
        onClick = onClick,
        interactionSource = interactionSource
    ) {
        Icon(
            painter = if (isPressed) painterResource(id = selectedImage) else painterResource(id = unselectedImage),
            contentDescription = contentDescription,
        )
    }
}

Pontos principais sobre o código

  • MomentaryIconButton usa um unselectedImage: Int, o ID do recurso drawable para o ícone quando o botão não é pressionado, e selectedImage: Int, o ID do recurso drawable para o ícone quando o botão é pressionado.
  • Ele usa um interactionSource para rastrear especificamente as interações de "pressionar" do usuário.
  • isPressed é verdadeiro quando o botão está sendo pressionado e falso caso contrário. Quando isPressed é true, o LaunchedEffect entra em um loop.
    • Dentro desse loop, ele usa um delay (com stepDelay) para criar pausas entre as ações de acionamento. coerceIn garante que o atraso seja de pelo menos 1 ms para evitar loops infinitos.
    • O pressedListener é invocado após cada atraso no loop. Isso faz com que a ação seja repetida.
  • O pressedListener usa rememberUpdatedState para garantir que o lambda onClick (a ação a ser executada) seja sempre o mais atualizado da composição mais recente.
  • O Icon muda a imagem exibida com base no fato de o botão estar pressionado ou não.
    • Se isPressed for verdadeiro, o selectedImage será mostrado.
    • Caso contrário, o unselectedImage será mostrado.

Em seguida, use esse MomentaryIconButton em um exemplo. O snippet a seguir demonstra dois botões de ícone que controlam um contador:

@Preview()
@Composable
fun MomentaryIconButtonExample() {
    var pressedCount by remember { mutableIntStateOf(0) }

    Row(
        modifier = Modifier.fillMaxWidth(),
        verticalAlignment = Alignment.CenterVertically
    ) {
        MomentaryIconButton(
            unselectedImage = R.drawable.fast_rewind,
            selectedImage = R.drawable.fast_rewind_filled,
            stepDelay = 100L,
            onClick = { pressedCount -= 1 },
            contentDescription = "Decrease count button"
        )
        Spacer(modifier = Modifier)
        Text("advanced by $pressedCount frames")
        Spacer(modifier = Modifier)
        MomentaryIconButton(
            unselectedImage = R.drawable.fast_forward,
            selectedImage = R.drawable.fast_forward_filled,
            contentDescription = "Increase count button",
            stepDelay = 100L,
            onClick = { pressedCount += 1 }
        )
    }
}

Pontos principais sobre o código

  • O elemento combinável MomentaryIconButtonExample mostra um Row que contém duas instâncias de MomentaryIconButton e um elemento combinável Text para criar uma interface que incrementa e decrementa um contador.
  • Ele mantém uma variável de estado mutável pressedCount usando remember e mutableIntStateOf, inicializada como 0. Quando pressedCount muda, todos os elementos que o observam (como o elemento combinável Text) são recompostos para refletir o novo valor.
  • O primeiro MomentaryIconButton diminui pressedCount quando clicado ou pressionado.
  • O segundo MomentaryIconButton aumenta o pressedCount quando clicado ou pressionado.
  • Os dois botões usam um stepDelay de 100 milissegundos, o que significa que a ação onClick é repetida a cada 100 ms enquanto um botão é pressionado.

Resultado

O vídeo a seguir mostra a interface com os botões de ícone e o contador:

Figura 3. Uma interface de contador com dois botões de ícone (mais e menos) que aumentam e diminuem o contador.

Outros recursos