Bộ chọn ngày cho phép người dùng chọn một ngày, một phạm vi ngày hoặc cả hai. Các thành phần này sử dụng hộp thoại lịch hoặc phương thức nhập văn bản để cho phép người dùng chọn ngày.
Loại
Có 3 loại bộ chọn ngày:
- Đã gắn vào đế: Xuất hiện cùng dòng trong bố cục. Thành phần này phù hợp với bố cục nhỏ gọn, nơi hộp thoại chuyên dụng có thể gây cảm giác khó chịu.
- Hộp thoại: Xuất hiện dưới dạng hộp thoại phủ lên nội dung của ứng dụng. Điều này giúp người dùng tập trung rõ ràng vào việc chọn ngày.
- Nhập theo phương thức: Kết hợp 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 các bộ chọn ngày này trong ứng dụng bằng cách sử dụng các thành phần kết hợp sau:
DatePicker: Composable chung cho bộ chọn ngày. Vùng chứa mà bạn sử dụng sẽ xác định xem đó là vùng chứa được neo hay vùng chứa phương thức.DatePickerDialog: Vùng chứa cho cả bộ chọn ngày phương thức và bộ chọn ngày nhập phương thức.DateRangePicker: Đối với bất kỳ bộ chọn ngày nào mà người dùng có thể chọn một phạm vi có 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, lấy đối tượng DatePickerState hoặc DateRangePickerState. Các thuộc tính của chúng thu thập thông tin về lựa chọn của người dùng bằng bộ chọn ngày, chẳng hạn như ngày hiện được chọn.
Để biết thêm thông tin về cách sử dụng ngày đã chọn, hãy xem phần Sử dụng 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, một bộ chọn ngày cố định sẽ mở ra 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 ) } } } } } @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)) }
Các điểm chính về mã
- Bộ chọn ngày sẽ 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 tham số
trailingIconcủaOutlinedTextField. - Biến trạng thái
showDatePickerkiểm soát chế độ hiển thị của bộ chọn ngày được cố định.
- Nút biểu tượng đóng vai trò là đối số cho tham số
- 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 nội dung mà không ảnh hưởng đến bố cục của các thành phần khác. selectedDatethu thập giá trị của ngày đã chọn từ đối tượngDatePickerStatevà định dạng giá trị đó bằng hàmconvertMillisToDate.- Ngày đã chọn sẽ xuất hiện trong trường văn bản.
- Bộ chọn ngày được cố định vị trí bên dưới trường văn bản bằng cách sử dụng đối tượng sửa đổi
offset. Boxđược dùng làm vùng chứa gốc để cho phép phân lớp đúng cách trường văn bản và bộ chọn ngày.
Kết quả
Sau khi nhấp vào biểu tượng lịch, phương thức triển khai này sẽ có dạng như sau:
Bộ chọn ngày theo phương thức
Bộ chọn ngày theo phương thức hiển thị một hộp thoại nổi trên màn hình. Để triển khai, hãy tạo một DatePickerDialog và truyền 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) } }
Các điểm chính về mã
- Hàm composable
DatePickerModalhiển thị một bộ chọn ngày theo phương thức. - Biểu thức lambda
onDateSelectedsẽ thực thi khi người dùng chọn một ngày.- Thành phần này hiển thị ngày đã chọn cho thành phần kết hợp mẹ.
- Biểu thức lambda
onDismisssẽ thực thi khi người dùng đóng hộp thoại.
Kết quả
Phương thức triển khai có dạng như sau:
Bộ chọn ngày theo phương thức nhập
Bộ chọn ngày phương thức có dữ liệu đầ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) } }
Các điểm chính về mã
Cách này rất giống với ví dụ về bộ chọn ngày theo phương thức. Điểm khác biệt chính là:
- Tham số
initialDisplayModeđặt chế độ hiển thị ban đầu thànhDisplayMode.Input.
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 giữa ngày bắt đầu và ngày kết thúc. Để làm việc này, hãy sử dụng DateRangePicker.
Cách sử dụng DateRangePicker về cơ bản giống như DatePicker. Bạn có thể
sử dụng thành phần này cho bộ chọn được neo làm thành phần con của PopUp hoặc bạn có thể sử dụng thành phần này làm
bộ chọn phương thức và truyền thành phần này đến DatePickerDialog. Sự khác biệt chính là 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 theo phương thức có phạm vi:
@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ố
onDateRangeSelectedlà một lệnh gọi lại nhậnPair<Long?, Long?>đại diện cho ngày bắt đầu và ngày kết thúc đã chọn. Thao tác này cho phép thành phần kết hợp mẹ truy cập vào phạm vi đã chọn. rememberDateRangePickerState()tạo trạng thái cho bộ chọn phạm vi ngày.DatePickerDialogtạo một vùng chứa hộp thoại phụ trợ.- Trong trình xử lý
onClickcủa nút xác nhận,onDateRangeSelectedsẽ truyền phạm vi đã chọn lên 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:
Sử dụng ngày đã chọn
Để ghi lại 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 đây minh hoạ điều này, mặc dù bạn có thể xem cách triển khai đầy đủ trong ứng dụng đoạn mã chính thức.
// ... var selectedDate by remember { mutableStateOf<Long?>(null) } // ... if (selectedDate != null) { val date = Date(selectedDate!!) val formattedDate = SimpleDateFormat("MMM dd, yyyy", LocalLocale.current.platformLocale).format(date) Text("Selected date: $formattedDate") } else { Text("No date selected") } // ... DatePickerModal( onDateSelected = { selectedDate = it showModal = false }, onDismiss = { showModal = false } ) } // ...
Về cơ bản, điều tương tự cũng áp dụng cho bộ chọn phạm vi ngày, mặc dù bạn cần sử dụng Pair<Long?, Long?> hoặc lớp dữ liệu để ghi lại giá trị bắt đầu và kết thúc.