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

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

الأنواع

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

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

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

  • 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's 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?> أو فئة بيانات لتسجيل قيم البدء والانتهاء.

انظر أيضًا