منتقي التاريخ

تتيح أدوات اختيار التاريخ للمستخدمين اختيار تاريخ أو نطاق زمني أو كليهما. ويستخدمون مربّع حوار تقويم أو إدخال نصي للسماح للمستخدمين باختيار التواريخ.

الأنواع

هناك ثلاثة أنواع من أدوات اختيار التاريخ:

  • مثبَّتة: تظهر مضمّنة في التنسيق. وهي مناسبة للتنسيقات المدمجة التي قد يبدو فيها مربّع الحوار المخصّص مزعجًا.
  • النافذة المشروطة: تظهر كنافذة حوارية تغطي محتوى التطبيق. ويوفّر ذلك تركيزًا واضحًا على اختيار التاريخ.
  • الإدخال المشروط: يجمع بين حقل نصي وأداة اختيار التاريخ المشروطة.

يمكنك تنفيذ أدوات اختيار التاريخ هذه في تطبيقك باستخدام العناصر القابلة للإنشاء التالية:

  • ‫DatePicker: دالة مركّبة عامة لأداة اختيار التاريخ. تحدّد الحاوية التي تستخدمها ما إذا كانت ثابتة أو عائمة.
  • ‫DatePickerDialog: الحاوية الخاصة بكل من أدوات اختيار التاريخ المنبثقة وأدوات اختيار التاريخ المنبثقة التي تتطلّب إدخال بيانات.
  • ‫DateRangePicker: لأي أداة اختيار تاريخ يمكن للمستخدم من خلالها تحديد نطاق يتضمّن تاريخ بدء وتاريخ انتهاء.

الولاية

المَعلمة الأساسية التي تشترك فيها عناصر Date Picker القابلة للإنشاء المختلفة هي 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.
    • يعمل زر الرمز كمعلَمة للوسيطة OutlinedTextField's trailingIcon.
    • يتحكّم متغير الحالة showDatePicker في إمكانية ظهور أداة اختيار التاريخ المثبّتة.
  • حاوية أداة اختيار التاريخ هي عنصر Popup قابل للإنشاء، ويتم وضعها فوق المحتوى بدون التأثير في تصميم العناصر الأخرى.
  • تعرض selectedDate قيمة التاريخ المحدّد من الكائن DatePickerState وتنسّقه باستخدام الدالة convertMillisToDate.
  • يظهر التاريخ المحدّد في حقل النص.
  • يتم وضع أداة اختيار التاريخ المثبّتة أسفل حقل النص باستخدام offset modifier.
  • يتم استخدام 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 أداة اختيار التاريخ المنبثقة.
  • يتم تنفيذ تعبير دالة onDateSelected Lambda عندما يختار المستخدم تاريخًا.
    • تعرض هذه السمة التاريخ المحدّد للعنصر القابل للإنشاء الرئيسي.
  • يتم تنفيذ تعبير onDismiss lambda عندما يغلق المستخدم مربّع الحوار.

النتائج

يظهر هذا التنفيذ على النحو التالي:

مثال على أداة اختيار التاريخ في النافذة المنبثقة
الشكل 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", LocalLocale.current.platformLocale).format(date)
            Text("Selected date: $formattedDate")
        } else {
            Text("No date selected")
        }
// ...
        DatePickerModal(
            onDateSelected = {
                selectedDate = it
                showModal = false
            },
            onDismiss = { showModal = false }
        )
    }
// ...

وينطبق الأمر نفسه بشكل أساسي على أدوات اختيار التاريخ للنطاق، ولكن عليك استخدام Pair<Long?, Long?> أو فئة بيانات لتسجيل قيمتَي البدء والانتهاء.

انظر أيضًا