Pager in der Funktion „Schreiben“

Mit den Composables HorizontalPager und VerticalPager kannst du Inhalte nach links und rechts oder nach oben und unten durchblättern. Diese Elemente haben ähnliche Funktionen wie ViewPager im Ansichtssystem. Standardmäßig nimmt das HorizontalPager die volle Breite des Bildschirms ein, VerticalPager die volle Höhe. Die Seitenblätter springen jeweils nur eine Seite weiter. Diese Standardeinstellungen sind alle konfigurierbar.

HorizontalPager

Mit HorizontalPager können Sie einen Bildlauf erstellen, der horizontal nach links und rechts scrollt:

Abbildung 1. Demo von HorizontalPager

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

VerticalPager

Verwenden Sie VerticalPager, um einen Bildlauf, der nach oben und unten scrollt, zu erstellen:

Abbildung 2. Demo von VerticalPager

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

Lazy Creation

Seiten in HorizontalPager und VerticalPager werden lazily komponiert und bei Bedarf layoutet. Wenn der Nutzer durch die Seiten scrollt, werden alle Seiten entfernt, die nicht mehr erforderlich sind.

Mehr Seiten im Hintergrund laden

Standardmäßig werden mit dem Bildlauf nur die sichtbaren Seiten auf dem Bildschirm geladen. Wenn Sie mehr Seiten im Hintergrund laden möchten, setzen Sie beyondBoundsPageCount auf einen Wert größer als null.

In der Bildlaufleiste zu einem Element scrollen

Wenn Sie zum Scrollen zu einer bestimmten Seite im Paginierungselement ein PagerState-Objekt erstellen möchten, verwenden Sie rememberPagerState() und übergeben Sie es als state-Parameter an das Paginierungselement. Sie können PagerState#scrollToPage() in diesem Status innerhalb eines CoroutineScope aufrufen:

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

Wenn Sie die Seite animieren möchten, verwenden Sie die Funktion 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")
}

Benachrichtigungen bei Änderungen des Seitenstatus erhalten

PagerState hat drei Properties mit Informationen zu Seiten: currentPage, settledPage und targetPage.

  • currentPage: Die Seite, die der Anlegeposition am nächsten ist. Standardmäßig befindet sich die Anlegeposition am Anfang des Layouts.
  • settledPage: Die Seitennummer, wenn keine Animation oder kein Scrollen ausgeführt wird. Das unterscheidet sich von der Eigenschaft currentPage, da currentPage sofort aktualisiert wird, wenn sich die Seite nahe genug an der Anlegeposition befindet. settledPage bleibt jedoch unverändert, bis alle Animationen abgeschlossen sind.
  • targetPage: Die vorgeschlagene Endposition für eine Scrollbewegung.

Mit der Funktion snapshotFlow können Sie Änderungen an diesen Variablen beobachten und darauf reagieren. Wenn Sie beispielsweise bei jeder Seitenänderung ein Analytics-Ereignis senden möchten, gehen Sie so vor:

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

Seitenindikator hinzufügen

Wenn Sie einer Seite einen Indikator hinzufügen möchten, verwenden Sie das PagerState-Objekt, um Informationen dazu zu erhalten, welche Seite aus der Anzahl der Seiten ausgewählt ist, und zeichnen Sie Ihren benutzerdefinierten Indikator.

Wenn Sie beispielsweise einen einfachen Kreisindikator benötigen, können Sie die Anzahl der Kreise wiederholen und die Kreisfarbe mithilfe von pagerState.currentPage ändern, je nachdem, ob die Seite ausgewählt ist:

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

Ein Bildlauf-Widget mit einem Kreissymbol unter dem Inhalt
Abbildung 3: Ein Vorlesegerät mit einem Kreissymbol unter dem Inhalt

Scrolleffekte für Elemente auf Inhalte anwenden

Ein häufiger Anwendungsfall ist die Verwendung der Scrollposition, um Effekte auf die Elemente des Bildlaufs anzuwenden. Mit PagerState.currentPageOffsetFraction können Sie herausfinden, wie weit eine Seite von der aktuell ausgewählten entfernt ist. Sie können dann Transformationseffekte auf Ihre Inhalte anwenden, die sich an der Entfernung von der ausgewählten Seite orientieren.

Abbildung 4: Transformationen auf den Inhalt des Seitenschiebers anwenden

Wenn Sie beispielsweise die Deckkraft von Elementen anhand ihrer Entfernung vom Mittelpunkt anpassen möchten, ändern Sie den alpha mit Modifier.graphicsLayer für ein Element im 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
    }
}

Benutzerdefinierte Seitenformate

Standardmäßig nehmen HorizontalPager und VerticalPager jeweils die volle Breite oder Höhe ein. Sie können die Variable pageSize entweder auf Fixed, Fill (Standard) oder eine benutzerdefinierte Größenberechnung festlegen.

So legen Sie beispielsweise eine Seite mit einer festen Breite von 100.dp fest:

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

Wenn Sie die Seitengröße anhand der Darstellungsgröße festlegen möchten, verwenden Sie eine benutzerdefinierte Berechnung der Seitengröße. Erstellen Sie ein benutzerdefiniertes PageSize-Objekt und teilen Sie die availableSpace durch drei, wobei Sie den Abstand zwischen den Elementen berücksichtigen:

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

Textabstand

Sowohl HorizontalPager als auch VerticalPager unterstützen das Ändern des Inhaltsabstands, mit dem Sie die maximale Größe und Ausrichtung von Seiten beeinflussen können.

Wenn Sie beispielsweise das start-Abstand festlegen, werden die Seiten am Ende ausgerichtet:

Seitenleiste mit Anfangsabstand, bei der die Inhalte nach unten ausgerichtet sind

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

Wenn Sie sowohl für den start- als auch für den end-Abstand denselben Wert festlegen, wird das Element horizontal zentriert:

Seitenleiste mit Anfangs- und Endabstand, die den Inhalt zentriert anzeigen

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

Wenn Sie den end-Abstand festlegen, werden die Seiten am Anfang ausgerichtet:

Seitenleiste mit Anfangs- und Endabstand, die den Inhalt am Anfang ausrichtet

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

Sie können die Werte für top und bottom festlegen, um ähnliche Effekte für VerticalPager zu erzielen. Der Wert 32.dp wird hier nur als Beispiel verwendet. Sie können jede der Abstände auf einen beliebigen Wert festlegen.

Scrollverhalten anpassen

Die Standard-Composables HorizontalPager und VerticalPager geben an, wie Scrollgesten mit dem Pager funktionieren. Sie können die Standardeinstellungen wie pagerSnapDistance oder flingBehavior jedoch anpassen und ändern.

Snap-Distanz

Standardmäßig legen HorizontalPager und VerticalPager die maximale Anzahl von Seiten fest, die mit einer Wischgeste jeweils übersprungen werden können. Wenn Sie das ändern möchten, legen Sie auf der flingBehavior die Option pagerSnapDistance fest:

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