ปัดไปด้านข้างเพื่อปิดหรืออัปเดต

คอมโพเนนต์ SwipeToDismissBox ช่วยให้ผู้ใช้ปิดหรืออัปเดตรายการได้โดยการปัดไปทางซ้ายหรือขวา

แพลตฟอร์ม API

ใช้ Composable SwipeToDismissBox เพื่อใช้การดำเนินการที่ทริกเกอร์ด้วยท่าทางสัมผัสด้วยการปัด พารามิเตอร์สำคัญมีดังนี้

  • state: สถานะ SwipeToDismissBoxState ที่สร้างขึ้นเพื่อจัดเก็บค่าที่ได้จากการคำนวณในรายการปัด ซึ่งจะทริกเกอร์เหตุการณ์เมื่อสร้างขึ้น
  • backgroundContent: Composable ที่ปรับแต่งได้ซึ่งแสดงอยู่ด้านหลังเนื้อหาของรายการ ซึ่งจะปรากฏขึ้นเมื่อปัดเนื้อหา

ตัวอย่างพื้นฐาน: อัปเดตหรือปิดเมื่อปัด

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

data class TodoItem(
    val itemDescription: String,
    var isItemDone: Boolean = false
)

@Composable
fun TodoListItem(
    todoItem: TodoItem,
    onToggleDone: (TodoItem) -> Unit,
    onRemove: (TodoItem) -> Unit,
    modifier: Modifier = Modifier,
) {
    val swipeToDismissBoxState = rememberSwipeToDismissBoxState(
        confirmValueChange = {
            if (it == StartToEnd) onToggleDone(todoItem)
            else if (it == EndToStart) onRemove(todoItem)
            // Reset item when toggling done status
            it != StartToEnd
        }
    )

    SwipeToDismissBox(
        state = swipeToDismissBoxState,
        modifier = modifier.fillMaxSize(),
        backgroundContent = {
            when (swipeToDismissBoxState.dismissDirection) {
                StartToEnd -> {
                    Icon(
                        if (todoItem.isItemDone) Icons.Default.CheckBox else Icons.Default.CheckBoxOutlineBlank,
                        contentDescription = if (todoItem.isItemDone) "Done" else "Not done",
                        modifier = Modifier
                            .fillMaxSize()
                            .background(Color.Blue)
                            .wrapContentSize(Alignment.CenterStart)
                            .padding(12.dp),
                        tint = Color.White
                    )
                }
                EndToStart -> {
                    Icon(
                        imageVector = Icons.Default.Delete,
                        contentDescription = "Remove item",
                        modifier = Modifier
                            .fillMaxSize()
                            .background(Color.Red)
                            .wrapContentSize(Alignment.CenterEnd)
                            .padding(12.dp),
                        tint = Color.White
                    )
                }
                Settled -> {}
            }
        }
    ) {
        ListItem(
            headlineContent = { Text(todoItem.itemDescription) },
            supportingContent = { Text("swipe me to update or remove.") }
        )
    }
}

ประเด็นสำคัญเกี่ยวกับโค้ด

  • swipeToDismissBoxState จัดการสถานะคอมโพเนนต์ ซึ่งจะทริกเกอร์ Callback confirmValueChange เมื่อการโต้ตอบกับรายการเสร็จสิ้น เนื้อหา Callback จะจัดการการดำเนินการต่างๆ ที่เป็นไปได้ Callback จะแสดงผลบูลีนที่บอกคอมโพเนนต์ว่าควรแสดงภาพเคลื่อนไหวการปิดหรือไม่ ในกรณีนี้
    • หากปัดรายการตั้งแต่ต้นจนจบ ระบบจะเรียกใช้แลมบ์ดา onToggleDone โดยส่ง todoItem ปัจจุบัน ซึ่งสอดคล้องกับการอัปเดตรายการสิ่งที่ต้องทำ
    • หากปัดรายการจากท้ายไปต้น ระบบจะเรียกใช้ Lambda onRemove โดยส่ง todoItem ปัจจุบัน ซึ่งจะสอดคล้องกับการลบรายการสิ่งที่ต้องทำ
    • it != StartToEnd: บรรทัดนี้จะแสดงผลเป็น true หากทิศทางการปัดไม่ใช่ StartToEnd และแสดงผลเป็น false ในกรณีอื่นๆ การแสดงผล false จะป้องกันไม่ให้ SwipeToDismissBox หายไปทันทีหลังจากปัดเพื่อ "สลับเสร็จสิ้น" ซึ่งช่วยให้ยืนยันหรือแสดงภาพเคลื่อนไหวได้
  • SwipeToDismissBox ช่วยให้ผู้ใช้ปัดในแนวนอนเพื่อโต้ตอบกับแต่ละรายการได้ ในสถานะพัก จะแสดงเนื้อหาด้านในของคอมโพเนนต์ แต่เมื่อผู้ใช้เริ่มปัด เนื้อหาจะถูกย้ายออกไปและ backgroundContent จะปรากฏขึ้น ทั้งเนื้อหาปกติและ backgroundContent จะได้รับข้อจำกัดทั้งหมดของคอนเทนเนอร์หลักเพื่อแสดงผลในตัวเอง ระบบจะวาด content ไว้ด้านบนของ backgroundContent ในกรณีนี้
    • backgroundContent จะใช้เป็น Icon ที่มีสีพื้นหลังตาม SwipeToDismissBoxValue:
    • Blue เมื่อปัด StartToEnd — สลับรายการสิ่งที่ต้องทำ
    • Red เมื่อปัด EndToStart — ลบรายการสิ่งที่ต้องทำ
    • ไม่มีการแสดงสิ่งใดในพื้นหลังสำหรับ Settled — เมื่อไม่มีการปัดรายการ จะไม่มีการแสดงสิ่งใดในพื้นหลัง
    • ในทำนองเดียวกัน Icon ที่แสดงจะปรับตามทิศทางการปัด ดังนี้
    • StartToEnd จะแสดงไอคอน CheckBox เมื่อรายการสิ่งที่ต้องทำเสร็จแล้ว และไอคอน CheckBoxOutlineBlank เมื่อยังไม่เสร็จ
    • EndToStart จะแสดงไอคอน Delete

@Composable
private fun SwipeItemExample() {
    val todoItems = remember {
        mutableStateListOf(
            TodoItem("Pay bills"), TodoItem("Buy groceries"),
            TodoItem("Go to gym"), TodoItem("Get dinner")
        )
    }

    LazyColumn {
        items(
            items = todoItems,
            key = { it.itemDescription }
        ) { todoItem ->
            TodoListItem(
                todoItem = todoItem,
                onToggleDone = { todoItem ->
                    todoItem.isItemDone = !todoItem.isItemDone
                },
                onRemove = { todoItem ->
                    todoItems -= todoItem
                },
                modifier = Modifier.animateItem()
            )
        }
    }
}

ประเด็นสำคัญเกี่ยวกับโค้ด

  • mutableStateListOf(...) สร้างรายการที่สังเกตได้ซึ่งสามารถเก็บออบเจ็กต์ TodoItem ได้ เมื่อมีการเพิ่มหรือนำรายการออกจากลิสต์นี้ Compose จะจัดองค์ประกอบส่วนต่างๆ ของ UI ที่ขึ้นอยู่กับลิสต์นั้นใหม่
    • ภายใน mutableStateListOf() มีการเริ่มต้นออบเจ็กต์ TodoItem 4 รายการพร้อมคำอธิบายที่เกี่ยวข้อง ได้แก่ "จ่ายบิล" "ซื้อของใช้" "ไปยิม" และ "กินข้าวเย็น"
  • LazyColumn แสดงรายการ todoItems ที่เลื่อนในแนวตั้ง
  • onToggleDone = { todoItem -> ... } เป็นฟังก์ชัน Callback ที่เรียกใช้จากภายใน TodoListItem เมื่อผู้ใช้ทำเครื่องหมายออบเจ็กต์ว่าเสร็จแล้ว โดยจะอัปเดตพร็อพเพอร์ตี้ isItemDone ของ todoItem เนื่องจาก todoItems เป็น mutableStateListOf การเปลี่ยนแปลงนี้จึงทริกเกอร์การจัดองค์ประกอบใหม่ ซึ่งจะอัปเดต UI
  • onRemove = { todoItem -> ... } คือฟังก์ชัน Callback ที่จะทริกเกอร์เมื่อผู้ใช้นำรายการออก ระบบจะนำ todoItem ที่ระบุออกจากรายการ todoItems ซึ่งจะทำให้เกิดการจัดองค์ประกอบใหม่ด้วย และระบบจะนำรายการออกจากรายการที่แสดง
  • ระบบจะใช้ตัวแก้ไข animateItem กับ TodoListItem แต่ละรายการเพื่อให้ระบบเรียกใช้ placementSpec ของตัวแก้ไขเมื่อปิดรายการ ซึ่งจะแสดงภาพเคลื่อนไหวของการนำรายการออก รวมถึงการจัดลำดับรายการอื่นๆ ในรายการใหม่

ผลลัพธ์

วิดีโอต่อไปนี้แสดงฟังก์ชันการปัดเพื่อปิดพื้นฐานจากข้อมูลโค้ดก่อนหน้า

รูปที่ 1 การใช้งานพื้นฐานของการปัดเพื่อปิดซึ่งสามารถทำเครื่องหมายรายการว่าเสร็จสมบูรณ์และแสดงภาพเคลื่อนไหวการปิดสำหรับรายการในลิสต์

ดูโค้ดตัวอย่างแบบเต็มได้ที่ไฟล์ต้นฉบับใน GitHub

ตัวอย่างขั้นสูง: ภาพเคลื่อนไหวของสีพื้นหลังเมื่อปัด

ข้อมูลโค้ดต่อไปนี้แสดงวิธีรวมเกณฑ์ตำแหน่งเพื่อทำให้สีพื้นหลังของรายการเคลื่อนไหวเมื่อปัด

data class TodoItem(
    val itemDescription: String,
    var isItemDone: Boolean = false
)

@Composable
fun TodoListItemWithAnimation(
    todoItem: TodoItem,
    onToggleDone: (TodoItem) -> Unit,
    onRemove: (TodoItem) -> Unit,
    modifier: Modifier = Modifier,
) {
    val swipeToDismissBoxState = rememberSwipeToDismissBoxState(
        confirmValueChange = {
            if (it == StartToEnd) onToggleDone(todoItem)
            else if (it == EndToStart) onRemove(todoItem)
            // Reset item when toggling done status
            it != StartToEnd
        }
    )

    SwipeToDismissBox(
        state = swipeToDismissBoxState,
        modifier = modifier.fillMaxSize(),
        backgroundContent = {
            when (swipeToDismissBoxState.dismissDirection) {
                StartToEnd -> {
                    Icon(
                        if (todoItem.isItemDone) Icons.Default.CheckBox else Icons.Default.CheckBoxOutlineBlank,
                        contentDescription = if (todoItem.isItemDone) "Done" else "Not done",
                        modifier = Modifier
                            .fillMaxSize()
                            .drawBehind {
                                drawRect(lerp(Color.LightGray, Color.Blue, swipeToDismissBoxState.progress))
                            }
                            .wrapContentSize(Alignment.CenterStart)
                            .padding(12.dp),
                        tint = Color.White
                    )
                }
                EndToStart -> {
                    Icon(
                        imageVector = Icons.Default.Delete,
                        contentDescription = "Remove item",
                        modifier = Modifier
                            .fillMaxSize()
                            .background(lerp(Color.LightGray, Color.Red, swipeToDismissBoxState.progress))
                            .wrapContentSize(Alignment.CenterEnd)
                            .padding(12.dp),
                        tint = Color.White
                    )
                }
                Settled -> {}
            }
        }
    ) {
        OutlinedCard(shape = RectangleShape) {
            ListItem(
                headlineContent = { Text(todoItem.itemDescription) },
                supportingContent = { Text("swipe me to update or remove.") }
            )
        }
    }
}

ประเด็นสำคัญเกี่ยวกับโค้ด

  • drawBehind จะวาดลงใน Canvas โดยตรงด้านหลังเนื้อหาของ Composable Icon
    • drawRect() จะวาดสี่เหลี่ยมผืนผ้าบน Canvas และเติมขอบเขตทั้งหมดของขอบเขตการวาดด้วย Color ที่ระบุ
  • เมื่อปัด สีพื้นหลังของรายการจะเปลี่ยนไปอย่างราบรื่นโดยใช้ lerp
    • สำหรับการปัดจาก StartToEnd สีพื้นหลังจะค่อยๆ เปลี่ยนจากสีเทาอ่อนเป็นสีน้ำเงิน
    • สำหรับการปัดจากEndToStart สีพื้นหลังจะค่อยๆ เปลี่ยนจากสีเทาอ่อนเป็นสีแดง
    • ปริมาณการเปลี่ยนจากสีหนึ่งไปเป็นอีกสีหนึ่งจะกำหนดโดย swipeToDismissBoxState.progress
  • OutlinedCard เพิ่มการแยกภาพที่ละเอียดอ่อนระหว่างรายการต่างๆ

@Composable
private fun SwipeItemWithAnimationExample() {
    val todoItems = remember {
        mutableStateListOf(
            TodoItem("Pay bills"), TodoItem("Buy groceries"),
            TodoItem("Go to gym"), TodoItem("Get dinner")
        )
    }

    LazyColumn {
        items(
            items = todoItems,
            key = { it.itemDescription }
        ) { todoItem ->
            TodoListItemWithAnimation(
                todoItem = todoItem,
                onToggleDone = { todoItem ->
                    todoItem.isItemDone = !todoItem.isItemDone
                },
                onRemove = { todoItem ->
                    todoItems -= todoItem
                },
                modifier = Modifier.animateItem()
            )
        }
    }
}

ประเด็นสำคัญเกี่ยวกับโค้ด

  • ดูประเด็นสำคัญเกี่ยวกับโค้ดนี้ได้ที่ประเด็นสำคัญจากส่วนก่อนหน้า ซึ่งอธิบายข้อมูลโค้ดที่เหมือนกัน

ผลลัพธ์

วิดีโอต่อไปนี้แสดงฟังก์ชันการทำงานขั้นสูงที่มีสีพื้นหลังแบบเคลื่อนไหว

รูปที่ 2 การใช้การปัดเพื่อแสดงหรือลบ โดยมีสีพื้นหลังแบบเคลื่อนไหวและเกณฑ์ที่นานขึ้นก่อนที่ระบบจะบันทึกการดำเนินการ

ดูโค้ดตัวอย่างแบบเต็มได้ที่ไฟล์ต้นฉบับใน GitHub

แหล่งข้อมูลเพิ่มเติม