Botones de íconos

Los botones de ícono muestran las acciones que los usuarios pueden realizar. Los botones de ícono deben usar un ícono con un significado claro y, por lo general, representan acciones comunes o de uso frecuente.

Existen dos tipos de botones de ícono:

  • Predeterminado: Estos botones pueden abrir otros elementos, como un menú o una búsqueda.
  • Activar o desactivar: Estos botones pueden representar acciones binarias que se pueden activar o desactivar, como "favorito" o "marcador".
5 botones con diferentes íconos (configuración, más, etc.). Algunos están rellenos, lo que indica que están seleccionados, y otros tienen un contorno.
Figura 1: Botones de ícono, algunos de los cuales están rellenos (lo que indica la selección) y con contorno.

Superficie de la API

Usa el elemento componible IconButton para implementar botones de ícono estándar. Para crear diferentes estilos visuales, como relleno, tonalidad de relleno o contorno, usa FilledIconButton, FilledTonalIconButton y OutlinedIconButton, respectivamente.

Los parámetros clave para IconButton incluyen los siguientes:

  • onClick: Es una función lambda que se ejecuta cuando el usuario presiona el botón de ícono.
  • enabled: Es un valor booleano que controla el estado habilitado del botón. Cuando es false, el botón no responde a la entrada del usuario.
  • content: Es el contenido componible dentro del botón, por lo general, un Icon.

Ejemplo básico: Botón de activación con ícono

En este ejemplo, se muestra cómo implementar un botón de activación con ícono. Un botón de ícono de activación cambia su apariencia según si está seleccionado o no.

@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."
        )
    }
}

Puntos clave sobre el código

  • El elemento ToggleIconButtonExample componible define un IconButton que se puede activar o desactivar.
    • mutableStateOf(false) crea un objeto MutableState que contiene un valor booleano, inicialmente false. Esto convierte a isToggled en un contenedor de estado, lo que significa que Compose recompone la IU cada vez que cambia su valor.
    • rememberSaveable garantiza que el estado de isToggled persista en los cambios de configuración, como la rotación de la pantalla.
  • La lambda onClick de IconButton define el comportamiento del botón cuando se hace clic en él, lo que alterna el estado entre true y false.
  • El parámetro painter del elemento que admite composición Icon carga de forma condicional un painterResource diferente según el estado de isToggled. Esto cambia la apariencia visual del ícono.
    • Si isToggled es true, se carga el elemento de diseño de corazón relleno.
    • Si isToggled es false, se carga el elemento de diseño de corazón delineado.
  • El contentDescription de Icon también se actualiza según el estado de isToggled para proporcionar información de accesibilidad adecuada.

Resultado

En la siguiente imagen, se muestra el botón de ícono de activación del fragmento anterior en su estado no seleccionado:

Un botón de activación de favoritos (un corazón) en su estado no seleccionado (sin rellenar).
Figura 2: Un botón de ícono de activación de "favorito" en su estado no seleccionado.

Ejemplo avanzado: Acciones repetidas cuando se presiona

En esta sección, se muestra cómo crear botones de ícono que activen una acción de forma continua mientras el usuario los mantiene presionados, en lugar de activarla solo una vez por clic.

@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,
        )
    }
}

Puntos clave sobre el código

  • MomentaryIconButton toma un unselectedImage: Int, el ID de recurso de elementos de diseño para el ícono cuando no se presiona el botón, y selectedImage: Int, el ID de recurso de elementos de diseño para el ícono cuando se presiona el botón.
  • Usa un interactionSource para hacer un seguimiento específico de las interacciones de "presión" del usuario.
  • isPressed es verdadero cuando se presiona el botón de forma activa y falso en caso contrario. Cuando isPressed es true, el LaunchedEffect entra en un bucle.
    • Dentro de este bucle, usa un delay (con stepDelay) para crear pausas entre las acciones de activación. coerceIn garantiza que el retraso sea de al menos 1 ms para evitar bucles infinitos.
    • Se invoca pressedListener después de cada retraso dentro del bucle. Esto hace que la acción se repita.
  • pressedListener usa rememberUpdatedState para garantizar que la lambda onClick (la acción que se realizará) siempre sea la más actualizada de la composición más reciente.
  • El Icon cambia la imagen que muestra según si el botón está presionado o no.
    • Si isPressed es verdadero, se muestra selectedImage.
    • De lo contrario, se muestra el unselectedImage.

Luego, usa este MomentaryIconButton en un ejemplo. En el siguiente fragmento, se muestran dos botones de ícono que controlan un 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 }
        )
    }
}

Puntos clave sobre el código

  • El elemento componible MomentaryIconButtonExample muestra un Row que contiene dos instancias de MomentaryIconButton y un elemento componible Text para compilar una IU que incremente y disminuya un contador.
  • Mantiene una variable de estado mutable pressedCount con remember y mutableIntStateOf, inicializada en 0. Cuando cambia pressedCount, se vuelven a componer todos los elementos componibles que lo observan (como el elemento componible Text) para reflejar el valor nuevo.
  • El primer MomentaryIconButton disminuye pressedCount cuando se hace clic en él o se mantiene presionado.
  • El segundo MomentaryIconButton aumenta pressedCount cuando se hace clic en él o se mantiene presionado.
  • Ambos botones usan un stepDelay de 100 milisegundos, lo que significa que la acción onClick se repite cada 100 ms mientras se mantiene presionado un botón.

Resultado

En el siguiente video, se muestra la IU con los botones de ícono y el contador:

Figura 3. Una IU de contador con dos botones de ícono (más y menos) que aumentan y disminuyen el contador.

Recursos adicionales