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: Kombiniert ein Textfeld mit einer modalen Datumsauswahl.

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 nicht modale Datumsauswahlen.
  • DateRangePicker: Für jede Datumsauswahl, in der der Nutzer einen Zeitraum mit einem Start- und Enddatum auswählen kann.

Status

Der wichtigste Parameter, 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 auf das Kalendersymbol im Feld 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 einem offset-Modifier 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 der 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 Lambda-Ausdruck onDateSelected wird ausgeführt, wenn der Nutzer ein Datum auswählt.
    • Das ausgewählte Datum wird für die übergeordnete zusammensetzbare 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.

Datumsauswahl für die Eingabe

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 Bereich

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?> mit den ausgewählten Start- und Enddaten empfängt. Dadurch erhält die übergeordnete Composable Zugriff auf den ausgewählten Bereich.
  • rememberDateRangePickerState() erstellt den Status für die Auswahl des Datumsbereichs.
  • Mit DatePickerDialog wird ein modaler Dialogcontainer erstellt.
  • Im onClick-Handler der Schaltfläche „Bestätigen“ übergibt onDateRangeSelected den ausgewählten Bereich an die übergeordnete zusammensetzbare Funktion.
  • Die DateRangePicker-Composable-Funktion 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", LocalLocale.current.platformLocale).format(date)
            Text("Selected date: $formattedDate")
        } else {
            Text("No date selected")
        }
// ...
        DatePickerModal(
            onDateSelected = {
                selectedDate = it
                showModal = false
            },
            onDismiss = { showModal = false }
        )
    }
// ...

Im Wesentlichen 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.

Weitere Informationen