เครื่องมือเลือกวันที่

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

ประเภท

เครื่องมือเลือกวันที่มี 3 ประเภท ได้แก่

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

คุณสามารถใช้ฟังก์ชันที่ประกอบกันได้ต่อไปนี้เพื่อใช้เครื่องมือเลือกวันที่เหล่านี้ในแอป

  • DatePicker: ฟังก์ชันที่ประกอบกันได้ทั่วไปสำหรับเครื่องมือเลือกวันที่ คอนเทนเนอร์ที่คุณใช้จะเป็นตัวกำหนดว่าเครื่องมือเลือกวันที่นั้นจะอยู่บนแท่นชาร์จหรือแบบโมดัล
  • DatePickerDialog: คอนเทนเนอร์สำหรับเครื่องมือเลือกวันที่ทั้งแบบโมดัลและการป้อนข้อมูลแบบโมดัล
  • DateRangePicker: สำหรับเครื่องมือเลือกวันที่ที่ผู้ใช้เลือกช่วงวันที่ที่มีวันที่เริ่มต้นและวันที่สิ้นสุดได้

สถานะ

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

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

เครื่องมือเลือกวันที่แบบอยู่บนแท่นชาร์จ

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

@Composable
fun DatePickerDocked() {
    var showDatePicker by remember { mutableStateOf(false) }
    val datePickerState = rememberDatePickerState()
    val selectedDate = datePickerState.selectedDateMillis?.let {
        convertMillisToDate(it)
    } ?: ""

    Box(
        modifier = Modifier.fillMaxWidth()
    ) {
        OutlinedTextField(
            value = selectedDate,
            onValueChange = { },
            label = { Text("DOB") },
            readOnly = true,
            trailingIcon = {
                IconButton(onClick = { showDatePicker = !showDatePicker }) {
                    Icon(
                        imageVector = Icons.Default.DateRange,
                        contentDescription = "Select date"
                    )
                }
            },
            modifier = Modifier
                .fillMaxWidth()
                .height(64.dp)
        )

        if (showDatePicker) {
            Popup(
                onDismissRequest = { showDatePicker = false },
                alignment = Alignment.TopStart
            ) {
                Box(
                    modifier = Modifier
                        .fillMaxWidth()
                        .offset(y = 64.dp)
                        .shadow(elevation = 4.dp)
                        .background(MaterialTheme.colorScheme.surface)
                        .padding(16.dp)
                ) {
                    DatePicker(
                        state = datePickerState,
                        showModeToggle = false
                    )
                }
            }
        }
    }
}

@Composable
fun DatePickerFieldToModal(modifier: Modifier = Modifier) {
    var selectedDate by remember { mutableStateOf<Long?>(null) }
    var showModal by remember { mutableStateOf(false) }

    OutlinedTextField(
        value = selectedDate?.let { convertMillisToDate(it) } ?: "",
        onValueChange = { },
        label = { Text("DOB") },
        placeholder = { Text("MM/DD/YYYY") },
        trailingIcon = {
            Icon(Icons.Default.DateRange, contentDescription = "Select date")
        },
        modifier = modifier
            .fillMaxWidth()
            .pointerInput(selectedDate) {
                awaitEachGesture {
                    // Modifier.clickable doesn't work for text fields, so we use Modifier.pointerInput
                    // in the Initial pass to observe events before the text field consumes them
                    // in the Main pass.
                    awaitFirstDown(pass = PointerEventPass.Initial)
                    val upEvent = waitForUpOrCancellation(pass = PointerEventPass.Initial)
                    if (upEvent != null) {
                        showModal = true
                    }
                }
            }
    )

    if (showModal) {
        DatePickerModal(
            onDateSelected = { selectedDate = it },
            onDismiss = { showModal = false }
        )
    }
}

fun convertMillisToDate(millis: Long): String {
    val formatter = SimpleDateFormat("MM/dd/yyyy", Locale.getDefault())
    return formatter.format(Date(millis))
}

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

  • เครื่องมือเลือกวันที่จะปรากฏขึ้นเมื่อผู้ใช้คลิก IconButton
    • ปุ่มไอคอนทำหน้าที่เป็นอาร์กิวเมนต์สำหรับพารามิเตอร์ trailingIcon ของ OutlinedTextField
    • ตัวแปรสถานะ showDatePicker จะควบคุมการมองเห็นเครื่องมือเลือกวันที่แบบอยู่บนแท่นชาร์จ
  • คอนเทนเนอร์ของเครื่องมือเลือกวันที่คือฟังก์ชันที่ประกอบกันได้ Popup ซึ่งซ้อนทับเนื้อหาโดยไม่ส่งผลต่อเลย์เอาต์ขององค์ประกอบอื่นๆ
  • selectedDate จะเก็บค่าของวันที่ที่เลือกจากออบเจ็กต์ DatePickerState และจัดรูปแบบโดยใช้ฟังก์ชัน convertMillisToDate
  • วันที่ที่เลือกจะปรากฏในช่องข้อความ
  • เครื่องมือเลือกวันที่แบบอยู่บนแท่นชาร์จจะอยู่ใต้ช่องข้อความโดยใช้ตัวปรับแต่ง offset
  • ใช้ Box เป็นคอนเทนเนอร์รากเพื่อให้มีการซ้อนทับช่องข้อความและเครื่องมือเลือกวันที่อย่างเหมาะสม

ผลลัพธ์

หลังจากคลิกไอคอนปฏิทิน การใช้งานนี้จะปรากฏดังนี้

ตัวอย่างเครื่องมือเลือกวันที่ที่เชื่อมต่อ
รูปที่ 1 เครื่องมือเลือกวันที่แบบอยู่บนแท่นชาร์จ

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

@Composable
fun DatePickerModal(
    onDateSelected: (Long?) -> Unit,
    onDismiss: () -> Unit
) {
    val datePickerState = rememberDatePickerState()

    DatePickerDialog(
        onDismissRequest = onDismiss,
        confirmButton = {
            TextButton(onClick = {
                onDateSelected(datePickerState.selectedDateMillis)
                onDismiss()
            }) {
                Text("OK")
            }
        },
        dismissButton = {
            TextButton(onClick = onDismiss) {
                Text("Cancel")
            }
        }
    ) {
        DatePicker(state = datePickerState)
    }
}

  • ฟังก์ชันที่ประกอบกันได้ DatePickerModal จะแสดงเครื่องมือเลือกวันที่แบบโมดัล
  • นิพจน์แลมดา onDateSelected จะทำงานเมื่อผู้ใช้เลือกวันที่
    • โดยจะแสดงวันที่ที่เลือกไปยังฟังก์ชันที่ประกอบกันได้หลัก
  • นิพจน์แลมดา onDismiss จะทำงานเมื่อผู้ใช้ปิดกล่องโต้ตอบ

ผลลัพธ์

การใช้งานนี้จะปรากฏดังนี้

ตัวอย่างเครื่องมือเลือกวันที่แบบโมดัล
รูปที่ 2 เครื่องมือเลือกวันที่แบบโมดัล

เครื่องมือเลือกวันที่แบบโมดัลพร้อมการป้อนข้อมูล

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

@Composable
fun DatePickerModalInput(
    onDateSelected: (Long?) -> Unit,
    onDismiss: () -> Unit
) {
    val datePickerState = rememberDatePickerState(initialDisplayMode = DisplayMode.Input)

    DatePickerDialog(
        onDismissRequest = onDismiss,
        confirmButton = {
            TextButton(onClick = {
                onDateSelected(datePickerState.selectedDateMillis)
                onDismiss()
            }) {
                Text("OK")
            }
        },
        dismissButton = {
            TextButton(onClick = onDismiss) {
                Text("Cancel")
            }
        }
    ) {
        DatePicker(state = datePickerState)
    }
}

เครื่องมือเลือกวันที่แบบโมดัลพร้อมการป้อนข้อมูลนี้คล้ายกับตัวอย่างเครื่องมือเลือกวันที่แบบโมดัลมาก โดยมีข้อแตกต่างหลักๆ ดังนี้

  • พารามิเตอร์ initialDisplayMode จะตั้งค่าโหมดการแสดงผลเริ่มต้นเป็น DisplayMode.Input
เครื่องมือเลือกวันที่แบบโมดอลพร้อมอินพุต
รูปที่ 3 เครื่องมือเลือกวันที่แบบโมดัลพร้อมการป้อนข้อมูล

เครื่องมือเลือกวันที่พร้อมช่วง

คุณสามารถสร้างเครื่องมือเลือกวันที่ที่ช่วยให้ผู้ใช้เลือกช่วงระหว่างวันที่เริ่มต้นและวันที่สิ้นสุดได้ โดยใช้ DateRangePicker.

การใช้ DateRangePicker จะเหมือนกับการใช้ DatePicker โดยพื้นฐาน คุณสามารถ ใช้สำหรับเครื่องมือเลือกวันที่แบบอยู่บนแท่นชาร์จเป็นองค์ประกอบย่อยของPopUpหรือใช้เป็น เครื่องมือเลือกวันที่แบบโมดัลและส่งไปยังDatePickerDialog ข้อแตกต่างหลักๆ คือ คุณต้องใช้ DateRangePickerState แทน DatePickerState

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

@Composable
fun DateRangePickerModal(
    onDateRangeSelected: (Pair<Long?, Long?>) -> Unit,
    onDismiss: () -> Unit
) {
    val dateRangePickerState = rememberDateRangePickerState()

    DatePickerDialog(
        onDismissRequest = onDismiss,
        confirmButton = {
            TextButton(
                onClick = {
                    onDateRangeSelected(
                        Pair(
                            dateRangePickerState.selectedStartDateMillis,
                            dateRangePickerState.selectedEndDateMillis
                        )
                    )
                    onDismiss()
                }
            ) {
                Text("OK")
            }
        },
        dismissButton = {
            TextButton(onClick = onDismiss) {
                Text("Cancel")
            }
        }
    ) {
        DateRangePicker(
            state = dateRangePickerState,
            title = {
                Text(
                    text = "Select date range"
                )
            },
            showModeToggle = false,
            modifier = Modifier
                .fillMaxWidth()
                .height(500.dp)
                .padding(16.dp)
        )
    }
}

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

  • พารามิเตอร์ onDateRangeSelected เป็นการเรียกกลับที่รับ Pair<Long?, Long?> ซึ่งแสดงถึงวันที่เริ่มต้นและวันที่สิ้นสุดที่เลือก ซึ่งจะช่วยให้ฟังก์ชันที่ประกอบกันได้หลักเข้าถึงช่วงที่เลือกได้
  • rememberDateRangePickerState() จะสร้างสถานะสำหรับเครื่องมือเลือกช่วงวันที่
  • DatePickerDialog จะสร้างคอนเทนเนอร์กล่องโต้ตอบแบบโมดัล
  • ในตัวแฮนเดิล onClick ของปุ่มยืนยัน onDateRangeSelected จะส่งช่วงที่เลือกไปยังฟังก์ชันที่ประกอบกันได้หลัก
  • ฟังก์ชันที่ประกอบกันได้ DateRangePicker ทำหน้าที่เป็นเนื้อหาของกล่องโต้ตอบ

ผลลัพธ์

การใช้งานนี้จะปรากฏดังนี้

ตัวอย่างเครื่องมือเลือกวันที่แบบช่วงในโมดัล
รูปที่ 4 เครื่องมือเลือกวันที่แบบโมดัลพร้อมช่วงที่เลือก

ใช้วันที่ที่เลือก

หากต้องการเก็บวันที่ที่เลือก ให้ติดตามวันที่ดังกล่าวในฟังก์ชันที่ประกอบกันได้หลักเป็น Long และส่งค่าไปยัง DatePicker ใน onDateSelected ข้อมูลโค้ดต่อไปนี้ แสดงวิธีดำเนินการดังกล่าว แต่คุณสามารถดูการใช้งานแบบเต็มได้ในแอปข้อมูลโค้ด อย่างเป็นทางการ

// ...
    var selectedDate by remember { mutableStateOf<Long?>(null) }
// ...
        if (selectedDate != null) {
            val date = Date(selectedDate!!)
            val formattedDate = SimpleDateFormat("MMM dd, yyyy", Locale.getDefault()).format(date)
            Text("Selected date: $formattedDate")
        } else {
            Text("No date selected")
        }
// ...
        DatePickerModal(
            onDateSelected = {
                selectedDate = it
                showModal = false
            },
            onDismiss = { showModal = false }
        )
    }
// ...

โดยพื้นฐานแล้ว วิธีเดียวกันนี้ใช้ได้กับเครื่องมือเลือกวันที่แบบช่วง แม้ว่าคุณจะต้องใช้ Pair<Long?, Long?> หรือคลาสข้อมูลเพื่อเก็บค่าเริ่มต้นและค่าสิ้นสุด

ดูเพิ่มเติม