Окна выбора даты позволяют пользователям выбрать дату, диапазон дат или и то, и другое. Они используют диалоговое окно календаря или текстовое поле, чтобы пользователи могли выбирать даты.
Типы
Существует три типа окон выбора даты:
- Закрепленный. Показывается в макете. Оно подходит для компактных макетов, где отдельное диалоговое окно может показаться навязчивым.
- Модальное окно. Появляется в виде диалогового окна поверх контента приложения. Это позволяет пользователю сосредоточиться на выборе даты.
- Модальное окно ввода. Сочетает текстовое поле с модальным окном выбора даты.
Вы можете реализовать эти окна выбора даты в своем приложении, используя следующие composable-функции:
DatePicker– общая composable-функция для окна выбора даты. Тип контейнера определяет, будет ли он закрепленным или модальным.DatePickerDialog– контейнер для модального окна выбора даты и модального окна ввода даты.DateRangePicker– для любого окна выбора даты, в котором пользователь может указать диапазон с датами начала и окончания.
Штат
Ключевой параметр, общий для разных composable-функций окна выбора даты, – 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управляет видимостью закрепленного окна выбора даты.
- Кнопка со значком служит аргументом для параметра
- Контейнер окна выбора даты – это composable-функция
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выполняется, когда пользователь выбирает дату.- Она предоставляет выбранную дату родительской composable-функции.
- Лямбда-выражение
onDismissвыполняется, когда пользователь закрывает диалоговое окно.
Результаты
Вот как это выглядит:
Окно выбора даты в модальном окне ввода
Модальное окно выбора даты с вводом отображает диалоговое окно, которое плавает над экраном и позволяет пользователю ввести дату.
@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?>, представляющий выбранные даты начала и окончания. Это дает родительской composable-функции доступ к выбранному диапазону. rememberDateRangePickerState()создает состояние для выбора диапазона дат.DatePickerDialogсоздает контейнер модального диалогового окна.- В обработчике
onClickкнопки подтвержденияonDateRangeSelectedпередает выбранный диапазон родительской composable-функции. - Функция
DateRangePickerслужит контентом диалогового окна.
Результаты
Вот как это выглядит:
Использовать выбранную дату
Чтобы зафиксировать выбранную дату, отслеживайте ее в родительской composable-функции как Long и передавайте значение в DatePicker в onDateSelected. Ниже приведен пример кода. Полную версию можно найти в официальном приложении с примерами кода.
// ... 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 } ) } // ...
То же самое относится к окнам выбора диапазона дат, но для получения начального и конечного значений необходимо использовать Pair<Long?, Long?> или класс данных.