Boutons d'icône

Les boutons d'icône affichent les actions que les utilisateurs peuvent effectuer. Les boutons d'icône doivent utiliser une icône dont la signification est claire et représentent généralement des actions courantes ou fréquemment utilisées.

Il existe deux types de boutons d'icône :

  • Par défaut : ces boutons peuvent ouvrir d'autres éléments, comme un menu ou une recherche.
  • Bouton d'activation/désactivation : ces boutons peuvent représenter des actions binaires qui peuvent être activées ou désactivées, comme "favori" ou "signet".
Cinq boutons d'icône avec différentes icônes (paramètres, plus, etc.). Certaines sont pleines, ce qui indique qu'elles sont sélectionnées, et d'autres sont vides.
Figure 1. Boutons d'icônes, dont certains sont pleins (indiquant une sélection) et d'autres sont entourés d'un contour.

Surface d'API

Utilisez le composable IconButton pour implémenter des boutons d'icône standards. Pour créer différents styles visuels tels que "filled", "filled tonal" ou "outlined", utilisez respectivement FilledIconButton, FilledTonalIconButton et OutlinedIconButton.

Les paramètres clés de IconButton sont les suivants :

  • onClick : fonction lambda qui s'exécute lorsque l'utilisateur appuie sur le bouton d'icône.
  • enabled : valeur booléenne qui contrôle l'état d'activation du bouton. Lorsque la valeur est false, le bouton ne répond pas aux entrées de l'utilisateur.
  • content : contenu composable à l'intérieur du bouton, généralement un Icon.

Exemple de base : bouton d'icône d'activation/désactivation

Cet exemple montre comment implémenter un bouton d'icône d'activation/désactivation. Un bouton d'icône d'activation/désactivation change d'apparence selon qu'il est sélectionné ou non.

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

Points clés concernant le code

  • Le composable ToggleIconButtonExample définit un IconButton activable/désactivable.
    • mutableStateOf(false) crée un objet MutableState qui contient une valeur booléenne, initialement false. isToggled devient ainsi un conteneur d'état, ce qui signifie que Compose recompose l'UI chaque fois que sa valeur change.
    • rememberSaveable garantit que l'état isToggled persiste lors des modifications de configuration, comme la rotation de l'écran.
  • Le lambda onClick de IconButton définit le comportement du bouton lorsqu'un utilisateur clique dessus, en basculant l'état entre true et false.
  • Le paramètre painter du composable Icon charge de manière conditionnelle un painterResource différent en fonction de l'état isToggled. Cela modifie l'apparence visuelle de l'icône.
    • Si isToggled est défini sur true, il charge l'élément drawable en forme de cœur plein.
    • Si isToggled est défini sur false, il charge l'élément drawable en forme de cœur.
  • La contentDescription de Icon est également mise à jour en fonction de l'état de isToggled pour fournir les informations d'accessibilité appropriées.

Résultat

L'image suivante montre le bouton d'icône d'activation/désactivation de l'extrait précédent dans son état non sélectionné :

Bouton d'activation/désactivation des favoris (un cœur) à l'état non sélectionné (non rempli).
Figure 2. Bouton d'activation/désactivation "Favori" non sélectionné.

Exemple avancé : actions répétées lors d'un appui

Cette section explique comment créer des boutons d'icône qui déclenchent une action en continu lorsque l'utilisateur appuie de manière prolongée dessus, plutôt que de la déclencher une seule fois par 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,
        )
    }
}

Points clés concernant le code

  • MomentaryIconButton utilise un unselectedImage: Int, l'ID de ressource drawable de l'icône lorsque le bouton n'est pas enfoncé, et selectedImage: Int, l'ID de ressource drawable de l'icône lorsque le bouton est enfoncé.
  • Il utilise un interactionSource pour suivre spécifiquement les interactions de type "appui" de l'utilisateur.
  • isPressed est défini sur "true" lorsque l'utilisateur appuie sur le bouton, et sur "false" dans le cas contraire. Lorsque isPressed est défini sur true, LaunchedEffect entre dans une boucle.
    • Dans cette boucle, il utilise un delay (avec stepDelay) pour créer des pauses entre les actions de déclenchement. coerceIn garantit que le délai est d'au moins 1 ms pour éviter les boucles infinies.
    • pressedListener est appelé après chaque délai dans la boucle. L'action est alors répétée.
  • pressedListener utilise rememberUpdatedState pour s'assurer que le lambda onClick (l'action à effectuer) est toujours le plus récent de la dernière composition.
  • L'image affichée par Icon change selon que l'utilisateur appuie ou non sur le bouton.
    • Si isPressed est défini sur "true", l'élément selectedImage s'affiche.
    • Sinon, l'unselectedImage s'affiche.

Ensuite, utilisez ce MomentaryIconButton dans un exemple. L'extrait de code suivant illustre deux boutons d'icône contrôlant un compteur :

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

Points clés concernant le code

  • Le composable MomentaryIconButtonExample affiche un Row contenant deux instances MomentaryIconButton et un composable Text pour créer une UI permettant d'incrémenter et de décrémenter un compteur.
  • Il conserve une variable d'état modifiable pressedCount à l'aide de remember et mutableIntStateOf, initialisée à 0. Lorsque pressedCount change, tous les composables qui l'observent (comme le composable Text) se recomposent pour refléter la nouvelle valeur.
  • Le premier MomentaryIconButton diminue pressedCount lorsque l'utilisateur clique dessus ou le maintient enfoncé.
  • Le deuxième MomentaryIconButton augmente pressedCount lorsque l'utilisateur clique dessus ou le maintient enfoncé.
  • Les deux boutons utilisent un stepDelay de 100 millisecondes, ce qui signifie que l'action onClick se répète toutes les 100 ms lorsqu'un bouton est enfoncé.

Résultat

La vidéo suivante montre l'interface utilisateur avec les boutons d'icône et le compteur :

Figure 3. UI de compteur avec deux boutons d'icône (plus et moins) qui incrémentent et décrémentent le compteur.

Ressources supplémentaires