หากต้องการพลิกดูเนื้อหาในแนวนอนหรือแนวตั้ง คุณสามารถใช้ Composable HorizontalPager และ VerticalPager ซึ่งมีฟังก์ชันคล้ายกับ ViewPager ในระบบการดู โดยค่าเริ่มต้น
HorizontalPager จะใช้ความกว้างเต็มหน้าจอ และ VerticalPager จะใช้ความสูงเต็มหน้าจอ
นอกจากนี้ เพจเจอร์ยังแสดงได้ทีละหน้าเท่านั้น ค่าเริ่มต้นทั้งหมดนี้
กำหนดค่าได้
HorizontalPager
หากต้องการสร้างเพจเจอร์ที่เลื่อนในแนวนอนไปทางซ้ายและขวา ให้ใช้
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
หากต้องการสร้างเพจเจอร์ที่เลื่อนขึ้นและลง ให้ใช้ VerticalPager
VerticalPager
// Display 10 items val pagerState = rememberPagerState(pageCount = { 10 }) VerticalPager(state = pagerState) { page -> // Our page content Text( text = "Page: $page", modifier = Modifier.fillMaxWidth() ) }
การสร้างเมื่อจำเป็น
หน้าในทั้ง HorizontalPager และ VerticalPager จะสร้างแบบเลย์เอาต์ตามต้องการ
และจัดวางเมื่อจำเป็น เมื่อผู้ใช้เลื่อนดูหน้าต่างๆ Composable จะนำหน้าเว็บที่ไม่จำเป็นออก
โหลดหน้าถัดไปเพิ่มเติม
โดยค่าเริ่มต้น เพจเจอร์จะโหลดเฉพาะหน้าที่มองเห็นได้บนหน้าจอ หากต้องการโหลดหน้าถัดไปเพิ่มเติม ให้ตั้งค่า beyondBoundsPageCount เป็นค่าที่มากกว่า 0
เลื่อนไปยังรายการในเพจเจอร์
หากต้องการเลื่อนไปยังหน้าใดหน้าหนึ่งในเพจเจอร์ ให้สร้างออบเจ็กต์ PagerState
โดยใช้ rememberPagerState() แล้วส่งเป็นพารามิเตอร์ state ไปยังเพจเจอร์ คุณสามารถเรียกใช้ PagerState#scrollToPage() ในสถานะนี้ภายใน
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") }
หากต้องการสร้างภาพเคลื่อนไหวไปยังหน้าใด ให้ใช้ฟังก์ชัน
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") }
รับการแจ้งเตือนเกี่ยวกับการเปลี่ยนแปลงสถานะของหน้า
PagerState มีพร็อพเพอร์ตี้ 3 รายการที่มีข้อมูลเกี่ยวกับหน้าต่างๆ ได้แก่ currentPage, settledPage และ targetPage
currentPage: หน้าที่ใกล้กับตำแหน่งสแนปมากที่สุด โดยค่าเริ่มต้น ตำแหน่งสแนปจะอยู่ที่จุดเริ่มต้นของเลย์เอาต์settledPage: หมายเลขหน้าเมื่อไม่มีการแสดงภาพเคลื่อนไหวหรือการเลื่อน ซึ่งแตกต่างจากพร็อพเพอร์ตี้currentPageตรงที่currentPageจะอัปเดตทันทีหากหน้าอยู่ใกล้ตำแหน่งสแนปมากพอ แต่settledPageจะยังคงเหมือนเดิมจนกว่าภาพเคลื่อนไหวทั้งหมดจะแสดงเสร็จtargetPage: ตำแหน่งหยุดที่เสนอสำหรับการเลื่อน
คุณสามารถใช้ฟังก์ชัน snapshotFlow เพื่อสังเกตการเปลี่ยนแปลงตัวแปรเหล่านี้
และตอบสนองต่อการเปลี่ยนแปลงได้ ตัวอย่างเช่น หากต้องการส่งเหตุการณ์ Analytics เมื่อมีการเปลี่ยนแปลงหน้าเว็บแต่ละครั้ง
คุณสามารถทำดังนี้
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") }
เพิ่มตัวบ่งชี้หน้า
หากต้องการเพิ่มตัวบ่งชี้ลงในหน้า ให้ใช้ออบเจ็กต์ PagerState เพื่อรับข้อมูลเกี่ยวกับหน้าที่เลือกจากจำนวนหน้าทั้งหมด แล้ววาดตัวบ่งชี้ที่กำหนดเอง
เช่น หากต้องการสร้างตัวบ่งชี้รูปวงกลม คุณสามารถทำซ้ำจำนวนวงกลมและเปลี่ยนสีวงกลมเมื่อมีการเลือกหน้า โดยใช้ 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) ) } }
ใช้เอฟเฟกต์การเลื่อนรายการกับเนื้อหา
Use Case ทั่วไปคือการใช้ตำแหน่งการเลื่อนเพื่อใช้เอฟเฟกต์กับรายการเพจเจอร์ หากต้องการดูว่าหน้าหนึ่งๆ อยู่ห่างจากหน้าที่เลือกเท่าใด คุณสามารถใช้ PagerState.currentPageOffsetFraction จากนั้นคุณจะใช้เอฟเฟกต์การแปลงรูปภาพกับเนื้อหาตามระยะห่างจากหน้าที่เลือกได้
เช่น หากต้องการปรับความทึบแสงของรายการตามระยะห่างจากกึ่งกลาง ให้เปลี่ยน alpha โดยใช้ Modifier.graphicsLayer ในรายการภายในเพจเจอร์ ดังนี้
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 } }
ขนาดหน้าที่กำหนดเอง
โดยค่าเริ่มต้น HorizontalPager และ VerticalPager จะใช้ความกว้างเต็มหรือความสูงเต็มตามลำดับ คุณตั้งค่าตัวแปร pageSize ให้มี Fixed, Fill (ค่าเริ่มต้น) หรือการคำนวณขนาดที่กำหนดเองได้
เช่น หากต้องการตั้งค่าหน้าที่มีความกว้างคงที่ 100.dp ให้ทำดังนี้
val pagerState = rememberPagerState(pageCount = { 4 }) HorizontalPager( state = pagerState, pageSize = PageSize.Fixed(100.dp) ) { page -> // page content }
หากต้องการปรับขนาดหน้าตามขนาดวิวพอร์ต ให้ใช้การคำนวณขนาดหน้าแบบกำหนดเอง
สร้างออบเจ็กต์ PageSize ที่กำหนดเองและแบ่ง availableSpace ออกเป็น 3 ส่วน โดยคำนึงถึงระยะห่างระหว่างรายการ
private val threePagesPerViewport = object : PageSize { override fun Density.calculateMainAxisPageSize( availableSpace: Int, pageSpacing: Int ): Int { return (availableSpace - 2 * pageSpacing) / 3 } }
ระยะห่างจากขอบเนื้อหา
HorizontalPager และ VerticalPager รองรับการเปลี่ยนระยะห่างจากขอบเนื้อหา ซึ่งช่วยให้คุณกำหนดขนาดสูงสุดและการจัดข้อความของหน้าได้
เช่น การตั้งค่าระยะห่างจากขอบเป็น start จะจัดข้อความชิดด้านท้ายของหน้า
val pagerState = rememberPagerState(pageCount = { 4 }) HorizontalPager( state = pagerState, contentPadding = PaddingValues(start = 64.dp), ) { page -> // page content }
การตั้งค่าระยะห่างจากขอบทั้ง start และ end ให้เป็นค่าเดียวกันจะจัดรายการไว้กึ่งกลางในแนวนอน
val pagerState = rememberPagerState(pageCount = { 4 }) HorizontalPager( state = pagerState, contentPadding = PaddingValues(horizontal = 32.dp), ) { page -> // page content }
การตั้งค่าระยะห่างจากขอบเป็น end จะจัดข้อความชิดจุดเริ่มต้นของหน้า
val pagerState = rememberPagerState(pageCount = { 4 }) HorizontalPager( state = pagerState, contentPadding = PaddingValues(end = 64.dp), ) { page -> // page content }
คุณสามารถตั้งค่า top และ bottom เพื่อให้ได้เอฟเฟกต์ที่คล้ายกันสำหรับ VerticalPager ได้ ค่า 32.dp ใช้เป็นตัวอย่างที่นี่เท่านั้น คุณสามารถตั้งค่ามิติข้อมูลระยะห่างจากขอบแต่ละรายการเป็นค่าใดก็ได้
ปรับแต่งลักษณะการเลื่อน
Composable HorizontalPager และ VerticalPager เริ่มต้นจะระบุวิธีที่ท่าทางการเลื่อนทำงานร่วมกับเพจเจอร์ อย่างไรก็ตาม คุณสามารถปรับแต่งและเปลี่ยน
ค่าเริ่มต้น เช่น pagerSnapDistance หรือ flingBehavior ได้
ระยะการสแนป
โดยค่าเริ่มต้น HorizontalPager และ VerticalPager จะกำหนดจำนวนหน้าสูงสุดที่ท่าทางการตวัดสามารถเลื่อนผ่านได้เป็นครั้งละ 1 หน้า หากต้องการเปลี่ยน
ค่านี้ ให้ตั้งค่า pagerSnapDistance ใน 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) } }
สร้างเพจเจอร์ที่เลื่อนอัตโนมัติ
ส่วนนี้จะอธิบายวิธีสร้างเพจเจอร์ที่เลื่อนอัตโนมัติพร้อมตัวระบุหน้าใน Compose ระบบจะเลื่อนคอลเล็กชันรายการในแนวนอนโดยอัตโนมัติ แต่ผู้ใช้ก็สามารถปัดไปมาระหว่างรายการต่างๆ ด้วยตนเองได้เช่นกัน หากผู้ใช้โต้ตอบกับเพจเจอร์ ระบบจะหยุดการเลื่อนอัตโนมัติ
ตัวอย่างพื้นฐาน
ข้อมูลโค้ดต่อไปนี้จะสร้างการใช้งานเพจเจอร์เลื่อนอัตโนมัติพื้นฐานพร้อมตัวบ่งชี้ภาพ ซึ่งแต่ละหน้าจะแสดงผลเป็นสีที่แตกต่างกัน
@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) } }
ประเด็นสำคัญเกี่ยวกับโค้ด
- ฟังก์ชัน
AutoAdvancePagerจะสร้างมุมมองการแบ่งหน้าแนวนอนพร้อม การเลื่อนอัตโนมัติ โดยรับรายการออบเจ็กต์Colorเป็นอินพุต ซึ่งจะใช้เป็นสีพื้นหลังสำหรับแต่ละหน้า pagerStateสร้างขึ้นโดยใช้rememberPagerStateซึ่งเก็บสถานะ ของเพจเจอร์pagerIsDraggedและpageIsPressedจะติดตามการโต้ตอบของผู้ใช้LaunchedEffectจะเลื่อนเพจเจอร์โดยอัตโนมัติทุกๆ 2 วินาที เว้นแต่ผู้ใช้จะลากเพจเจอร์หรือกดหน้าใดหน้าหนึ่งHorizontalPagerจะแสดงรายการของหน้า โดยแต่ละหน้าจะมี ComposableTextซึ่งแสดงหมายเลขหน้า ตัวปรับแต่งจะเติมเต็มพื้นที่หน้าเว็บ ตั้งค่าสีพื้นหลังจากpageItemsและทำให้หน้าเว็บคลิกได้
@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) ) } } } }
ประเด็นสำคัญเกี่ยวกับโค้ด
- Composable
Boxทำหน้าที่เป็นองค์ประกอบรูทและมีRowเพื่อ จัดเรียงตัวบ่งชี้หน้าในแนวนอน - ตัวบ่งชี้หน้าที่กำหนดเองจะแสดงเป็นแถววงกลม โดยแต่ละวงที่
Boxเชื่อมต่อกับCircleShapeจะแสดงหน้าเว็บ - วงกลมของหน้าปัจจุบันจะมีค่าสีเป็น
DarkGrayส่วนวงอื่นๆ จะเป็นLightGrayพารามิเตอร์currentPageIndexจะกำหนดว่าวงใดจะแสดงเป็นสีเทาเข้ม
ผลลัพธ์
วิดีโอนี้แสดงเพจเจอร์พื้นฐานที่เปลี่ยนหน้าอัตโนมัติจากข้อมูลโค้ดก่อนหน้า