日期挑選器可讓使用者選取日期、日期範圍或同時選取二者。這些 API 會使用日曆對話方塊或文字輸入功能,讓使用者選取日期。
類型
日期挑選器分為三種類型:
- 已固定:在版面配置中內嵌顯示。這個選項適合用於精簡版面配置,因為在這種情況下,專屬對話方塊可能會造成干擾。
- 模態:以對話方塊形式顯示,並疊加在應用程式內容上。這可讓使用者清楚專注於日期選取作業。
- 模態輸入:結合文字欄位和模態日期挑選器。
您可以使用下列可組合項,在應用程式中實作這些日期挑選器:
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
的形式追蹤日期,並將值傳遞至 onDateSelected
中的 DatePicker
。下列程式碼片段可說明這一點,不過您也可以在官方程式碼片段應用程式中查看完整的實作方式。
// ... 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?>
或資料類別擷取開始和結束值。