Untuk membalik konten secara horizontal atau vertikal, Anda dapat menggunakan
composable HorizontalPager dan VerticalPager. Fungsi ini
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 setelan 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 men-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 dikomposisikan secara lambat
dan ditata letak 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 objek PagerState
menggunakan rememberPagerState() dan teruskan sebagai parameter state ke
pager. Anda dapat memanggil PagerState#scrollToPage() pada status ini, di dalam
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 menganimasikan 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 scroll yang berjalan. Properti ini berbeda dengan properticurrentPagekarenacurrentPagesegera diperbarui jika halaman cukup dekat dengan posisi snap, tetapisettledPagetetap sama hingga semua animasi selesai berjalan.targetPage: Posisi berhenti yang diusulkan untuk gerakan scroll.
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, 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 yang 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
pusat, 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 masing-masing menggunakan lebar penuh atau
tinggi penuh. Anda dapat menetapkan variabel pageSize agar memiliki
Fixed, Fill (default), atau penghitungan ukuran kustom.
Misalnya, untuk menetapkan halaman dengan lebar tetap sebesar 100.dp:
val pagerState = rememberPagerState(pageCount = { 4 }) HorizontalPager( state = pagerState, pageSize = PageSize.Fixed(100.dp) ) { page -> // page content }
Untuk mengukur halaman berdasarkan ukuran area pandang, gunakan penghitungan 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 maksimum dan perataan halaman.
Misalnya, menyetel padding start akan menyelaraskan halaman ke arah 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 }
Menyetel padding end akan menyelaraskan 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 yang 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 scroll berfungsi dengan pager. Namun, Anda dapat menyesuaikan dan mengubah
setelan default seperti pagerSnapDistance atau flingBehavior.
Jarak snap
Secara default, HorizontalPager dan VerticalPager menetapkan jumlah maksimum
halaman yang dapat di-scroll oleh gestur ayunkan jari ke satu halaman dalam satu waktu. Untuk mengubahnya, tetapkan pagerSnapDistance di 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 maju otomatis
Bagian ini menjelaskan cara membuat pager maju otomatis dengan indikator halaman di Compose. Kumpulan item di-scroll secara otomatis 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 maju otomatis 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 penting tentang kode
- Fungsi
AutoAdvancePagermembuat tampilan paging horizontal dengan kemajuan otomatis. Fungsi ini menggunakan daftar objekColorsebagai input, yang digunakan sebagai warna latar belakang untuk setiap halaman. pagerStatedibuat menggunakanrememberPagerState, yang menyimpan status pager.pagerIsDraggeddanpageIsPressedmelacak interaksi pengguna.LaunchedEffectakan otomatis memajukan pager setiap dua detik, kecuali jika pengguna menarik pager atau menekan salah satu halaman.HorizontalPagermenampilkan daftar halaman, masing-masing dengan 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 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 dipotong menjadiCircleShapemewakili halaman. - Lingkaran halaman saat ini berwarna
DarkGray, sedangkan lingkaran lainnya berwarnaLightGray. ParametercurrentPageIndexmenentukan lingkaran mana yang dirender dalam warna abu-abu gelap.
Hasil
Video ini menampilkan pager maju otomatis dasar dari cuplikan sebelumnya: