Cercapersone in Scrivi

Per scorrere i contenuti in modo da sinistra e destra oppure verso l'alto e verso il basso, puoi utilizzare rispettivamente gli elementi componibili HorizontalPager e VerticalPager. Questi elementi componibili hanno funzioni simili a ViewPager nel sistema di visualizzazione. Per impostazione predefinita, HorizontalPager occupa l'intera larghezza dello schermo, VerticalPager occupa l'intera altezza e i cercapersone passano solo a una pagina alla volta. Questi valori predefiniti sono tutti configurabili.

HorizontalPager

Per creare un pager che scorre orizzontalmente verso sinistra e destra, utilizza 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 pager con scorrimento 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 vengono composte pigramente e disposte in modo da essere strutturate quando necessario. Mentre l'utente scorre le pagine, il componibile rimuove tutte le pagine che non sono più obbligatorie.

Carica altre 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 al pager come parametro state. 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")
}

Ricevere notifiche sulle modifiche allo 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 agganciamento è all'inizio del layout.
  • settledPage: il numero della pagina quando non è in corso alcuna animazione o scorrimento. Questa è diversa dalla proprietà currentPage in quanto currentPage si aggiorna immediatamente se la pagina è abbastanza vicina alla posizione di agganciamento, mentre settledPage rimane uguale fino a quando l'esecuzione di tutte le animazioni non è completata.
  • targetPage: la posizione di interruzione proposta per un movimento di scorrimento.

Puoi utilizzare la funzione snapshotFlow per osservare le modifiche apportate a queste variabili e reagire a queste ultime. Ad esempio, per inviare un evento di Analytics a ogni modifica di pagina, puoi:

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 su quale pagina è selezionata dal numero di pagine e traccia l'indicatore personalizzato.

Ad esempio, se vuoi un indicatore circolare semplice, puoi ripetere il numero di cerchi e modificare 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)
        )
    }
}

Cercapersone che mostra un indicatore circolare sotto i contenuti
Figura 3. Cercapersone che mostra un indicatore circolare sotto i contenuti

Applicare effetti di scorrimento degli elementi ai contenuti

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

Figura 4. Applicazione delle trasformazioni ai contenuti dei cercapersone

Ad esempio, per regolare l'opacità degli elementi in base a quanto sono distanti 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 utilizzano rispettivamente l'intera larghezza o l'altezza completa. Puoi impostare la variabile pageSize in modo che abbia Fixed, Fill (valore predefinito) o un calcolo delle dimensioni personalizzate.

Ad esempio, per impostare una pagina a larghezza fissa di 100.dp:

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

Per ridimensionare le pagine in base a quelle dell'area visibile, usa un calcolo delle dimensioni delle pagine personalizzate. 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 entrambi la modifica della spaziatura interna dei contenuti, il che ti consente di influire sulle dimensioni e sull'allineamento massimi delle pagine.

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

Cercapersone con spaziatura interna 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
}

Se imposti lo stesso valore per la spaziatura interna start e end, l'elemento viene centrato orizzontalmente:

Cercapersone con spaziatura interna iniziale e finale che mostra i contenuti centrati

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

Se imposti la spaziatura interna end, le pagine vengono allineate verso l'inizio:

Cercapersone con spaziatura interna 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 ogni dimensione di spaziatura interna su qualsiasi valore.

Personalizza il comportamento di scorrimento

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

Distanza di scatto

Per impostazione predefinita, HorizontalPager e VerticalPager impostano il numero massimo di pagine su cui è possibile scorrere un gesto di scorrimento fino a 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),
        beyondBoundsPageCount = 10,
        flingBehavior = fling
    ) {
        PagerSampleItem(page = it)
    }
}