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 im Layout Inline angezeigt. Sie eignet sich für kompakte Layouts, in denen ein spezielles Dialogfeld aufdringlich wirken könnte.
  • Modal: Wird als Dialogfeld über den App-Inhalten angezeigt. So wird der Fokus auf die Datumsauswahl gelenkt.
  • Modale Eingabe: Hier wird ein Textfeld mit einer modalen Datumsauswahl kombiniert.

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

  • DatePicker: Allgemeines Composeable für eine Datumsauswahl. Je nach Container wird das Gerät angedockt oder als Modell angezeigt.
  • DatePickerDialog: Der Container für modale und modale Eingabe-Datumsauswahlen.
  • DateRangePicker: Für jede Datumsauswahl, bei der der Nutzer einen Zeitraum mit einem Start- und Enddatum auswählen kann.

Bundesland

Der wichtigste Parameter, den die verschiedenen Datumsauswahl-Kompositionen gemeinsam haben, ist state. Er kann entweder ein DatePickerState- oder ein DateRangePickerState-Objekt annehmen. Mit ihren Properties werden Informationen zur Auswahl des Nutzers mithilfe der Datumsauswahl erfasst, 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 der Nutzer auf das Kalendersymbol im Feld klickt, 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.
    • Mit der Statusvariablen showDatePicker wird die Sichtbarkeit der angedockten Datumsauswahl gesteuert.
  • Der Container der Datumsauswahl ist ein Popup-kompositbares Element, das 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 convertMillisToDate-Funktion.
  • 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 das Textfeld und die Datumsauswahl richtig zu schichten.

Ergebnisse

Nach dem Klicken auf das Kalendersymbol sieht diese 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. Erstelle zur Implementierung ein DatePickerDialog und übergib ein DatePicker-Element.

@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 dem übergeordneten Composeable-Element zur Verfügung gestellt.
  • Der Lambda-Ausdruck onDismiss wird ausgeführt, wenn der Nutzer den Dialog schließt.

Ergebnisse

Diese Implementierung sieht so aus:

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

Modale Datumsauswahl

Eine modale Datumsauswahl mit Eingabefeld blendet ein Dialogfeld über dem Bildschirm ein, 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 im Grunde dasselbe wie beim Beispiel für eine modale Datumsauswahl. Der Hauptunterschied besteht darin:

  • 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 Nutzer einen Zeitraum zwischen einem Start- und einem Enddatum auswählen können. Verwenden Sie dazu DateRangePicker.

Die Verwendung von DateRangePicker entspricht im Wesentlichen der von 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 Informationen zum Code

  • Der Parameter onDateRangeSelected ist ein Rückruf, der einen Pair<Long?, Long?> empfängt, der 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 Auswahl des Zeitraums.
  • Mit DatePickerDialog wird ein modaler Dialogfeldcontainer erstellt.
  • Im onClick-Handler der Bestätigungsschaltfläche gibt onDateRangeSelected den ausgewählten Bereich an das übergeordnete Composeable weiter.
  • Das DateRangePicker-Komposit dient als Dialoginhalt.

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

Wenn Sie das ausgewählte Datum erfassen möchten, erfassen Sie es im übergeordneten Composeable als Long und übergeben Sie den Wert an die DatePicker in onDateSelected. Das folgende Snippet veranschaulicht das. Die vollständige Implementierung findest du 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 Wesentlichen gilt das Gleiche für Datumsauswahlen für Bereiche. Sie müssen jedoch eine Pair<Long?, Long?> oder eine Datenklasse verwenden, um die Start- und Endwerte zu erfassen.

Siehe auch