Untuk membalik konten secara horizontal atau vertikal, Anda dapat menggunakan
HorizontalPager dan VerticalPager composable. Keduanya memiliki
fungsi yang mirip dengan ViewPager dalam sistem tampilan. Secara default, HorizontalPager menggunakan lebar layar penuh dan VerticalPager menggunakan tinggi penuh. Pager juga hanya menggeser satu halaman dalam satu waktu. Semua default ini dapat dikonfigurasi.
HorizontalPager
Untuk membuat pager yang di-scroll secara horizontal ke kiri dan kanan, gunakan 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
Untuk membuat pager yang di-scroll ke atas dan ke bawah, gunakan VerticalPager:
VerticalPager
// Display 10 items val pagerState = rememberPagerState(pageCount = { 10 }) VerticalPager(state = pagerState) { page -> // Our page content Text( text = "Page: $page", modifier = Modifier.fillMaxWidth() ) }
Pembuatan lambat
Halaman di HorizontalPager dan VerticalPager disusun secara lambat
dan ditata saat diperlukan. Saat pengguna men-scroll halaman, composable akan menghapus halaman yang tidak lagi diperlukan.
Memuat lebih banyak halaman di luar layar
Secara default, pager hanya memuat halaman yang terlihat di layar. Untuk memuat lebih banyak halaman di luar layar, tetapkan beyondBoundsPageCount ke nilai yang lebih tinggi dari nol.
Men-scroll ke item di pager
Untuk men-scroll ke halaman tertentu di pager, buat PagerState objek
menggunakan rememberPagerState() dan teruskan sebagai parameter state ke
pager. Anda dapat memanggil PagerState#scrollToPage() pada status ini, di dalam a
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") }
Jika Anda ingin membuat animasi ke halaman, gunakan fungsi
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") }
Mendapatkan notifikasi tentang perubahan status halaman
PagerState memiliki tiga properti dengan informasi tentang halaman:
currentPage, settledPage, dan targetPage.
currentPage: Halaman terdekat dengan posisi snap. Secara default, posisi snap berada di awal tata letak.settledPage: Nomor halaman saat tidak ada animasi atau scrolling yang berjalan. Hal ini berbeda dengan properticurrentPagekarenacurrentPageakan segera diperbarui jika halaman cukup dekat dengan posisi snap, tetapisettledPagetetap sama hingga semua animasi selesai berjalan.targetPage: Posisi berhenti yang diusulkan untuk gerakan scrolling.
Anda dapat menggunakan fungsi snapshotFlow untuk mengamati perubahan pada variabel ini dan bereaksi terhadapnya. Misalnya, untuk mengirim peristiwa analisis pada setiap perubahan halaman, Anda dapat melakukan hal berikut:
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") }
Menambahkan indikator halaman
Untuk menambahkan indikator ke halaman, gunakan objek PagerState untuk mendapatkan informasi tentang halaman mana yang dipilih dari jumlah halaman, dan gambar indikator kustom Anda.
Misalnya, untuk membuat indikator lingkaran, Anda dapat mengulangi jumlah lingkaran dan mengubah warna lingkaran berdasarkan apakah halaman dipilih atau tidak, menggunakan 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) ) } }
Menerapkan efek scroll item ke konten
Kasus penggunaan umum adalah menggunakan posisi scroll untuk menerapkan efek ke item pager Anda. Untuk mengetahui seberapa jauh halaman dari halaman yang dipilih, Anda dapat menggunakan
PagerState.currentPageOffsetFraction. Kemudian, Anda dapat menerapkan efek transformasi ke konten berdasarkan jarak dari halaman yang dipilih.
Misalnya, untuk menyesuaikan opasitas item berdasarkan seberapa jauh item tersebut dari
tengah, ubah alpha menggunakan Modifier.graphicsLayer pada item
di dalam 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 } }
Ukuran halaman kustom
Secara default, HorizontalPager dan VerticalPager menggunakan lebar penuh atau tinggi penuh. Anda dapat menetapkan variabel pageSize agar memiliki
Fixed, Fill (default), atau perhitungan ukuran kustom.
Misalnya, untuk menetapkan halaman lebar tetap 100.dp:
val pagerState = rememberPagerState(pageCount = { 4 }) HorizontalPager( state = pagerState, pageSize = PageSize.Fixed(100.dp) ) { page -> // page content }
Untuk menentukan ukuran halaman berdasarkan ukuran area pandang, gunakan perhitungan ukuran halaman kustom. Buat objek PageSize kustom dan bagi
availableSpace dengan tiga, dengan mempertimbangkan jarak antar-item:
private val threePagesPerViewport = object : PageSize { override fun Density.calculateMainAxisPageSize( availableSpace: Int, pageSpacing: Int ): Int { return (availableSpace - 2 * pageSpacing) / 3 } }
Padding konten
HorizontalPager dan VerticalPager mendukung perubahan padding konten, yang memungkinkan Anda memengaruhi ukuran dan perataan maksimum halaman.
Misalnya, menetapkan padding start akan meratakan halaman ke akhir:
val pagerState = rememberPagerState(pageCount = { 4 }) HorizontalPager( state = pagerState, contentPadding = PaddingValues(start = 64.dp), ) { page -> // page content }
Menetapkan padding start dan end ke nilai yang sama akan memusatkan item secara horizontal:
val pagerState = rememberPagerState(pageCount = { 4 }) HorizontalPager( state = pagerState, contentPadding = PaddingValues(horizontal = 32.dp), ) { page -> // page content }
Menetapkan padding end akan meratakan halaman ke awal:
val pagerState = rememberPagerState(pageCount = { 4 }) HorizontalPager( state = pagerState, contentPadding = PaddingValues(end = 64.dp), ) { page -> // page content }
Anda dapat menetapkan nilai top dan bottom untuk mencapai efek serupa untuk VerticalPager. Nilai 32.dp hanya digunakan di sini sebagai contoh; Anda dapat menetapkan setiap dimensi padding ke nilai apa pun.
Menyesuaikan perilaku scroll
Composable HorizontalPager dan VerticalPager default menentukan cara gestur scrolling berfungsi dengan pager. Namun, Anda dapat menyesuaikan dan mengubah default seperti pagerSnapDistance atau flingBehavior.
Jarak snap
Secara default, HorizontalPager dan VerticalPager menetapkan jumlah maksimum halaman yang dapat di-scroll oleh gestur geser ke satu halaman dalam satu waktu. Untuk mengubah
ini, tetapkan pagerSnapDistance pada 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) } }
Membuat pager yang otomatis maju
Bagian ini menjelaskan cara membuat pager yang otomatis maju dengan indikator halaman di Compose. Kumpulan item otomatis di-scroll secara horizontal, tetapi pengguna juga dapat menggeser antar-item secara manual. Jika pengguna berinteraksi dengan pager, progres otomatis akan berhenti.
Contoh dasar
Bersama-sama, cuplikan berikut membuat implementasi pager otomatis maju dasar dengan indikator visual, yang setiap halamannya dirender sebagai warna yang berbeda:
@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) } }
Poin-poin penting tentang kode
- Fungsi
AutoAdvancePagermembuat tampilan halaman horizontal dengan kemajuan otomatis. Fungsi ini mengambil daftar objekColorsebagai input, yang digunakan sebagai warna latar belakang untuk setiap halaman. pagerStatedibuat menggunakanrememberPagerState, yang menyimpan status pager.pagerIsDraggeddanpageIsPressedmelacak interaksi pengguna.- The
LaunchedEffectotomatis memajukan pager setiap dua detik kecuali jika pengguna menarik pager atau menekan salah satu halaman. HorizontalPagermenampilkan daftar halaman, yang masing-masing memiliki composableTextyang menampilkan nomor halaman. Pengubah mengisi halaman, menetapkan warna latar belakang daripageItems, dan membuat halaman dapat diklik.
@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) ) } } } }
Poin-poin penting tentang kode
- Composable
Boxbertindak sebagai elemen root dan berisiRowuntuk mengatur indikator halaman secara horizontal. - Indikator halaman kustom ditampilkan sebagai baris lingkaran, dengan setiap
Boxyang dipangkas keCircleShapemewakili halaman. - Lingkaran halaman saat ini diberi warna
DarkGray, sedangkan lingkaran lainnya adalahLightGray. ParametercurrentPageIndexmenentukan lingkaran mana yang dirender dalam warna abu-abu gelap.
Hasil
Video ini menampilkan pager otomatis maju dasar dari cuplikan sebelumnya: