Faire glisser pour actualiser

Le composant "Pull to refresh" (Tirer pour actualiser) permet aux utilisateurs de faire glisser leur doigt vers le bas au début du contenu d'une application pour actualiser les données.

Surface d'API

Utilisez le PullToRefreshBox composable pour implémenter la fonctionnalité "Pull to refresh", qui sert de conteneur pour votre contenu défilant. Les paramètres clés suivants contrôlent le comportement et l'apparence de l'actualisation :

  • isRefreshing: valeur booléenne indiquant si l'action d'actualisation est en cours.
  • onRefresh: fonction lambda qui s'exécute lorsque l'utilisateur lance une actualisation.
  • indicator : personnalise l'indicateur que le système dessine lors de l'actualisation.

Exemple de base

Cet extrait illustre l'utilisation de base de PullToRefreshBox :

@Composable
fun PullToRefreshBasicSample(
    items: List<String>,
    isRefreshing: Boolean,
    onRefresh: () -> Unit,
    modifier: Modifier = Modifier
) {
    PullToRefreshBox(
        isRefreshing = isRefreshing,
        onRefresh = onRefresh,
        modifier = modifier
    ) {
        LazyColumn(Modifier.fillMaxSize()) {
            items(items) {
                ListItem({ Text(text = it) })
            }
        }
    }
}

Points clés concernant le code

  • PullToRefreshBox encapsule un LazyColumn, qui affiche une liste de chaînes.
  • PullToRefreshBox nécessite les paramètres isRefreshing et onRefresh.
  • Le contenu du bloc PullToRefreshBox représente le contenu défilant.

Résultat

Cette vidéo illustre l'implémentation de base de la fonctionnalité "Pull to refresh" à partir du code précédent :

Figure 1 : Implémentation de base de la fonctionnalité "Pull to refresh" sur une liste d'éléments.

Exemple avancé : personnaliser la couleur de l'indicateur

@Composable
fun PullToRefreshCustomStyleSample(
    items: List<String>,
    isRefreshing: Boolean,
    onRefresh: () -> Unit,
    modifier: Modifier = Modifier
) {
    val state = rememberPullToRefreshState()

    PullToRefreshBox(
        isRefreshing = isRefreshing,
        onRefresh = onRefresh,
        modifier = modifier,
        state = state,
        indicator = {
            Indicator(
                modifier = Modifier.align(Alignment.TopCenter),
                isRefreshing = isRefreshing,
                containerColor = MaterialTheme.colorScheme.primaryContainer,
                color = MaterialTheme.colorScheme.onPrimaryContainer,
                state = state
            )
        },
    ) {
        LazyColumn(Modifier.fillMaxSize()) {
            items(items) {
                ListItem({ Text(text = it) })
            }
        }
    }
}

Points clés concernant le code

  • La couleur de l'indicateur est personnalisée via les propriétés containerColor et color du paramètre indicator.
  • rememberPullToRefreshState() gère l'état de l'action d'actualisation. Vous utilisez cet état conjointement avec le paramètre indicator.

Résultat

Cette vidéo montre une implémentation de la fonctionnalité "Pull to refresh" avec un indicateur coloré :

Figure 2 : Implémentation de la fonctionnalité "Pull to refresh" avec un style personnalisé.

Exemple avancé : créer un indicateur entièrement personnalisé

Vous pouvez créer des indicateurs personnalisés complexes en tirant parti des composables et des animations existants.Cet extrait montre comment créer un indicateur entièrement personnalisé dans votre implémentation de la fonctionnalité "Pull to refresh" :

@Composable
fun PullToRefreshCustomIndicatorSample(
    items: List<String>,
    isRefreshing: Boolean,
    onRefresh: () -> Unit,
    modifier: Modifier = Modifier
) {
    val state = rememberPullToRefreshState()

    PullToRefreshBox(
        isRefreshing = isRefreshing,
        onRefresh = onRefresh,
        modifier = modifier,
        state = state,
        indicator = {
            MyCustomIndicator(
                state = state,
                isRefreshing = isRefreshing,
                modifier = Modifier.align(Alignment.TopCenter)
            )
        }
    ) {
        LazyColumn(Modifier.fillMaxSize()) {
            items(items) {
                ListItem({ Text(text = it) })
            }
        }
    }
}

// ...
@Composable
fun MyCustomIndicator(
    state: PullToRefreshState,
    isRefreshing: Boolean,
    modifier: Modifier = Modifier,
) {
    Box(
        modifier = modifier.pullToRefresh(
            state = state,
            isRefreshing = isRefreshing,
            threshold = PositionalThreshold,
            onRefresh = {

            }
        ),
        contentAlignment = Alignment.Center
    ) {
        Crossfade(
            targetState = isRefreshing,
            animationSpec = tween(durationMillis = CROSSFADE_DURATION_MILLIS),
            modifier = Modifier.align(Alignment.Center)
        ) { refreshing ->
            if (refreshing) {
                CircularProgressIndicator(Modifier.size(SPINNER_SIZE))
            } else {
                val distanceFraction = { state.distanceFraction.coerceIn(0f, 1f) }
                Icon(
                    imageVector = Icons.Filled.CloudDownload,
                    contentDescription = "Refresh",
                    modifier = Modifier
                        .size(18.dp)
                        .graphicsLayer {
                            val progress = distanceFraction()
                            this.alpha = progress
                            this.scaleX = progress
                            this.scaleY = progress
                        }
                )
            }
        }
    }
}

Points clés concernant le code

  • L'extrait précédent utilisait l'Indicator fourni par la bibliothèque. Cet extrait crée un composable d'indicateur personnalisé appelé MyCustomIndicator. Dans ce composable, le modificateur pullToRefreshIndicator gère le positionnement et le déclenchement d'une actualisation.
  • Comme dans l'extrait précédent, l'exemple extrait l'instance PullToRefreshState. Vous pouvez ainsi transmettre la même instance à la fois à PullToRefreshBox et à pullToRefreshModifier.
  • L'exemple utilise la couleur du conteneur et le seuil de position de la classe PullToRefreshDefaults. Vous pouvez ainsi réutiliser le comportement et le style par défaut de la bibliothèque Material, tout en personnalisant uniquement les éléments qui vous intéressent.
  • MyCustomIndicator utilise Crossfade pour effectuer une transition entre une icône de cloud et un CircularProgressIndicator. L'icône de cloud s'agrandit lorsque l'utilisateur tire, puis passe à un CircularProgressIndicator lorsque l'action d'actualisation commence.
    • targetState utilise isRefreshing pour déterminer l'état à afficher (l'icône de cloud ou l'indicateur de progression circulaire).
    • animationSpec définit une animation tween pour la transition, avec une durée spécifiée de CROSSFADE_DURATION_MILLIS.
    • state.distanceFraction représente la distance parcourue par l'utilisateur, allant de 0f (aucun tirage) à 1f (tirage complet).
    • Le modificateur graphicsLayer modifie l'échelle et la transparence.

Résultat

Cette vidéo montre l'indicateur personnalisé du code précédent :

Figure 3 : Implémentation de la fonctionnalité "Pull to refresh" avec un indicateur personnalisé.

Ressources supplémentaires