Per scorrere i contenuti in orizzontale o in verticale, puoi utilizzare i componibili HorizontalPager e VerticalPager. Questi hanno funzioni simili a ViewPager nel sistema di visualizzazione. Per impostazione predefinita, HorizontalPager occupa l'intera larghezza dello schermo e VerticalPager occupa l'intera altezza. Inoltre, i pager scorrono solo una pagina alla volta. Tutte queste impostazioni predefinite sono configurabili.
HorizontalPager
Per creare un pager che scorre orizzontalmente verso sinistra e verso destra, utilizza
HorizontalPager:
HorizontalPager
// Display 10 items val pagerState = rememberPagerState(pageCount = { 10 }) HorizontalPager(state = pagerState) { page -> // Our page content Text( text = "Page: $page", modifier = Modifier.fillMaxWidth() ) }
VerticalPager
Per creare un pager che scorre verso l'alto e verso il basso, utilizza VerticalPager:
VerticalPager
// Display 10 items val pagerState = rememberPagerState(pageCount = { 10 }) VerticalPager(state = pagerState) { page -> // Our page content Text( text = "Page: $page", modifier = Modifier.fillMaxWidth() ) }
Creazione lazy
Le pagine sia in HorizontalPager che in VerticalPager vengono composte in modo lazy e disposte quando necessario. Man mano che l'utente scorre le pagine, il composable rimuove quelle che non sono più necessarie.
Carica più pagine fuori schermo
Per impostazione predefinita, il pager carica solo le pagine visibili sullo schermo. Per caricare più pagine fuori schermo, imposta beyondBoundsPageCount su un valore maggiore di zero.
Scorri fino a un elemento nel pager
Per scorrere fino a una determinata pagina nel pager, crea un oggetto PagerState utilizzando rememberPagerState() e passalo come parametro state al pager. Puoi chiamare PagerState#scrollToPage() in questo stato, all'interno di un
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") }
Se vuoi animare la pagina, utilizza la funzione 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") }
Ricevi notifiche sulle modifiche allo stato della pagina
PagerState ha tre proprietà con informazioni sulle pagine:
currentPage, settledPage e targetPage.
currentPage: la pagina più vicina alla posizione di aggancio. Per impostazione predefinita, la posizione di aggancio è all'inizio del layout.settledPage: il numero di pagina quando non è in esecuzione alcuna animazione o scorrimento. Questa proprietà è diversa dacurrentPagein quantocurrentPagesi aggiorna immediatamente se la pagina è abbastanza vicina alla posizione di aggancio, mentresettledPagerimane invariata fino al termine dell'esecuzione di tutte le animazioni.targetPage: la posizione di arresto proposta per un movimento di scorrimento.
Puoi utilizzare la funzione snapshotFlow per osservare le modifiche a queste variabili e reagire di conseguenza. Ad esempio, per inviare un evento di analisi a ogni cambio di pagina, puoi procedere nel seguente modo:
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") }
Aggiungi un indicatore della pagina
Per aggiungere un indicatore a una pagina, utilizza l'oggetto PagerState per ottenere informazioni su quale pagina è selezionata tra il numero di pagine e disegna l'indicatore personalizzato.
Ad esempio, per creare un indicatore circolare, puoi ripetere il numero di cerchi e cambiare il colore del cerchio in base alla selezione della pagina, utilizzando 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) ) } }
Applica effetti di scorrimento degli elementi ai contenuti
Un caso d'uso comune è l'utilizzo della posizione di scorrimento per applicare effetti agli elementi del pager. Per scoprire quanto è distante una pagina da quella selezionata, puoi utilizzare
PagerState.currentPageOffsetFraction. Puoi quindi applicare effetti di trasformazione ai tuoi contenuti in base alla distanza dalla pagina selezionata.
Ad esempio, per regolare l'opacità degli elementi in base alla loro distanza dal centro, modifica alpha utilizzando Modifier.graphicsLayer su un elemento all'interno del 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 } }
Dimensioni di pagina personalizzate
Per impostazione predefinita, HorizontalPager e VerticalPager occupano rispettivamente l'intera larghezza o l'intera altezza. Puoi impostare la variabile pageSize in modo che abbia un valore Fixed, Fill (predefinito) o un calcolo delle dimensioni personalizzato.
Ad esempio, per impostare una pagina con larghezza fissa di 100.dp:
val pagerState = rememberPagerState(pageCount = { 4 }) HorizontalPager( state = pagerState, pageSize = PageSize.Fixed(100.dp) ) { page -> // page content }
Per dimensionare le pagine in base alle dimensioni dell'area visibile, utilizza un calcolo personalizzato delle dimensioni della pagina. Crea un oggetto PageSize personalizzato e dividi availableSpace per tre, tenendo conto della spaziatura tra gli elementi:
private val threePagesPerViewport = object : PageSize { override fun Density.calculateMainAxisPageSize( availableSpace: Int, pageSpacing: Int ): Int { return (availableSpace - 2 * pageSpacing) / 3 } }
Spaziatura interna dei contenuti
HorizontalPager e VerticalPager supportano entrambe la modifica del padding dei contenuti,
che ti consente di influenzare le dimensioni massime e l'allineamento delle pagine.
Ad esempio, l'impostazione della spaziatura interna start allinea le pagine verso la fine:
val pagerState = rememberPagerState(pageCount = { 4 }) HorizontalPager( state = pagerState, contentPadding = PaddingValues(start = 64.dp), ) { page -> // page content }
Se imposti lo stesso valore per il padding start e end, l'elemento viene centrato orizzontalmente:
val pagerState = rememberPagerState(pageCount = { 4 }) HorizontalPager( state = pagerState, contentPadding = PaddingValues(horizontal = 32.dp), ) { page -> // page content }
L'impostazione del padding end allinea le pagine verso l'inizio:
val pagerState = rememberPagerState(pageCount = { 4 }) HorizontalPager( state = pagerState, contentPadding = PaddingValues(end = 64.dp), ) { page -> // page content }
Puoi impostare i valori top e bottom per ottenere effetti simili per
VerticalPager. Il valore 32.dp viene utilizzato qui solo come esempio; puoi impostare
ciascuna delle dimensioni di spaziatura interna su qualsiasi valore.
Personalizza il comportamento di scorrimento
I componibili predefiniti HorizontalPager e VerticalPager specificano come i gesti di scorrimento funzionano con il pager. Tuttavia, puoi personalizzare e modificare le impostazioni predefinite, come pagerSnapDistance o flingBehavior.
Distanza di aggancio
Per impostazione predefinita, HorizontalPager e VerticalPager impostano il numero massimo di pagine che un gesto di scorrimento rapido può scorrere su una pagina alla volta. Per modificare questa impostazione, imposta pagerSnapDistance su 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), beyondViewportPageCount = 10, flingBehavior = fling ) { PagerSampleItem(page = it) } }
Crea un pager con avanzamento automatico
Questa sezione descrive come creare un pager con avanzamento automatico con indicatori di pagina in Compose. La raccolta di elementi scorre automaticamente in orizzontale, ma gli utenti possono anche scorrere manualmente tra gli elementi. Se un utente interagisce con il pager, la progressione automatica si interrompe.
Esempio di base
Insieme, i seguenti snippet creano un'implementazione di base di un pager con avanzamento automatico con un indicatore visivo, in cui ogni pagina viene visualizzata con un colore diverso:
@Composable fun AutoAdvancePager(pageItems: List<Color>, modifier: Modifier = Modifier) { Box(modifier = Modifier.fillMaxSize()) { val pagerState = rememberPagerState(pageCount = { pageItems.size }) val pagerIsDragged by pagerState.interactionSource.collectIsDraggedAsState() val pageInteractionSource = remember { MutableInteractionSource() } val pageIsPressed by pageInteractionSource.collectIsPressedAsState() // Stop auto-advancing when pager is dragged or one of the pages is pressed val autoAdvance = !pagerIsDragged && !pageIsPressed if (autoAdvance) { LaunchedEffect(pagerState, pageInteractionSource) { while (true) { delay(2000) val nextPage = (pagerState.currentPage + 1) % pageItems.size pagerState.animateScrollToPage(nextPage) } } } HorizontalPager( state = pagerState ) { page -> Text( text = "Page: $page", textAlign = TextAlign.Center, modifier = modifier .fillMaxSize() .background(pageItems[page]) .clickable( interactionSource = pageInteractionSource, indication = LocalIndication.current ) { // Handle page click } .wrapContentSize(align = Alignment.Center) ) } PagerIndicator(pageItems.size, pagerState.currentPage) } }
Punti chiave sul codice
- La funzione
AutoAdvancePagercrea una visualizzazione con scorrimento orizzontale e avanzamento automatico. Accetta come input un elenco di oggettiColor, che vengono utilizzati come colori di sfondo per ogni pagina. pagerStateviene creato utilizzandorememberPagerState, che contiene lo stato del pager.pagerIsDraggedepageIsPressedmonitorano l'interazione dell'utente.LaunchedEffectfa avanzare automaticamente il pager ogni due secondi, a meno che l'utente non lo trascini o prema una delle pagine.HorizontalPagermostra un elenco di pagine, ciascuna con un componibileTextche mostra il numero di pagina. Il modificatore riempie la pagina, imposta il colore di sfondo dapageItemse rende la pagina cliccabile.
@Composable fun PagerIndicator(pageCount: Int, currentPageIndex: Int, modifier: Modifier = Modifier) { Box(modifier = Modifier.fillMaxSize()) { Row( modifier = Modifier .wrapContentHeight() .fillMaxWidth() .align(Alignment.BottomCenter) .padding(bottom = 8.dp), horizontalArrangement = Arrangement.Center ) { repeat(pageCount) { iteration -> val color = if (currentPageIndex == iteration) Color.DarkGray else Color.LightGray Box( modifier = modifier .padding(2.dp) .clip(CircleShape) .background(color) .size(16.dp) ) } } } }
Punti chiave sul codice
- Un componibile
Boxfunge da elemento root e contiene unRowper disporre gli indicatori di pagina in orizzontale. - Un indicatore di pagina personalizzato viene visualizzato come una riga di cerchi, in cui ogni
Boxritagliato in unCircleShaperappresenta una pagina. - Il cerchio della pagina corrente è colorato come
DarkGray, mentre gli altri cerchi sonoLightGray. Il parametrocurrentPageIndexdetermina quale cerchio viene visualizzato in grigio scuro.
Risultato
Questo video mostra il pager di base con avanzamento automatico degli snippet precedenti: