Pager w sekcji Utwórz

Aby przewracać treści w lewo i prawo lub w górę i dół, możesz użyć odpowiednio komponentów HorizontalPager i VerticalPager. Te komponenty mają podobne funkcje do komponentów ViewPager w systemie widoków. Domyślnie HorizontalPager zajmuje całą szerokość ekranu, VerticalPager zajmuje całą wysokość, a przeglądarka przenosi tylko jedną stronę naraz. Wszystkie te ustawienia domyślne można konfigurować.

HorizontalPager

Aby utworzyć stronę z przewijaniem poziomym w kierunku lewo-prawo, użyj:HorizontalPager

Rysunek 1. Demo HorizontalPager

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

VerticalPager

Aby utworzyć stronę przewijaną w górę i w dół, użyj VerticalPager:

Rysunek 2. Demo VerticalPager

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

Tworzenie bezmyślne

Strony w HorizontalPagerVerticalPagerkomponowane i układane w miarę potrzeby. Gdy użytkownik przewija strony, kompozyt usunął wszystkie strony, które nie są już potrzebne.

Wczytywanie większej liczby stron poza ekranem

Domyślnie przewijanie wczytuje tylko widoczne strony na ekranie. Aby wczytywać więcej stron poza ekranem, ustaw wartość parametru beyondBoundsPageCount na większą niż zero.

Przewijanie do elementu w pagerze

Aby przewinąć do określonej strony w pagerze, utwórz obiekt PagerState za pomocą obiektu rememberPagerState() i przekaż go jako parametr state do pager. W tym stanie możesz zadzwonić doPagerState#scrollToPage()w ramach 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")
}

Jeśli chcesz użyć animacji na stronie, użyj funkcji 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")
}

Otrzymywanie powiadomień o zmianach stanu strony

PagerState ma 3 właściwości z informacjami o stronach: currentPage, settledPage i targetPage.

  • currentPage: strona najbliższa pozycji przyciągania. Domyślnie pozycja przyciągania znajduje się na początku układu.
  • settledPage: numer strony, gdy nie działa żadna animacja ani nie jest wykonywane przewijanie. Różnica między właściwością currentPage a  polega na tym, że currentPage aktualizuje się natychmiast, gdy strona jest wystarczająco blisko pozycji przyciągania, ale settledPage pozostaje taka sama, dopóki nie zakończą się wszystkie animacje.
  • targetPage: proponowana pozycja zatrzymania podczas przewijania.

Za pomocą funkcji snapshotFlow możesz obserwować zmiany tych zmiennych i na nie reagować. Aby np. wysyłać zdarzenie Analytics przy każdej zmianie strony, wykonaj te czynności:

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

Dodawanie wskaźnika strony

Aby dodać wskaźnik do strony, użyj obiektu PagerState, aby uzyskać informacje o tym, która strona jest wybrana spośród wszystkich stron, i narysuj niestandardowy wskaźnik.

Jeśli na przykład chcesz użyć prostego wskaźnika w kształcie koła, możesz powtórzyć liczbę kół i zmienić ich kolor w zależności od tego, czy dana strona jest wybrana, za pomocą: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)
        )
    }
}

Strony przewijania z wskaźnikiem w kształcie koła pod treścią
Rysunek 3. Przewijanie pokazujące okrągły wskaźnik pod treścią

Stosowanie efektów przewijania elementów do treści

Typowym zastosowaniem jest użycie pozycji przewijania do zastosowania efektów do elementów strony przewijania. Aby dowiedzieć się, jak daleko jest dana strona od aktualnie wybranej strony, możesz użyć PagerState.currentPageOffsetFraction. Następnie możesz zastosować efekty transformacji do treści na podstawie odległości od wybranej strony.

Rysunek 4. Zmiana treści w układce

Aby na przykład dostosować przezroczystość elementów w zależności od ich odległości od środka, zmień wartość alpha za pomocą Modifier.graphicsLayer w elemencie na stronie:

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

Niestandardowe rozmiary stron

Domyślnie HorizontalPagerVerticalPager zajmują odpowiednio całą szerokość lub całą wysokość. Zmienną pageSize możesz ustawić tak, aby miała wartość Fixed, Fill (domyślnie) lub wartość obliczona na podstawie niestandardowego rozmiaru.

Aby na przykład ustawić stałą szerokość strony 100.dp:

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

Aby dostosować rozmiary stron do rozmiaru widocznego obszaru, użyj niestandardowego obliczenia rozmiaru strony. Utwórz niestandardowy obiekt PageSize i podziel wartość availableSpace przez 3, biorąc pod uwagę odstęp między elementami:

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

wypełnienie treści;

Zarówno HorizontalPager, jak i VerticalPager obsługują zmianę wypełnienia treści, co pozwala wpływać na maksymalny rozmiar i wyrównanie stron.

Na przykład ustawienie wypełnienia start wyrównuje strony do końca:

Stronka z wypełnieniem na początku, na której treść jest wyrównana do końca

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

Ustawienie wartości startend na tę samą wartość spowoduje wyśrodkowanie elementu w poziomie:

Przeglądarka z odstępem na początku i na końcu, pokazująca treść wyśrodkowaną

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

Ustawienie wypełnienia end wyrównuje strony do początku:

Stronka z wypełnieniem na początku i na końcu, na której treść jest wyrównana do lewej

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

Aby uzyskać podobne efekty dla VerticalPager, możesz ustawić wartości top i bottom. Wartość 32.dp jest tu używana tylko do celów poglądowych. Każdy z wymiarów wypełniania możesz ustawić na dowolną wartość.

Dostosowywanie zachowania przewijania

Domyślne komponenty HorizontalPagerVerticalPager określają, jak gesty przewijania działają w przypadku strony. Możesz jednak dostosować i zmienić domyślne wartości, takie jak pagerSnapDistance lub flingBehavior.

Odległość przyciągania

Domyślnie HorizontalPagerVerticalPager określają maksymalną liczbę stron, które gest przesuwania może przewinąć o jedną stronę naraz. Aby to zmienić, ustaw wartość pagerSnapDistance w ustawieniu 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)
    }
}