Cercapersone in Scrivi

Per scorrere i contenuti a sinistra e a destra o verso l'alto e verso il basso, puoi utilizzare il HorizontalPager e VerticalPager componibili. Questi componibili hanno funzioni simili ViewPager nella vista di un sistema operativo completo. Per impostazione predefinita, HorizontalPager occupa l'intera larghezza dello schermo. VerticalPager occupa tutto l'altezza e i cercapersone scartano solo una pagina alla volta nel tempo. Questi valori predefiniti sono tutti configurabili.

HorizontalPager

Per creare un pager che scorra orizzontalmente a destra e a sinistra, usa HorizontalPager:

Figura 1. Demo di 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 cercapersone che scorra verso l'alto e verso il basso, usa VerticalPager:

Figura 2. Demo di 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 in HorizontalPager e VerticalPager sono pigramente composta e organizzata quando necessario. Come utente scorre le pagine, l'elemento componibile rimuove tutte le pagine che non sono più obbligatorio.

Carica altre pagine fuori schermo

Per impostazione predefinita, il cercapersone carica solo le pagine visibili sullo schermo. Per caricare altre pagine fuori schermo, imposta beyondBoundsPageCount su un valore maggiore di zero.

Scorri fino a un elemento nel selettore di pagine

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 aggiungere un'animazione alla pagina, utilizza il metodo 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 dello stato delle pagine

PagerState ha tre proprietà con informazioni sulle pagine: currentPage, settledPage, e targetPage.

  • currentPage: la pagina più vicina alla posizione di agganciamento. Per impostazione predefinita, la posizione di aggancio è all'inizio del layout.
  • settledPage: il numero di pagina quando non è in esecuzione alcuna animazione o scorrimento. Questo è diverso dalla proprietà currentPage in quanto currentPage si aggiorna immediatamente se la pagina è sufficientemente vicina alla posizione di posizionamento, ma settledPage rimane invariato 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 a queste. Ad esempio, per inviare un evento di analisi a ogni modifica della 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 di pagina

Per aggiungere un indicatore a una pagina, utilizza l'oggetto PagerState per ottenere informazioni sulla pagina selezionata tra il numero di pagine e disegna il tuo indicatore personalizzato.

Ad esempio, se vuoi un semplice indicatore circolare, puoi ripetere il numero i cerchi e cambiarne il colore in base alla selezione della pagina, usando 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)
        )
    }
}

Cercapersone con un indicatore circolare sotto i contenuti
Figura 3. Cercapersone con un indicatore circolare sotto i contenuti

Applica gli effetti di scorrimento degli elementi ai contenuti

Un caso d'uso comune è utilizzare la posizione di scorrimento per applicare effetti al cercapersone elementi. Per scoprire quanto è distante una pagina da quella attualmente selezionata, puoi utilizzare PagerState.currentPageOffsetFraction. Puoi quindi applicare effetti di trasformazione ai contenuti in base alla distanza della pagina selezionata.

Figura 4. Applicazione di trasformazioni ai contenuti Pager

Ad esempio, per regolare l'opacità degli elementi in base alla loro distanza dalla al centro, modifica alpha utilizzando Modifier.graphicsLayer su un elemento all'interno del cercapersone:

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

Formati di pagina personalizzati

Per impostazione predefinita, HorizontalPager e VerticalPager occupano l'intera larghezza o rispettivamente a tutta 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 impostare le dimensioni delle pagine in base alle dimensioni dell'area visibile, utilizza un calcolo personalizzato delle dimensioni delle pagine. Crea un oggetto personalizzato PageSize 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

Sia HorizontalPager sia VerticalPager supportano la modifica del padding dei contenuti, che ti consente di influenzare le dimensioni massime e l'allineamento delle pagine.

Ad esempio, se imposti la spaziatura interna start, le pagine vengono allineate alla fine:

Paginatore con spaziatura iniziale che mostra i contenuti allineati verso la fine

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

L'impostazione della spaziatura interna start e end sullo stesso valore viene centrata sull'elemento orizzontalmente:

Paginatore con spaziatura iniziale e finale che mostra i contenuti al centro

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

L'impostazione della spaziatura interna end consente di allineare le pagine all'inizio:

Paginatore con spaziatura iniziale e finale che mostra i contenuti allineati all'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 solo qui come esempio; puoi impostare ciascuna dimensione di spaziatura interna a qualsiasi valore.

Personalizza comportamento di scorrimento

I componibili HorizontalPager e VerticalPager predefiniti specificano come i gesti di scorrimento funzionano con il cercapersone. Tuttavia, puoi personalizzare e modificare i valori predefiniti, ad esempio pagerSnapDistance o flingBehavior.

Distanza di aggancio

Per impostazione predefinita, HorizontalPager e VerticalPager impostano il numero massimo di pagine scorrevoli che possono essere fatte scorrere fino a una pagina alla volta. Per modificare questo, impostare pagerSnapDistance il 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)
    }
}