חלונית לבחירת תאריך

חלונית לבחירת תאריך מאפשרת למשתמשים לבחור תאריך, טווח תאריכים או את שניהם. הם משתמשים בתיבת דו-שיח של יומן או בהזנת טקסט כדי לאפשר למשתמשים לבחור תאריכים.

סוגים

יש שלושה סוגים של בוררי תאריכים:

  • מחובר לאביזר עגינה: מופיע בתוך השורה. הוא מתאים לפריסות קומפקטיות שבהן תיבת דו-שיח ייעודית עשויה להפריע.
  • מודולרי: מוצג כתיבת דו-שיח שמכסה את תוכן האפליקציה. כך אפשר להתמקד בבחירת התאריכים.
  • קלט מודלי: שילוב של שדה טקסט עם חלונית לבחירת תאריך מודל.

אפשר להטמיע את בוחרי התאריכים באפליקציה באמצעות תכנים קומפוזביליים:

  • DatePicker: רכיב מורכב כללי לבורר תאריכים. מאגר התגים קובעים אם הוא מחובר לאביזר עגינה או לדגם.
  • DatePickerDialog: הקונטיינר של בוררי התאריכים גם בחלון קופץ וגם בחלון קופץ להזנת נתונים.
  • DateRangePicker: בכל חלונית לבחירת תאריך שבה המשתמש יכול לבחור טווח עם תאריך התחלה וסיום.

מדינה

פרמטר המפתח המשותף של הקומפוזביליות הקומפוזביליות השונות של בורר התאריכים הוא state, שלוקח DatePickerState או אובייקט DateRangePickerState. המאפיינים שלהם מתעדים מידע על הבחירה של המשתמש באמצעות חלונית לבחירת תאריכים, כמו התאריך הנוכחי שנבחר.

מידע נוסף על האופן שבו אפשר להשתמש בתאריך שנבחר זמין בקטע שימוש בתאריך שנבחר.

חלונית לבחירת תאריך בעגינה

בדוגמה הבאה יש שדה טקסט שמבקש מהמשתמש להזין את תאריך הלידה שלהם. כשלוחצים על סמל היומן בשדה, נפתח בורר תאריכים מוצמדת מתחת לשדה הקלט.

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

נקודות עיקריות לגבי הקוד

  • החלונית לבחירת תאריך מופיעה כשהמשתמש לוחץ על IconButton.
    • לחצן הסמל משמש כארגומנט לפרמטר trailingIcon של OutlinedTextField.
    • משתנה המצב showDatePicker קובע את החשיפה של חלונית הבחירה של התאריך.
  • המאגר של בוחר התאריכים הוא תוכן קומפוזבילי מסוג Popup, שמוסיף שכבת-על בלי להשפיע על הפריסה של רכיבים אחרים.
  • selectedDate מתעד את הערך של התאריך שנבחר מהאובייקט DatePickerState ומעצב אותו באמצעות הפונקציה convertMillisToDate.
  • התאריך שנבחר מופיע בשדה הטקסט.
  • החלונית לבחירת תאריך בעגינה מוצבת מתחת לשדה הטקסט באמצעות offset מגביל.
  • השדה Box משמש כמאגר הבסיס כדי לאפשר שכבת-על מתאימה של הטקסט ואת החלונית לבחירת תאריך.

תוצאות

לאחר לחיצה על סמל היומן, היישום הזה מופיע כך:

דוגמה לחלונית לבחירת תאריך מוצמדת.
איור 1. חלונית לבחירת תאריך בטעינה.

חלונית לבחירת תאריך מודלים מציגה תיבת דו-שיח שמצפה על המסך. כדי ליישם את: אותו, יוצרים DatePickerDialog ומעבירים לו 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)
    }
}

  • הפונקציה הקומפוזבילית DatePickerModal מציגה חלונית לבחירת תאריך מודאלי.
  • ביטוי lambda onDateSelected מופעל כשהמשתמש בוחר תאריך.
    • הוא חושף את התאריך שנבחר לרכיב ההורה הניתן לקישור.
  • ביטוי lambda onDismiss מופעל כשהמשתמש סוגר את

תוצאות

היישום הזה נראה כך:

דוגמה לכלי לבחירת תאריך במודל.
איור 2. חלונית לבחירת תאריך בחלון מודאלי.

חלונית קלט מודלית לבחירת תאריך

בורר תאריכים מודאלי עם קלט מציג תיבת דו-שיח שצפה מעל המסך ומאפשרת למשתמש להזין תאריך.

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

אופן הפעולה הזה דומה מאוד לדוגמה של בוחר התאריכים בחלון העזר. ההבדל העיקרי הוא:

  • הפרמטר initialDisplayMode מגדיר את מצב התצוגה הראשוני כ- DisplayMode.Input.
חלונית לבחירת תאריך מודל עם קלט.
איור 3. חלונית לבחירת תאריך במסך עם קלט.

חלונית לבחירת תאריך עם טווח

ניתן ליצור חלונית לבחירת תאריך שמאפשרת למשתמש לבחור טווח בין תאריך ההתחלה ותאריך הסיום. כדי לעשות זאת, משתמשים בפקודה DateRangePicker.

השימוש ב-DateRangePicker זהה בעיקרו לשימוש ב-DatePicker. אפשר אפשר להשתמש בו לבורר בעגינה כילד של PopUp, או להשתמש בו כלי לבחירת פריט ומעביר אותו אל DatePickerDialog. ההבדל העיקרי הוא אתם משתמשים ב-DateRangePickerState במקום ב-DatePickerState.

קטע הקוד הבא מדגים איך יוצרים חלונית לבחירת תאריך של מודל טווח:

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

נקודות עיקריות לגבי הקוד

  • הפרמטר onDateRangeSelected הוא פונקציית קריאה חוזרת שמקבלת את הערך Pair<Long?, Long?> שמייצג את תאריכי ההתחלה והסיום שנבחרו. כך מתקבלת גישה של הורה ליצירת קומפוזיציות לטווח שנבחר.
  • rememberDateRangePickerState() יוצר את המצב של טווח התאריכים בוחר.
  • הרכיב DatePickerDialog יוצר קונטיינר של תיבת דו-שיח של חלון.
  • בטיפול onClick של לחצן האישור, onDateRangeSelected מעביר את הטווח שנבחר לרכיב ההורה הניתן לקישור.
  • הרכיב הניתן לקישור DateRangePicker משמש כתוכן של תיבת הדו-שיח.

תוצאות

היישום הזה נראה כך:

דוגמה לחלונית לבחירת טווח תאריכים בעיצוב מודאלי.
איור 4. חלון דו-שיח לבחירת תאריך עם טווח שנבחר.

שימוש בתאריך שנבחר

כדי לתעד את התאריך שנבחר, עוקבים אחריו ברכיב ההורה הניתן לקישור כ-Long ומעבירים את הערך ל-DatePicker ב-onDateSelected. קטע הקוד הבא מדגים זאת, אבל אפשר לראות את ההטמעה המלאה באפליקציה הרשמית של קטעי הקוד.

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

אותו הדבר רלוונטי לבוררי תאריכים בטווח, אבל צריך להשתמש ב-Pair<Long?, Long?> או בסוג נתונים כדי לתעד את ערכי ההתחלה והסיום.

למידע נוסף