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 wartości domyślne możesz skonfigurować.

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ć pager, który przewija się w górę i w dół, użyj elementu 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, funkcja kompozycyjna usuwa te, 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 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 analityczne po 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")
}

Dodaj wskaźnik 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 własny 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. Pager z ikoną koła 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 sprawdzić, 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. Zastosowanie przekształceń do treści pagera

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

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ść. Możesz skonfigurować zmienną pageSize tak, aby obliczała rozmiar według rozmiaru niestandardowego Fixed lub Fill (domyślnie).

Aby na przykład ustawić stronę o stałej szerokości 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:

Pager z dopełnieniem początkowym pokazującym treść wyrównaną 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:

Przewijanie z odstępem na początku i na końcu, aby treści były wyśrodkowane

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:

Pager z dopełnieniem początkowym i końcowym przedstawiającym treść wyrównaną do początku

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 została użyta tylko jako przykład. Każdy wymiar dopełnienia 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)
    }
}