Pager w sekcji Utwórz

Aby przechodzić między treściami w lewo i w prawo lub w górę i w dół, możesz używać funkcji kompozycyjnych HorizontalPager i VerticalPager. Te elementy kompozycyjne mają funkcje podobne do ViewPager w systemie widoków. Domyślnie HorizontalPager zajmuje całą szerokość ekranu, VerticalPager zajmuje całą wysokość, a pagery przewijają tylko 1 stronę. Wszystkie te wartości domyślne można konfigurować.

HorizontalPager

Aby utworzyć pager, który można przewijać w poziomie w lewo i w prawo, użyj funkcji HorizontalPager:

Rysunek 1. Demonstracja usługi 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 funkcji VerticalPager:

Rysunek 2. Demonstracja usługi VerticalPager

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

Leniwe tworzenie

Zarówno strony HorizontalPager, jak i VerticalPagerleniwe i układane w razie potrzeby. Gdy użytkownik przewija strony, funkcja kompozycyjna usuwa wszystkie strony, które nie są już potrzebne.

Wczytaj więcej stron poza ekranem

Domyślnie pager wczytuje tylko strony widoczne na ekranie. Aby wczytywać więcej stron poza ekranem, ustaw beyondBoundsPageCount na wartość większą od zera.

Przewijanie do elementu w folderze

Aby przewinąć do określonej strony w usłudze pager, utwórz obiekt PagerState za pomocą metody rememberPagerState() i prześlij go do pagera jako parametr state. Możesz wywołać PagerState#scrollToPage() w tym stanie w 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 utworzyć animację dla strony, 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 zawiera 3 usługi z informacjami o stronach: currentPage, settledPage i targetPage.

  • currentPage: strona najbliższa pozycji przyciągania. Domyślnie miejsce przyciągania znajduje się na początku układu.
  • settledPage: numer strony, na której nie ma animacji ani przewijania. Różni się ona od właściwości currentPage tym, że currentPage aktualizuje się od razu, gdy strona znajdzie się wystarczająco blisko pozycji przyciągania, ale parametr settledPage pozostaje taki sam, dopóki nie zostaną zakończone wszystkie animacje.
  • targetPage: proponowana pozycja zatrzymania 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 na stronie, możesz wykonać 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 została wybrana z liczby stron, i narysuj swój wskaźnik niestandardowy.

Jeśli na przykład potrzebujesz prostego wskaźnika okręgu, możesz powtórzyć liczbę kręgów i zmienić kolor okręgu w zależności od tego, czy strona została wybrana, za pomocą funkcji 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)
        )
    }
}

Pager ze wskaźnikiem okręgu pod treścią
Rysunek 3. Pager ze wskaźnikiem kołowym pod treścią

Stosuj do treści efekty przewijania

Typowym przypadkiem użycia jest użycie pozycji przewijania do zastosowania efektów do elementów pager. Aby sprawdzić, jak daleko od wybranej obecnie strony znajduje się dana strona, możesz użyć funkcji PagerState.currentPageOffsetFraction. Następnie możesz zastosować efekty przekształcenia do treści na podstawie odległości od wybranej strony.

Rysunek 4. Stosowanie przekształceń do treści pagera

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

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 elementy HorizontalPager i VerticalPager zajmują pełną szerokość lub pełną wysokość. W zmiennej pageSize możesz ustawić wartość Fixed, Fill (domyślnie) lub niestandardowy sposób obliczania rozmiaru.

Aby na przykład ustawić stronę o stałej szerokości 100.dp:

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

Aby dopasować rozmiar stron do rozmiaru widocznego obszaru, użyj niestandardowych obliczeń rozmiaru strony. Utwórz obiekt niestandardowy PageSize i podziel availableSpace przez trzy, biorąc pod uwagę odstępy między elementami:

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

Dopełnienie treści

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

Na przykład ustawienie dopełnienia start powoduje wyrównanie stron pod koniec:

Pager z dopełnieniem początkowym pokazujący treść wyrównaną do końca

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

Ustawienie dopełnienia start i end na tę samą wartość powoduje wyśrodkowanie elementu w poziomie:

Pager z dopełnieniem początkowym i końcowym pokazującym wyśrodkowanie treści

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

Ustawienie dopełnienia end powoduje wyrównanie strony względem 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
}

Możesz ustawić wartości top i bottom, aby uzyskać podobne efekty w przypadku VerticalPager. Wartość 32.dp jest tu użyta tylko jako przykład; każdy wymiar dopełnienia może mieć dowolną wartość.

Dostosowywanie działania przewijania

Domyślne elementy kompozycyjne HorizontalPager i VerticalPager określają sposób działania gestów przewijania z pagerem. Możesz jednak dostosować i zmienić wartości domyślne, np. pagerSnapDistance lub flingBehaviour.

Odległość przyciągania

Domyślnie HorizontalPager i VerticalPager określają maksymalną liczbę stron, które można przesunąć w dół, aby przewinąć stronę do jednej strony naraz. Aby to zmienić, ustaw pagerSnapDistance w 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)
    }
}