Datumsauswahl

Mit Datumsauswahlen können Nutzer ein Datum, einen Zeitraum oder beides auswählen. Sie verwenden ein Kalenderdialogfeld oder eine Texteingabe, damit Nutzer Datumsangaben auswählen können.

Typen

Es gibt drei Arten von Datumsauswahlen:

  • Angedockt: Wird inline im Layout angezeigt. Sie eignet sich für kompakte Layouts, in denen ein separates Dialogfeld aufdringlich wirken könnte.
  • Modal: Wird als Dialogfeld über dem Inhalt der App angezeigt. So wird der Fokus klar auf die Datumsauswahl gelegt.
  • Modale Eingabe: Hier wird ein Textfeld mit einer modalen Datumsauswahl kombiniert.

Sie können diese Datumsauswahlen in Ihrer App mit den folgenden Composables implementieren:

  • DatePicker: Allgemeine zusammensetzbare Funktion für eine Datumsauswahl. Der verwendete Container bestimmt, ob er angedockt oder modal ist.
  • DatePickerDialog: Der Container für modale und modale Eingabe-Datumsauswahlen.
  • DateRangePicker: Für alle Datumsauswahlen, in denen der Nutzer einen Zeitraum mit Start- und Enddatum auswählen kann.

Bundesland

Der Schlüsselparameter, den die verschiedenen Composables für die Datumsauswahl gemeinsam haben, ist state. Er akzeptiert entweder ein DatePickerState- oder ein DateRangePickerState-Objekt. Ihre Eigenschaften erfassen Informationen zur Auswahl des Nutzers über die Datumsauswahl, z. B. das aktuell ausgewählte Datum.

Weitere Informationen zur Verwendung des ausgewählten Datums finden Sie im Abschnitt Ausgewähltes Datum verwenden.

Angedockte Datumsauswahl

Im folgenden Beispiel wird der Nutzer in einem Textfeld aufgefordert, sein Geburtsdatum einzugeben. Wenn sie im Feld auf das Kalendersymbol klicken, wird unter dem Eingabefeld eine angedockte Datumsauswahl geöffnet.

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

Wichtige Punkte zum Code

  • Die Datumsauswahl wird angezeigt, wenn der Nutzer auf IconButton klickt.
    • Die Symbolschaltfläche dient als Argument für den trailingIcon-Parameter von OutlinedTextField.
    • Die Statusvariable showDatePicker steuert die Sichtbarkeit der angedockten Datumsauswahl.
  • Der Container der Datumsauswahl ist eine Popup-Composable, die den Inhalt überlagert, ohne das Layout anderer Elemente zu beeinträchtigen.
  • selectedDate erfasst den Wert des ausgewählten Datums aus dem DatePickerState-Objekt und formatiert ihn mit der Funktion convertMillisToDate.
  • Das ausgewählte Datum wird im Textfeld angezeigt.
  • Die angedockte Datumsauswahl wird mit dem Modifikator offset unter dem Textfeld positioniert.
  • Ein Box wird als Stammcontainer verwendet, um eine korrekte Schichtung des Textfelds und der Datumsauswahl zu ermöglichen.

Ergebnisse

Nachdem Sie auf das Kalendersymbol geklickt haben, sieht diese Implementierung so aus:

Beispiel für eine angedockte Datumsauswahl
Abbildung 1: Eine angedockte Datumsauswahl.

Bei einer modalen Datumsauswahl wird ein Dialogfeld angezeigt, das über dem Bildschirm schwebt. Erstellen Sie dazu ein DatePickerDialog und übergeben Sie ihm ein 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)
    }
}

  • Mit der zusammensetzbaren Funktion DatePickerModal wird eine modale Datumsauswahl angezeigt.
  • Der onDateSelected-Lambda-Ausdruck wird ausgeführt, wenn der Nutzer ein Datum auswählt.
    • Das ausgewählte Datum wird für die übergeordnete Composable-Funktion verfügbar gemacht.
  • Der Lambda-Ausdruck onDismiss wird ausgeführt, wenn der Nutzer das Dialogfeld schließt.

Ergebnisse

Diese Implementierung sieht so aus:

Beispiel für eine modale Datumsauswahl
Abbildung 2. Eine modale Datumsauswahl.

Eingabemodale Datumsauswahl

Bei einer modalen Datumsauswahl mit Eingabe wird ein Dialogfeld angezeigt, das über dem Bildschirm schwebt und in dem der Nutzer ein Datum eingeben kann.

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

Das ist dem Beispiel für die modale Datumsauswahl sehr ähnlich. Der Hauptunterschied ist folgender:

  • Mit dem Parameter initialDisplayMode wird der anfängliche Anzeigemodus auf DisplayMode.Input festgelegt.
Modale Datumsauswahl mit Eingabe.
Abbildung 3. Eine modale Datumsauswahl mit Eingabe.

Datumsauswahl mit Zeitraum

Sie können eine Datumsauswahl erstellen, mit der der Nutzer einen Zeitraum zwischen einem Start- und einem Enddatum auswählen kann. Verwenden Sie dazu DateRangePicker.

Die Verwendung von DateRangePicker ist im Wesentlichen dieselbe wie bei DatePicker. Sie können sie für eine angedockte Auswahl als untergeordnetes Element von PopUp verwenden oder als modale Auswahl verwenden und an DatePickerDialog übergeben. Der Hauptunterschied besteht darin, dass Sie DateRangePickerState anstelle von DatePickerState verwenden.

Das folgende Snippet zeigt, wie Sie eine modale Datumsauswahl mit einem Bereich erstellen:

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

Wichtige Punkte zum Code

  • Der Parameter onDateRangeSelected ist ein Callback, der ein Pair<Long?, Long?> empfängt, das die ausgewählten Start- und Enddaten darstellt. Dadurch erhält die übergeordnete Composable-Funktion Zugriff auf den ausgewählten Bereich.
  • rememberDateRangePickerState() erstellt den Status für die Datumsbereichsauswahl.
  • Mit DatePickerDialog wird ein modaler Dialogcontainer erstellt.
  • Im onClick-Handler der Bestätigungsschaltfläche übergibt onDateRangeSelected den ausgewählten Bereich an die übergeordnete zusammensetzbare Funktion.
  • Das DateRangePicker-Composable dient als Dialogfeldinhalt.

Ergebnisse

Diese Implementierung sieht so aus:

Beispiel für eine modale Zeitraumauswahl
Abbildung 4: Eine modale Datumsauswahl mit einem ausgewählten Zeitraum.

Ausgewähltes Datum verwenden

Um das ausgewählte Datum zu erfassen, verfolgen Sie es im übergeordneten Composable als Long und übergeben Sie den Wert an DatePicker in onDateSelected. Das folgende Snippet veranschaulicht dies. Die vollständige Implementierung finden Sie in der offiziellen Snippets-App.

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

Im Grunde gilt dasselbe für Datumsbereichsauswahlen. Hier müssen Sie jedoch eine Pair<Long?, Long?> oder eine Datenklasse verwenden, um die Start- und Endwerte zu erfassen.

Siehe auch