Pager dans Compose

Pour faire défiler le contenu à gauche et à droite ou de haut en bas, vous pouvez utiliser la HorizontalPager et VerticalPager et les composables, respectivement. Ces composables ont des fonctions similaires à ViewPager dans la vue du système d'exploitation. Par défaut, HorizontalPager occupe toute la largeur de l'écran. VerticalPager occupe toute la hauteur, et les bipeurs ne font qu'une page à la fois en temps réel. Ces valeurs par défaut sont toutes configurables.

HorizontalPager

Pour créer un pager qui défile horizontalement vers la gauche et vers la droite, utilisez HorizontalPager:

Figure 1 : Démonstration de HorizontalPager

// Display 10 items
val pagerState = rememberPagerState(pageCount = {
    10
})
HorizontalPager(state = pagerState) { page ->
    // Our page content
    Text(
        text = "Page: $page",
        modifier = Modifier.fillMaxWidth()
    )
}

VerticalPager

Pour créer un pager qui défile vers le haut et vers le bas, utilisez VerticalPager:

Figure 2 : Démonstration de VerticalPager

// Display 10 items
val pagerState = rememberPagerState(pageCount = {
    10
})
VerticalPager(state = pagerState) { page ->
    // Our page content
    Text(
        text = "Page: $page",
        modifier = Modifier.fillMaxWidth()
    )
}

Création différée

Les pages dans HorizontalPager et VerticalPager sont présentées de manière différée et disposés et disposés selon les besoins. En tant qu'utilisateur fait défiler les pages, le composable supprime toutes les pages qui ne sont plus obligatoire.

Charger plus de pages hors écran

Par défaut, le bipeur ne charge que les pages visibles à l'écran. Pour charger plus de pages hors écran, définissez beyondBoundsPageCount sur une valeur supérieure à zéro.

Faire défiler la page jusqu'à un élément

Pour accéder à une page en particulier, créez une PagerState à l'aide de rememberPagerState() et le transmettre au bipeur en tant que paramètre state. Vous pouvez appeler PagerState#scrollToPage() sur cet état, dans un élément CoroutineScope:

val pagerState = rememberPagerState(pageCount = {
    10
})
HorizontalPager(state = pagerState) { page ->
    // Our page content
    Text(
        text = "Page: $page",
        modifier = Modifier
            .fillMaxWidth()
            .height(100.dp)
    )
}

// scroll to page
val coroutineScope = rememberCoroutineScope()
Button(onClick = {
    coroutineScope.launch {
        // Call scroll to on pagerState
        pagerState.scrollToPage(5)
    }
}, modifier = Modifier.align(Alignment.BottomCenter)) {
    Text("Jump to Page 5")
}

Pour créer une animation au niveau de la page, utilisez la méthode PagerState#animateScrollToPage() :

val pagerState = rememberPagerState(pageCount = {
    10
})

HorizontalPager(state = pagerState) { page ->
    // Our page content
    Text(
        text = "Page: $page",
        modifier = Modifier
            .fillMaxWidth()
            .height(100.dp)
    )
}

// scroll to page
val coroutineScope = rememberCoroutineScope()
Button(onClick = {
    coroutineScope.launch {
        // Call scroll to on pagerState
        pagerState.animateScrollToPage(5)
    }
}, modifier = Modifier.align(Alignment.BottomCenter)) {
    Text("Jump to Page 5")
}

Recevoir une notification en cas de changement d'état d'une page

PagerState comporte trois propriétés contenant des informations sur les pages: currentPage, settledPage et targetPage.

  • currentPage: page la plus proche de la position d'ancrage. Par défaut, l'ancrage se trouve au début de la mise en page.
  • settledPage: numéro de page lorsqu'aucune animation ni aucun défilement n'est exécuté. Ce est différente de la propriété currentPage, car currentPage est immédiatement mis à jour si la page est suffisamment proche de la position d'ancrage, mais settledPage reste inchangé jusqu'à ce que toutes les animations aient terminé.
  • targetPage: position d'arrêt proposée pour un mouvement de défilement.

Vous pouvez utiliser la fonction snapshotFlow pour observer les modifications apportées à ces variables et d'y réagir. Par exemple, pour envoyer un événement d'analyse à chaque modification de page, vous pouvez effectuer les opérations suivantes:

val pagerState = rememberPagerState(pageCount = {
    10
})

LaunchedEffect(pagerState) {
    // Collect from the a snapshotFlow reading the currentPage
    snapshotFlow { pagerState.currentPage }.collect { page ->
        // Do something with each page change, for example:
        // viewModel.sendPageSelectedEvent(page)
        Log.d("Page change", "Page changed to $page")
    }
}

VerticalPager(
    state = pagerState,
) { page ->
    Text(text = "Page: $page")
}

Ajouter un indicateur de page

Pour ajouter un indicateur à une page, utilisez l'objet PagerState afin d'obtenir des informations. la page sélectionnée parmi celles qui sont disponibles, puis tracez votre indicateur.

Par exemple, si vous voulez un indicateur de cercle simple, vous pouvez répéter le nombre de cercles et changer la couleur du cercle selon que la page est sélectionnée, en utilisant pagerState.currentPage:

val pagerState = rememberPagerState(pageCount = {
    4
})
HorizontalPager(
    state = pagerState,
    modifier = Modifier.fillMaxSize()
) { page ->
    // Our page content
    Text(
        text = "Page: $page",
    )
}
Row(
    Modifier
        .wrapContentHeight()
        .fillMaxWidth()
        .align(Alignment.BottomCenter)
        .padding(bottom = 8.dp),
    horizontalArrangement = Arrangement.Center
) {
    repeat(pagerState.pageCount) { iteration ->
        val color = if (pagerState.currentPage == iteration) Color.DarkGray else Color.LightGray
        Box(
            modifier = Modifier
                .padding(2.dp)
                .clip(CircleShape)
                .background(color)
                .size(16.dp)
        )
    }
}

Bipeur affichant un indicateur de cercle sous le contenu
Figure 3. Bipeur affichant un indicateur de cercle sous le contenu

Appliquer des effets de défilement au contenu

Un cas d'utilisation courant consiste à utiliser la position de défilement pour appliquer des effets à votre pager. éléments. Pour connaître la distance qui sépare une page de la page actuellement sélectionnée, vous pouvez utiliser PagerState.currentPageOffsetFraction Vous pouvez ensuite appliquer des effets de transformation à votre contenu en fonction de la distance de la page sélectionnée.

Figure 4 : Appliquer des transformations au contenu du pager

Par exemple, pour ajuster l'opacité des éléments en fonction de leur distance par rapport à la centrez, modifiez alpha à l'aide de Modifier.graphicsLayer sur un élément du pager:

val pagerState = rememberPagerState(pageCount = {
    4
})
HorizontalPager(state = pagerState) { page ->
    Card(
        Modifier
            .size(200.dp)
            .graphicsLayer {
                // Calculate the absolute offset for the current page from the
                // scroll position. We use the absolute value which allows us to mirror
                // any effects for both directions
                val pageOffset = (
                    (pagerState.currentPage - page) + pagerState
                        .currentPageOffsetFraction
                    ).absoluteValue

                // We animate the alpha, between 50% and 100%
                alpha = lerp(
                    start = 0.5f,
                    stop = 1f,
                    fraction = 1f - pageOffset.coerceIn(0f, 1f)
                )
            }
    ) {
        // Card content
    }
}

Tailles de page personnalisées

Par défaut, HorizontalPager et VerticalPager occupent toute la largeur. à pleine hauteur, respectivement. Vous pouvez définir la variable pageSize pour qu'elle ait une valeur Fixed, Fill (par défaut) ou un calcul de taille personnalisé.

Par exemple, pour définir une page 100.dp à largeur fixe:

val pagerState = rememberPagerState(pageCount = {
    4
})
HorizontalPager(
    state = pagerState,
    pageSize = PageSize.Fixed(100.dp)
) { page ->
    // page content
}

Pour dimensionner les pages en fonction de la taille de la fenêtre d'affichage, utilisez une taille de page personnalisée. calcul. Créer un profil personnalisé PageSize et diviser la valeur availableSpace par trois, en tenant compte de l'espacement entre les éléments:

private val threePagesPerViewport = object : PageSize {
    override fun Density.calculateMainAxisPageSize(
        availableSpace: Int,
        pageSpacing: Int
    ): Int {
        return (availableSpace - 2 * pageSpacing) / 3
    }
}

Marge intérieure du contenu

HorizontalPager et VerticalPager acceptent tous deux la modification de la marge intérieure du contenu. ce qui vous permet d'influencer la taille et l'alignement maximales des pages.

Par exemple, la définition de la marge intérieure start aligne les pages vers la fin:

Pager avec une marge intérieure de début affichant le contenu aligné vers la fin

val pagerState = rememberPagerState(pageCount = {
    4
})
HorizontalPager(
    state = pagerState,
    contentPadding = PaddingValues(start = 64.dp),
) { page ->
    // page content
}

Définir les marges intérieures start et end sur la même valeur pour centrer l'élément horizontalement:

Pager avec une marge intérieure de début et de fin montrant le contenu centré

val pagerState = rememberPagerState(pageCount = {
    4
})
HorizontalPager(
    state = pagerState,
    contentPadding = PaddingValues(horizontal = 32.dp),
) { page ->
    // page content
}

La définition de la marge intérieure end aligne les pages par rapport au début:

Pager avec une marge intérieure de début et de fin indiquant que le contenu est aligné par rapport au début

val pagerState = rememberPagerState(pageCount = {
    4
})
HorizontalPager(
    state = pagerState,
    contentPadding = PaddingValues(end = 64.dp),
) { page ->
    // page content
}

Vous pouvez définir les valeurs top et bottom pour obtenir des effets similaires pour VerticalPager La valeur 32.dp n'est utilisée qu'à titre d'exemple. vous pouvez définir chacune des dimensions de marge intérieure sur n'importe quelle valeur.

Personnaliser le comportement de défilement

Les composables HorizontalPager et VerticalPager par défaut spécifient comment les gestes de défilement fonctionnent avec le pager. Vous pouvez toutefois personnaliser et modifier les valeurs par défaut, comme pagerSnapDistance ou flingBehavior.

Distance du cliché

Par défaut, HorizontalPager et VerticalPager définissent le nombre maximal de pages sur lesquelles un geste vif peut faire défiler l'écran jusqu'à une page à la fois. Pour modifier ça, définir pagerSnapDistance sur le flingBehavior:

val pagerState = rememberPagerState(pageCount = { 10 })

val fling = PagerDefaults.flingBehavior(
    state = pagerState,
    pagerSnapDistance = PagerSnapDistance.atMost(10)
)

Column(modifier = Modifier.fillMaxSize()) {
    HorizontalPager(
        state = pagerState,
        pageSize = PageSize.Fixed(200.dp),
        beyondBoundsPageCount = 10,
        flingBehavior = fling
    ) {
        PagerSampleItem(page = it)
    }
}