รายการด้วย Compose สำหรับ Wear OS


รายการช่วยให้ผู้ใช้เลือกรายการจากชุดตัวเลือกในอุปกรณ์ Wear OS ได้

อุปกรณ์ Wear OS หลายรุ่นใช้หน้าจอทรงกลม ซึ่งทำให้ดูรายการที่ปรากฏใกล้ด้านบนและด้านล่างของหน้าจอได้ยากขึ้น ด้วยเหตุนี้ Compose สำหรับ Wear OS จึงมีคลาส LazyColumn เวอร์ชันหนึ่งชื่อ TransformingLazyColumn ซึ่งรองรับการปรับขนาดและแอนิเมชันการเปลี่ยนรูปร่าง เมื่อรายการเคลื่อนไปที่ขอบ รายการจะเล็กลงและจางหายไป

วิธีใช้เอฟเฟกต์การปรับขนาดและการเลื่อนที่แนะนำ

  1. ใช้ Modifier.transformedHeight เพื่อให้ Compose คำนวณการเปลี่ยนแปลงความสูงเมื่อรายการเลื่อนผ่านหน้าจอ
  2. ใช้ transformation = SurfaceTransformation(transformationSpec) เพื่อใช้เอฟเฟกต์ภาพ ซึ่งรวมถึงการลดขนาดเนื้อหารายการ
  3. ใช้ TransformationSpec ที่กำหนดเองสำหรับคอมโพเนนต์ที่ไม่ใช้ transformation เป็นพารามิเตอร์ เช่น Text

แอนิเมชันต่อไปนี้แสดงวิธีที่องค์ประกอบรายการปรับขนาดและเปลี่ยนรูปร่างเมื่อเข้าใกล้ด้านบนและด้านล่างของหน้าจอ

ข้อมูลโค้ดต่อไปนี้แสดงวิธีสร้างรายการโดยใช้ TransformingLazyColumn เลย์เอาต์เพื่อสร้างเนื้อหาที่ ดูดีในหน้าจอ Wear OS ขนาดต่างๆ

ข้อมูลโค้ดยังแสดงการใช้ตัวปรับแต่ง minimumVerticalContentPadding ซึ่งคุณควรตั้งค่าในรายการเพื่อใช้ระยะห่างจากขอบที่ถูกต้องที่ด้านบนและด้านล่างของรายการ

หากต้องการแสดงตัวบ่งชี้การเลื่อน ให้แชร์ columnState ระหว่าง ScreenScaffold กับ TransformingLazyColumn

val columnState = rememberTransformingLazyColumnState()
val transformationSpec = rememberTransformationSpec()
ScreenScaffold(
    scrollState = columnState
) { contentPadding ->
    TransformingLazyColumn(
        state = columnState,
        contentPadding = contentPadding
    ) {
        item {
            ListHeader(
                modifier = Modifier
                    .fillMaxWidth()
                    .transformedHeight(this, transformationSpec)
                    .minimumVerticalContentPadding(ListHeaderDefaults.minimumTopListContentPadding),
                transformation = SurfaceTransformation(transformationSpec)
            ) {
                Text(text = "Header")
            }
        }
        // ... other items
        item {
            Button(
                modifier = Modifier
                    .fillMaxWidth()
                    .transformedHeight(this, transformationSpec)
                    .minimumVerticalContentPadding(ButtonDefaults.minimumVerticalListContentPadding),
                transformation = SurfaceTransformation(transformationSpec),
                onClick = { /* ... */ },
                icon = {
                    Icon(
                        imageVector = Icons.Default.Build,
                        contentDescription = "build",
                    )
                },
            ) {
                Text(
                    text = "Build",
                    maxLines = 1,
                    overflow = TextOverflow.Ellipsis,
                )
            }
        }
    }
}

เพิ่มเอฟเฟกต์การดีดและหยุด

การสแนปช่วยให้มั่นใจได้ว่าเมื่อผู้ใช้เลื่อนหรือตวัดรายการจนเสร็จ รายการจะหยุดลงโดยมีรายการหนึ่งๆ อยู่ในตำแหน่งที่เฉพาะเจาะจง ซึ่งโดยปกติคือตรงกลางหน้าจอ ในหน้าจอทรงกลมที่รายการปรับขนาดและเปลี่ยนรูปร่างเมื่อเคลื่อนออกจากตรงกลาง การดีดและหยุดมีประโยชน์อย่างยิ่งในการช่วยให้รายการที่เกี่ยวข้องมากที่สุดยังคงมองเห็นและอ่านได้อย่างชัดเจนในพื้นที่การดูที่เหมาะสม

หากต้องการเพิ่มลักษณะการทำงานแบบดีดและหยุด ให้ตั้งค่าพารามิเตอร์ flingBehavior เป็น TransformingLazyColumnDefaults.snapFlingBehavior(columnState) ตั้งค่า rotaryScrollableBehavior ให้ตรงกันโดยใช้ RotaryScrollableDefaults.snapBehavior(columnState) เพื่อให้ได้รับประสบการณ์การใช้งานที่สอดคล้องกันเมื่อใช้เม็ดมะยมหรือขอบจอจริง

val columnState = rememberTransformingLazyColumnState()
ScreenScaffold(scrollState = columnState) {
    TransformingLazyColumn(
        state = columnState,
        flingBehavior = TransformingLazyColumnDefaults.snapFlingBehavior(columnState),
        rotaryScrollableBehavior = RotaryScrollableDefaults.snapBehavior(columnState)
    ) {
        // ...
        // ...
    }
}

เลย์เอาต์แบบย้อนกลับ

โดยค่าเริ่มต้น รายการที่เลื่อนได้จะยึดกับขอบด้านบน หากผู้ใช้เลื่อนไปที่ด้านล่างของรายการมาตรฐานและมีการเพิ่มรายการใหม่ที่ส่วนท้าย รายการจะยังคงมุมมองของผู้ใช้ไว้ที่รายการปัจจุบัน ตัวอย่างเช่น หากผู้ใช้กำลังดูรายการที่ 10 ที่ด้านล่างของหน้าจอและมีการเพิ่มรายการที่ 11 มุมมองจะยังคงอยู่ที่รายการที่ 10 และรายการที่ 11 จะปรากฏนอกหน้าจอใต้มุมมองปัจจุบัน

ลักษณะการทำงานนี้มักจะไม่เป็นที่ต้องการสำหรับกรณีการใช้งาน เช่น แอปพลิเคชันการรับส่งข้อความหรือบันทึกแบบสด เมื่อมีรายการใหม่เข้ามา ผู้ใช้มักจะต้องการดูเนื้อหาล่าสุดทันทีหากอยู่ที่ด้านล่างของรายการอยู่แล้ว หากมีรายการจำนวนมากเข้ามาพร้อมกัน รายการควรข้ามไปแสดงรายการล่าสุดที่ด้านล่าง (ซึ่งหมายความว่ารายการระดับกลางบางรายการอาจไม่แสดงเลย เว้นแต่ผู้ใช้จะเลื่อนกลับขึ้นไป)

TransformingLazyColumn ช่วยให้คุณย้อนกลับเลย์เอาต์ได้โดยตั้งค่า reverseLayout = true เพื่อรองรับกรณีการใช้งานเหล่านี้ ซึ่งจะเปลี่ยนจุดยึดของรายการจากขอบด้านบนเป็นขอบด้านล่าง

การตั้งค่า reverseLayout = true ยังย้อนกลับลำดับภาพของรายการและทิศทางของท่าทางสัมผัสการเลื่อนเพื่อความสะดวกด้วย

  • ระบบจะจัดองค์ประกอบรายการจากด้านล่างขึ้นด้านบน ซึ่งหมายความว่าดัชนี 0 จะปรากฏที่ด้านล่างของหน้าจอ
  • การเลื่อนขึ้นจะแสดงรายการที่มีดัชนีสูงขึ้น

หากต้องการเพิ่มลักษณะการทำงานแบบดีดและหยุดพร้อมกับเลย์เอาต์แบบย้อนกลับ คุณสามารถรวม flingBehavior และ rotaryScrollableBehavior ได้ดังที่แสดงในข้อมูลโค้ดต่อไปนี้

val columnState = rememberTransformingLazyColumnState()
val transformationSpec = rememberTransformationSpec()
ScreenScaffold(scrollState = columnState) { contentPadding ->
    TransformingLazyColumn(
        state = columnState,
        contentPadding = contentPadding,
        reverseLayout = true,
        modifier = Modifier.fillMaxWidth()
    ) {
        items(10) { index ->
            Button(
                label = {
                    Text(
                        text = "Item ${index + 1}"
                    )
                },
                onClick = {},
                modifier = Modifier
                    .fillMaxWidth()
                    .transformedHeight(this, transformationSpec)
                    .minimumVerticalContentPadding(ButtonDefaults.minimumVerticalListContentPadding),
                transformation = SurfaceTransformation(transformationSpec)
            )
        }
        item {
            // With reverseLayout = true, the last item declared appears at the top.
            ListHeader(
                modifier = Modifier
                    .fillMaxWidth()
                    .transformedHeight(this, transformationSpec)
                    .minimumVerticalContentPadding(ListHeaderDefaults.minimumTopListContentPadding),
                transformation = SurfaceTransformation(transformationSpec)
            ) {
                Text("Header")
            }
        }
    }
}

รูปภาพต่อไปนี้แสดงความแตกต่างระหว่างรายการปกติกับรายการแบบย้อนกลับ

TransformingLazyColumn ที่มีเลย์เอาต์ปกติ ซึ่งแสดงรายการ 1 ที่ด้านบนและรายการอื่นๆ ตามลำดับจากน้อยไปมาก
รูปที่ 1 เลย์เอาต์รายการมาตรฐานที่เนื้อหาเติมจากบนลงล่าง
TransformingLazyColumn ที่มีเลย์เอาต์ย้อนกลับ ซึ่งแสดงรายการ 1 ที่ด้านล่างและแสดงรายการตามลำดับจากมากไปน้อยขึ้นไปด้านบน
รูปที่ 2 เลย์เอาต์รายการแบบย้อนกลับที่เนื้อหาเติมจากล่างขึ้นบน