เพจเจอร์ใน Compose

ในการพลิกผ่านเนื้อหาในลักษณะซ้ายและขวาหรือขึ้นและลง คุณสามารถใช้ เวลา HorizontalPager และ VerticalPager Composable ตามลำดับ Composable เหล่านี้มีหน้าที่คล้ายกับ ViewPager ในข้อมูลพร็อพเพอร์ตี้ ระบบ โดยค่าเริ่มต้น HorizontalPager จะใช้ความกว้างของหน้าจอ VerticalPager จะใช้ความสูงสูงสุด และเพจเจอร์ที่เลื่อนได้ 1 หน้าเท่านั้น ค่าเริ่มต้นเหล่านี้กำหนดค่าได้ทั้งหมด

HorizontalPager

หากต้องการสร้างเพจเจอร์ที่เลื่อนในแนวนอนไปทางซ้ายและขวา ให้ใช้ HorizontalPager:

รูปที่ 1 การสาธิตของ 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

รูปที่ 2 การสาธิตของ VerticalPager

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

การสร้างแบบ Lazy Loading

หน้าเว็บทั้งใน HorizontalPager และ VerticalPager ทำงานแบบ Lazy Loading เขียนและจัดวางเมื่อจำเป็น ในฐานะผู้ใช้ 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)
        )
    }
}

วันที่ เพจเจอร์แสดงสัญญาณรูปวงกลมใต้เนื้อหา
รูปที่ 3 เพจเจอร์แสดงสัญญาณรูปวงกลมใต้เนื้อหา

ใช้เอฟเฟกต์การเลื่อนรายการกับเนื้อหา

กรณีการใช้งานทั่วไปคือการใช้ตำแหน่งการเลื่อนเพื่อใส่เอฟเฟกต์กับเพจเจอร์ รายการ หากต้องการดูว่าหน้าหนึ่งๆ อยู่ห่างจากหน้าที่เลือกอยู่ไกลแค่ไหน คุณสามารถทำดังต่อไปนี้ ใช้ PagerState.currentPageOffsetFraction จากนั้นคุณจะใช้เอฟเฟกต์การเปลี่ยนรูปแบบกับเนื้อหาโดยอิงตามระยะทางได้ จากหน้าที่เลือก

รูปที่ 4 การใช้การเปลี่ยนรูปแบบกับเนื้อหาเพจเจอร์

เช่น ในการปรับความทึบแสงของสินค้าตามระยะห่างจาก ตรงกลาง ให้เปลี่ยน 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),
        beyondBoundsPageCount = 10,
        flingBehavior = fling
    ) {
        PagerSampleItem(page = it)
    }
}