Selettori della data

I selettori della data consentono agli utenti di selezionare una data, un intervallo di date o entrambi. Utilizzano una finestra di dialogo del calendario o l'inserimento di testo per consentire agli utenti di selezionare le date.

Tipi

Esistono tre tipi di selettori della data:

  • Agganciato: viene visualizzato in linea all'interno del layout. È adatta a layout compatti in cui una finestra di dialogo dedicata potrebbe risultare intrusiva.
  • Modale: viene visualizzato come finestra di dialogo che si sovrappone ai contenuti dell'app. In questo modo, l'attenzione è chiaramente rivolta alla selezione della data.
  • Input modale: combina un campo di testo con un selettore della data modale.

Puoi implementare questi selettori di date nella tua app utilizzando i seguenti composables:

  • DatePicker: Componente componibile generale per un selettore della data. Il contenitore che utilizzi determina se è agganciato o modello.
  • DatePickerDialog: il contenitore per i selettori di data modali e di input modali.
  • DateRangePicker: per qualsiasi selettore della data in cui l'utente può selezionare un intervallo con una data di inizio e una data di fine.

Stato

Il parametro chiave che i diversi composable per la selezione della data condividono è state, che accetta un oggetto DatePickerState o DateRangePickerState. Le loro proprietà acquisiscono informazioni sulla selezione dell'utente utilizzando il selettore della data, ad esempio la data selezionata corrente.

Per saperne di più su come utilizzare la data selezionata, consulta la sezione Utilizzare la data selezionata.

Selettore della data agganciato

Nell'esempio seguente, è presente un campo di testo che chiede all'utente di inserire la propria data di nascita. Quando fanno clic sull'icona del calendario nel campo, si apre un selettore di date ancorato sotto il campo di input.

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

Punti chiave del codice

  • Il selettore della data viene visualizzato quando l'utente fa clic su IconButton.
    • Il pulsante icona funge da argomento per il parametro OutlinedTextField trailingIcon.
    • La variabile di stato showDatePicker controlla la visibilità del selettore della data agganciato.
  • Il contenitore del selettore della data è un componente componibile Popup che si sovrappone ai contenuti senza influire sul layout degli altri elementi.
  • selectedDate acquisisce il valore della data selezionata dall'oggetto DatePickerState e lo formatta utilizzando la funzione convertMillisToDate.
  • La data selezionata viene visualizzata nel campo di testo.
  • Il selettore date agganciato è posizionato sotto il campo di testo utilizzando un modificatore offset.
  • Un Box viene utilizzato come contenitore principale per consentire la corretta stratificazione del campo di testo e del selettore della data.

Risultati

Dopo aver fatto clic sull'icona del calendario, questa implementazione viene visualizzata come segue:

Esempio di selettore date ancorato.
Figura 1. Un selettore date agganciato.

Un selettore della data modale mostra una finestra di dialogo che fluttua sullo schermo. Per implementarlo, crea un DatePickerDialog e trasmettigli un 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)
    }
}

  • La funzione componibile DatePickerModal mostra un selettore della data modale.
  • L'espressione lambda onDateSelected viene eseguita quando l'utente seleziona una data.
    • Espone la data selezionata al composable padre.
  • L'espressione lambda onDismiss viene eseguita quando l'utente chiude la finestra di dialogo.

Risultati

Questa implementazione viene visualizzata nel seguente modo:

Esempio di selettore date modale.
Figura 2. Un selettore della data modale.

Selettore della data modale di input

Un selettore della data modale con input mostra una finestra di dialogo che fluttua sullo schermo e consente all'utente di inserire una data.

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

È molto simile all'esempio di selettore di date modale. La differenza principale è la seguente:

  • Il parametro initialDisplayMode imposta la modalità di visualizzazione iniziale su DisplayMode.Input.
Selettore date modale con input.
Figura 3. Un selettore date modale con input.

Selettore date con intervallo

Puoi creare un selettore della data che consenta all'utente di selezionare un intervallo tra una data di inizio e una data di fine. Per farlo, utilizza DateRangePicker.

L'utilizzo di DateRangePicker è essenzialmente lo stesso di DatePicker. Puoi utilizzarlo per un selettore agganciato come elemento secondario di PopUp oppure come selettore modale e passarlo a DatePickerDialog. La differenza principale è che utilizzi DateRangePickerState anziché DatePickerState.

Il seguente snippet mostra come creare un selettore della data modale con un intervallo:

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

Punti chiave del codice

  • Il parametro onDateRangeSelected è un callback che riceve un Pair<Long?, Long?> che rappresenta le date di inizio e di fine selezionate. In questo modo il componente genitore ha accesso alla gamma selezionata.
  • rememberDateRangePickerState() crea lo stato per il selettore dell'intervallo di date.
  • DatePickerDialog crea un contenitore di finestre di dialogo modali.
  • Nel gestore onClick del pulsante di conferma, onDateRangeSelected passa l'intervallo selezionato al composable principale.
  • Il composable DateRangePicker funge da contenuto della finestra di dialogo.

Risultati

Questa implementazione viene visualizzata nel seguente modo:

Esempio di selettore della data dell&#39;intervallo modale.
Figura 4. Un selettore della data modale con un intervallo selezionato.

Utilizza la data selezionata

Per acquisire la data selezionata, monitorala nel composable principale come Long e trasferisci il valore a DatePicker in onDateSelected. Il seguente snippet lo dimostra, anche se puoi vedere l'implementazione completa nell'app ufficiale degli snippet.

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

Lo stesso vale per i selettori di date, anche se devi utilizzare un Pair<Long?, Long?> o una classe di dati per acquisire i valori di inizio e fine.

Vedi anche