Datumsauswahl

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

Typen

Es gibt drei Arten von Datumsauswahl-Elementen:

  • Angedockt: Wird inline im Layout angezeigt. Sie eignet sich für kompakte Layouts, bei denen ein spezielles 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 Datumsauswahl in Ihrer App mit den folgenden Composables implementieren:

  • DatePicker: Allgemeines Compose-Element für eine Datumsauswahl. Der von Ihnen verwendete Container bestimmt, ob es sich um ein angedocktes oder ein modales Fenster handelt.
  • DatePickerDialog: Der Container für die Datumsauswahl im modalen und modalen Eingabemodus.
  • DateRangePicker: Für jede Datumsauswahl, bei 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 gibt es ein Textfeld, in das der Nutzer sein Geburtsdatum eingeben soll. 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 Parameter trailingIcon von OutlinedTextField.
    • Die Statusvariable showDatePicker steuert die Sichtbarkeit der angedockten Datumsauswahl.
  • Der Container der Datumsauswahl ist ein Popup-Composable, das den Inhalt überlagert, ohne das Layout anderer Elemente zu beeinflussen.
  • 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-Modifikator 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 die Implementierung so aus:

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

Eine modale Datumsauswahl zeigt ein Dialogfeld an, das über dem Bildschirm schwebt. Erstellen Sie dazu ein DatePickerDialog und übergeben Sie 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)
    }
}

  • Die zusammensetzbare Funktion DatePickerModal zeigt eine modale Datumsauswahl an.
  • 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.

Modale Datumsauswahl für Eingabe

Eine modale Datumsauswahl mit Eingabe zeigt ein Dialogfeld an, das über dem Bildschirm schwebt und es dem Nutzer ermöglicht, ein Datum einzugeben.

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

Dies 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 mit der von DatePicker identisch. Sie können sie für eine angedockte Auswahl als untergeordnetes Element von PopUp 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?>-Objekt empfängt, das die ausgewählten Start- und Enddaten darstellt. Dadurch erhält die übergeordnete zusammensetzbare Funktion Zugriff auf den ausgewählten Bereich.
  • rememberDateRangePickerState() erstellt den Status für die Datumsauswahl.
  • Mit DatePickerDialog wird ein modaler Dialogfeldcontainer erstellt.
  • Im onClick-Handler des Bestätigungs-Buttons übergibt onDateRangeSelected den ausgewählten Bereich an das übergeordnete Composable.
  • Die zusammensetzbare Funktion DateRangePicker dient als Dialogfeldinhalt.

Ergebnisse

Diese Implementierung sieht so aus:

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

Ausgewähltes Datum verwenden

Um das ausgewählte Datum zu erfassen, tracken Sie es in der übergeordneten zusammensetzbaren Funktion 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 Datumsauswahl-Elemente für Zeiträume, allerdings müssen Sie ein Pair<Long?, Long?> oder eine Datenklasse verwenden, um die Start- und Endwerte zu erfassen.

Weitere Informationen