日期选择器 允许用户选择日期、日期范围或两者。它们使用日历对话框或文本输入来让用户选择日期。
类型
有三种类型的日期选择器
- 停靠式:出现在布局内联。它适用于紧凑的布局,在这些布局中,专用对话框可能会让人感觉侵入性。
- 模态:作为覆盖应用程序内容的对话框出现。这为日期选择提供了清晰的焦点。
- 模态输入:将文本字段与模态日期选择器结合使用。
您可以使用以下可组合项在您的应用中实现这些日期选择器
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
时,日期选择器将出现。- 图标按钮用作
OutlinedTextField
的trailingIcon
参数的参数。 showDatePicker
状态变量控制停靠式日期选择器的可见性。
- 图标按钮用作
- 日期选择器的容器是一个
Popup
可组合项,它覆盖内容而不影响其他元素的布局。 selectedDate
从DatePickerState
对象捕获所选日期的值,并使用convertMillisToDate
函数对其进行格式化。- 所选日期显示在文本字段中。
- 停靠式日期选择器使用
offset
修饰符放置在文本字段下方。 - 一个
Box
用作根容器以允许文本字段和日期选择器正确分层。
结果
点击日历图标后,此实现将显示如下
模态日期选择器
模态日期选择器显示一个浮动在屏幕上的对话框。要实现它,请创建一个 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
lambda 表达式在用户选择日期时执行。- 它将所选日期公开给父可组合项。
onDismiss
lambda 表达式在用户关闭对话框时执行。
结果
此实现将显示如下
输入模态日期选择器
带输入的模态日期选择器显示一个浮动在屏幕上的对话框,并允许用户输入日期。
@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
。
带范围的日期选择器
您可以创建一个允许用户选择开始日期和结束日期之间的范围的日期选择器。为此,请使用 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
可组合项用作对话框内容。
结果
此实现将显示如下
使用所选日期
要捕获所选日期,请在父可组合项中将其跟踪为 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?>
或数据类来捕获开始值和结束值。