Aby przeglądać treści w poziomie lub w pionie, możesz użyć funkcji kompozycyjnych HorizontalPager i VerticalPager. Mają one podobne funkcje jak ViewPager w systemie widoków. Domyślnie HorizontalPager zajmuje całą szerokość ekranu, a VerticalPager – całą wysokość. Paginatory również przesuwają tylko 1 stronę naraz. Wszystkie te ustawienia domyślne można konfigurować.
HorizontalPager
Aby utworzyć pager, który przewija się w poziomie w lewo i w prawo, użyj HorizontalPager:
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 VerticalPager:
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
Strony w HorizontalPager i VerticalPager są tworzone i układane z opóźnieniem, gdy jest to wymagane. Gdy użytkownik przewija strony, funkcja kompozycyjna usuwa te, które nie są już potrzebne.
Wczytywanie większej liczby stron poza ekranem
Domyślnie pager wczytuje tylko strony widoczne na ekranie. Aby wczytać więcej stron poza ekranem, ustaw beyondBoundsPageCount na wartość większą niż 0.
Przewijanie do elementu w paginatorze
Aby przewinąć do określonej strony w komponencie Pager, utwórz obiekt PagerState za pomocą rememberPagerState() i przekaż go jako parametr state do komponentu Pager. Możesz wywołać PagerState#scrollToPage() w tym stanie wewnątrz 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 animować przejście do 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 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 jest uruchomiona animacja ani przewijanie. Różni się ona od właściwościcurrentPagetym, żecurrentPagejest aktualizowana natychmiast, gdy strona jest wystarczająco blisko pozycji przyciągania, alesettledPagepozostaje taka sama, dopóki nie zakończą się wszystkie animacje.targetPage: proponowana pozycja zatrzymania ruchu przewijania.
Możesz użyć funkcji snapshotFlow, aby obserwować zmiany tych zmiennych i na nie reagować. Aby na przykład wysyłać zdarzenie Analytics przy każdej zmianie strony, 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") }
Dodawanie wskaźnika strony
Aby dodać wskaźnik do strony, użyj obiektu PagerState, aby uzyskać informacje o tym, która strona jest wybrana z liczby stron, i narysuj niestandardowy wskaźnik.
Aby na przykład utworzyć wskaźnik kołowy, możesz powtórzyć liczbę kółek i zmienić ich kolor w zależności od tego, czy strona jest wybrana, używając 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) ) } }
Stosowanie efektów przewijania elementów do treści
Typowym przypadkiem użycia jest wykorzystanie pozycji przewijania do stosowania efektów do elementów pagera. Aby dowiedzieć się, jak daleko od wybranej strony znajduje się dana strona, możesz użyć PagerState.currentPageOffsetFraction. Następnie możesz zastosować efekty przekształcania do treści na podstawie odległości od wybranej strony.
Jeśli na przykład chcesz dostosować przezroczystość elementów na podstawie ich odległości od środka, zmień alpha za pomocą Modifier.graphicsLayer na elemencie w pagerze:
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 HorizontalPager i VerticalPager zajmują odpowiednio pełną szerokość lub pełną wysokość. Możesz ustawić zmienną pageSize tak, aby miała wartość Fixed, Fill (domyślnie) lub niestandardowe obliczanie rozmiaru.
Aby na przykład ustawić stałą szerokość strony na 100.dp:
val pagerState = rememberPagerState(pageCount = { 4 }) HorizontalPager( state = pagerState, pageSize = PageSize.Fixed(100.dp) ) { page -> // page content }
Aby dostosować rozmiar stron do rozmiaru widocznego obszaru, użyj niestandardowego obliczania rozmiaru strony. Utwórz niestandardowy obiekt PageSize i podziel availableSpace przez 3, uwzględniając 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 wpływać na maksymalny rozmiar i wyrównanie stron.
Na przykład ustawienie dopełnienia start wyrównuje strony 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:
val pagerState = rememberPagerState(pageCount = { 4 }) HorizontalPager( state = pagerState, contentPadding = PaddingValues(horizontal = 32.dp), ) { page -> // page content }
Ustawienie dopełnienia end wyrównuje strony 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 używana tylko jako przykład. Możesz ustawić dowolną wartość dla każdego z wymiarów dopełnienia.
Dostosowywanie działania przewijania
Domyślne funkcje kompozycyjne HorizontalPager i VerticalPager określają, jak gesty przewijania działają z pagerem. Możesz jednak dostosować i zmienić ustawienia domyślne, takie jak pagerSnapDistance lub flingBehavior.
Odległość przyciągania
Domyślnie HorizontalPager i VerticalPager ustawiają maksymalną liczbę stron, które można przewinąć gestem szybkiego przesunięcia, na 1 stronę naraz. Aby to zmienić, ustaw pagerSnapDistance na 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) } }
Tworzenie pagera z automatycznym przechodzeniem
W tej sekcji opisano, jak utworzyć w Compose automatycznie przewijany pager ze wskaźnikami stron. Kolekcja elementów przewija się automatycznie w poziomie, ale użytkownicy mogą też ręcznie przesuwać palcem między elementami. Jeśli użytkownik wejdzie w interakcję z pagerem, automatyczne przechodzenie zostanie zatrzymane.
Podstawowy przykład
Poniższe fragmenty kodu tworzą podstawową implementację automatycznie przewijanego pagera ze wskaźnikiem wizualnym, w którym każda strona jest renderowana w innym kolorze:
@Composable fun AutoAdvancePager(pageItems: List<Color>, modifier: Modifier = Modifier) { Box(modifier = Modifier.fillMaxSize()) { val pagerState = rememberPagerState(pageCount = { pageItems.size }) val pagerIsDragged by pagerState.interactionSource.collectIsDraggedAsState() val pageInteractionSource = remember { MutableInteractionSource() } val pageIsPressed by pageInteractionSource.collectIsPressedAsState() // Stop auto-advancing when pager is dragged or one of the pages is pressed val autoAdvance = !pagerIsDragged && !pageIsPressed if (autoAdvance) { LaunchedEffect(pagerState, pageInteractionSource) { while (true) { delay(2000) val nextPage = (pagerState.currentPage + 1) % pageItems.size pagerState.animateScrollToPage(nextPage) } } } HorizontalPager( state = pagerState ) { page -> Text( text = "Page: $page", textAlign = TextAlign.Center, modifier = modifier .fillMaxSize() .background(pageItems[page]) .clickable( interactionSource = pageInteractionSource, indication = LocalIndication.current ) { // Handle page click } .wrapContentSize(align = Alignment.Center) ) } PagerIndicator(pageItems.size, pagerState.currentPage) } }
Najważniejsze informacje o kodzie
- Funkcja
AutoAdvancePagertworzy widok z automatycznym przewijaniem poziomym. Jako dane wejściowe przyjmuje listę obiektówColor, które są używane jako kolory tła dla każdej strony. pagerStatejest tworzony za pomocąrememberPagerState, który przechowuje stan pagera.pagerIsDraggedipageIsPressedśledzą interakcje użytkowników.LaunchedEffectautomatycznie przechodzi do kolejnej strony co 2 sekundy, chyba że użytkownik przeciągnie stronę lub naciśnie jedną ze stron.HorizontalPagerwyświetla listę stron, z których każda zawiera element kompozycyjnyTextwyświetlający numer strony. Modyfikator wypełnia stronę, ustawia kolor tła zpageItemsi sprawia, że strona jest klikalna.
@Composable fun PagerIndicator(pageCount: Int, currentPageIndex: Int, modifier: Modifier = Modifier) { Box(modifier = Modifier.fillMaxSize()) { Row( modifier = Modifier .wrapContentHeight() .fillMaxWidth() .align(Alignment.BottomCenter) .padding(bottom = 8.dp), horizontalArrangement = Arrangement.Center ) { repeat(pageCount) { iteration -> val color = if (currentPageIndex == iteration) Color.DarkGray else Color.LightGray Box( modifier = modifier .padding(2.dp) .clip(CircleShape) .background(color) .size(16.dp) ) } } } }
Najważniejsze informacje o kodzie
- Element kompozycyjny
Boxdziała jako element główny i zawiera elementRow, który umożliwia poziome rozmieszczenie wskaźników strony. - Niestandardowy wskaźnik strony jest wyświetlany jako wiersz okręgów, gdzie każdy
Boxprzycięty doCircleShapereprezentuje stronę. - Kółko bieżącej strony ma kolor
DarkGray, a pozostałe kółka sąLightGray. ParametrcurrentPageIndexokreśla, które kółko jest ciemnoszare.
Wynik
Ten film przedstawia podstawowy pager z automatycznym przechodzeniem do przodu z poprzednich fragmentów kodu: