Botões de ícone

Os botões de ícone mostram 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 pesquisa.
  • Alternar: esses botões podem representar ações binárias que podem ser ativadas ou desativadas, como "favorito" ou "marcador".
Cinco botões de ícones com ícones diferentes (configurações, mais, etc.). Alguns são preenchidos, indicando seleção, e outros são delineados.
Figura 1. Botões de ícone, alguns deles preenchidos (indicam seleção) e contornados.

Superfície da API

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

Os principais parâmetros de IconButton incluem:

  • onClick: uma função lambda que é executada quando o usuário toca no botão do ícone.
  • enabled: um booleano que controla o estado ativado 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 ativação

Este exemplo mostra como implementar um botão de ícone de alternância. Um botão de ícone de alternância muda a aparência dependendo se ele 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 com alternância.
    • 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.
    • O rememberSaveable garante que o estado do isToggled persista em mudanças de configuração, como a rotação da tela.
  • O 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 isToggled. Isso muda a aparência 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 delineado.
  • O contentDescription do Icon também é atualizado com base no estado isToggled para fornecer informações de acessibilidade adequadas.

Resultado

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

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

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

Esta seção demonstra como criar botões de ícone que acionam continuamente uma ação enquanto o usuário pressiona e mantém o botão, em vez de acionar 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 recebe um unselectedImage: Int, o ID do recurso drawable do ícone quando o botão não está pressionado, e selectedImage: Int, o ID do recurso drawable do ícone quando o botão está 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 ativamente 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 a lambda onClick (a ação a ser realizada) seja sempre a mais atualizada da composição mais recente.
  • O Icon muda a imagem exibida de acordo com se o botão está pressionado ou não.
    • Se isPressed for verdadeiro, o selectedImage será mostrado.
    • Caso contrário, o unselectedImage será mostrado.

Em seguida, use este 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 contendo duas instâncias MomentaryIconButton e um elemento combinável Text para criar uma interface para incrementar e decrementar 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 combináveis que o observam (como o elemento combinável Text) são recompostas para refletir o novo valor.
  • O primeiro MomentaryIconButton diminui pressedCount quando clicado ou pressionado.
  • O segundo MomentaryIconButton aumenta pressedCount quando clicado ou mantido pressionado.
  • Ambos os 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 é mantido 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 (sinal de mais e de menos) que incrementam e diminuem o contador.

Outros recursos