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

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

الأنواع

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

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

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

  • 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.
    • يعمل زر الرمز كوسيطة لحساب OutlinedTextField مَعلمة trailingIcon.
    • تتحكّم متغيّر الحالة 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?> أو فئة بيانات للحصول على قيم البداية والنهاية.

انظر أيضًا