Bộ chọn ngày

Bộ chọn ngày cho phép người dùng chọn ngày, phạm vi ngày hoặc cả hai. Họ sử dụng hộp thoại lịch hoặc đầu vào văn bản để cho phép người dùng chọn ngày.

Loại

Có ba loại bộ chọn ngày:

  • Gắn vào vị trí cố định: Xuất hiện cùng dòng trong bố cục. Phù hợp với máy ảnh nhỏ gọn các bố cục mà hộp thoại chuyên dụng có thể gây cảm giác xâm nhập.
  • Modal: Xuất hiện dưới dạng một hộp thoại phủ lên nội dung của ứng dụng. Điều này mang lại hãy tập trung rõ ràng vào việc chọn ngày.
  • Phương thức nhập mô-đun: Kết hợp một trường văn bản với bộ chọn ngày theo phương thức.

Bạn có thể triển khai bộ chọn ngày này trong ứng dụng của mình bằng cách sử dụng các cách sau thành phần kết hợp:

  • DatePicker: Thành phần kết hợp chung cho bộ chọn ngày. Vùng chứa mà bạn sử dụng xác định liệu thiết bị được gắn vào đế hay kiểu máy.
  • DatePickerDialog: Vùng chứa cho cả ngày nhập phương thức và phương thức bộ chọn.
  • DateRangePicker: Đối với bất kỳ bộ chọn ngày nào mà người dùng có thể chọn ngày bắt đầu và ngày kết thúc.

Trạng thái

Tham số chính mà các thành phần kết hợp bộ chọn ngày khác nhau có chung là state sẽ lấy DatePickerState hoặc DateRangePickerState. Thuộc tính của họ thu thập thông tin về lựa chọn của người dùng bằng cách sử dụng bộ chọn ngày, chẳng hạn như ngày hiện đã chọn.

Để biết thêm thông tin về cách bạn có thể sử dụng ngày đã chọn, hãy xem phần Sử dụng phần ngày đã chọn.

Bộ chọn ngày được cố định

Trong ví dụ sau, có một trường văn bản nhắc người dùng nhập ngày sinh của họ. Khi họ nhấp vào biểu tượng lịch trong trường này, được gắn vào bộ chọn ngày bên dưới trường nhập dữ liệu.

@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
                    )
                }
            }
        }
    }
}

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

Các điểm chính về mã

  • Bộ chọn ngày xuất hiện khi người dùng nhấp vào IconButton.
    • Nút biểu tượng đóng vai trò là đối số cho OutlinedTextField Tham số trailingIcon.
    • Biến trạng thái showDatePicker kiểm soát chế độ hiển thị của bộ chọn ngày được gắn vào đế sạc.
  • Vùng chứa của bộ chọn ngày là một thành phần kết hợp Popup, phủ lên mà không ảnh hưởng đến bố cục của các phần tử khác.
  • selectedDate ghi lại giá trị của ngày đã chọn trong đối tượng DatePickerState và định dạng đối tượng đó bằng convertMillisToDate .
  • Ngày đã chọn sẽ xuất hiện trong trường văn bản.
  • Bộ chọn ngày được gắn vào vị trí bên dưới trường văn bản bằng offset đối tượng sửa đổi.
  • Box được dùng làm vùng chứa gốc để cho phép phân lớp văn bản thích hợp và bộ chọn ngày.

Kết quả

Sau khi bạn nhấp vào biểu tượng lịch, quá trình triển khai sẽ như sau:

Ví dụ về bộ chọn ngày được cố định.
Hình 1. Bộ chọn ngày được gắn vào đế sạc.

Bộ chọn ngày theo cửa sổ phụ sẽ hiển thị một hộp thoại nổi trên màn hình. Để triển khai nó, hãy tạo DatePickerDialog rồi truyền vào đó 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)
    }
}

  • Hàm có khả năng kết hợp DatePickerModal cho thấy một bộ chọn ngày ở chế độ.
  • Biểu thức lambda onDateSelected thực thi khi người dùng chọn một ngày.
    • Lớp này hiển thị ngày đã chọn cho thành phần kết hợp mẹ.
  • Biểu thức lambda onDismiss thực thi khi người dùng đóng biểu thức .

Kết quả

Phương thức triển khai có dạng như sau:

Ví dụ về bộ chọn ngày mô-đun.
Hình 2. Bộ chọn ngày ở chế độ phụ.

Bộ chọn ngày cho cửa sổ phụ

Bộ chọn ngày ở chế độ có thông tin đầu vào sẽ hiển thị một hộp thoại nổi trên màn hình và cho phép người dùng nhập ngày.

@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)
    }
}

Thao tác này rất giống với ví dụ về bộ chọn ngày phụ. Chính như sau:

  • Tham số initialDisplayMode đặt chế độ hiển thị ban đầu thành DisplayMode.Input.
Bộ chọn ngày mô-đun có thông tin đầu vào.
Hình 3. Bộ chọn ngày ở chế độ có dữ liệu đầu vào.

Bộ chọn ngày có phạm vi

Bạn có thể tạo một bộ chọn ngày cho phép người dùng chọn một phạm vi ngày bắt đầu và ngày kết thúc. Để thực hiện việc này, hãy sử dụng DateRangePicker.

Việc sử dụng DateRangePicker về cơ bản giống như DatePicker. Bạn có thể sử dụng nó cho một bộ chọn được gắn cố định làm phần tử con của PopUp, hoặc bạn có thể dùng nó làm một bộ chọn phương thức rồi truyền đến DatePickerDialog. Điểm khác biệt chính là mà bạn sử dụng DateRangePickerState thay vì DatePickerState.

Đoạn mã sau đây minh hoạ cách tạo bộ chọn ngày ở chế độ phụ thuộc vào dải ô:

@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)
        )
    }
}

Các điểm chính về mã

  • Tham số onDateRangeSelected là một lệnh gọi lại nhận một Pair<Long?, Long?> đại diện cho ngày bắt đầu và ngày kết thúc đã chọn. Chiến dịch này cấp cho thành phần kết hợp mẹ quyền truy cập vào dải ô đã chọn.
  • rememberDateRangePickerState() tạo trạng thái cho phạm vi ngày bộ chọn.
  • DatePickerDialog tạo một vùng chứa hộp thoại phương thức.
  • Trong trình xử lý onClick của nút xác nhận, onDateRangeSelected sẽ chuyển lên dải ô đã chọn cho thành phần kết hợp mẹ.
  • Thành phần kết hợp DateRangePicker đóng vai trò là nội dung hộp thoại.

Kết quả

Phương thức triển khai có dạng như sau:

Ví dụ về bộ chọn ngày của phạm vi mô-đun.
Hình 4. Bộ chọn ngày cửa sổ phụ với phạm vi ngày đã chọn.

Sử dụng ngày đã chọn

Để nắm bắt ngày đã chọn, hãy theo dõi ngày đó trong thành phần kết hợp mẹ dưới dạng Long và truyền giá trị đến DatePicker trong onDateSelected. Đoạn mã sau minh hoạ điều này, mặc dù bạn có thể xem cách triển khai đầy đủ trong tệp chính thức đoạn mã ứng dụng.

// ...
    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 }
        )
    }
// ...

Về cơ bản, công cụ này áp dụng tương tự cho bộ chọn ngày theo phạm vi, mặc dù bạn cần sử dụng Pair<Long?, Long?> hoặc lớp dữ liệu để thu thập giá trị bắt đầu và giá trị kết thúc.

Xem thêm