Pemilih tanggal

Pemilih tanggal memungkinkan pengguna memilih tanggal, rentang tanggal, atau keduanya. Dialog ini menggunakan dialog kalender atau input teks untuk memungkinkan pengguna memilih tanggal.

Jenis

Ada tiga jenis pemilih tanggal:

  • Terpasang ke dok: Muncul secara inline dalam tata letak. Ini cocok untuk tata letak ringkas saat dialog khusus mungkin terasa mengganggu.
  • Modal: Muncul sebagai dialog yang menempatkan konten aplikasi. Hal ini memberikan fokus yang jelas pada pemilihan tanggal.
  • Input modal: Menggabungkan kolom teks dengan pemilih tanggal modal.

Anda dapat menerapkan pemilih tanggal ini di aplikasi menggunakan composable berikut:

  • DatePicker: Composable umum untuk pemilih tanggal. Penampung yang Anda gunakan menentukan apakah penampung tersebut di-dock atau model.
  • DatePickerDialog: Penampung untuk pemilih tanggal input modal dan modal.
  • DateRangePicker: Untuk pemilih tanggal tempat pengguna dapat memilih rentang dengan tanggal mulai dan tanggal akhir.

Status

Parameter utama yang sama-sama dimiliki oleh berbagai composable pemilih tanggal adalah state, yang menggunakan objek DatePickerState atau DateRangePickerState. Propertinya mengambil informasi tentang pilihan pengguna menggunakan pemilih tanggal, seperti tanggal yang dipilih saat ini.

Untuk mengetahui informasi selengkapnya tentang cara menggunakan tanggal yang dipilih, lihat Menggunakan bagian tanggal yang dipilih.

Pemilih tanggal yang di-dock

Dalam contoh berikut, ada kolom teks yang meminta pengguna memasukkan tanggal lahir mereka. Saat pengguna mengklik ikon kalender di kolom, pemilih tanggal yang di-dock akan terbuka di bawah kolom input.

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

Poin penting tentang kode

  • Pemilih tanggal muncul saat pengguna mengklik IconButton.
    • Tombol ikon berfungsi sebagai argumen untuk parameter trailingIcon OutlinedTextField.
    • Variabel status showDatePicker mengontrol visibilitas pemilih tanggal yang di-dock.
  • Penampung pemilih tanggal adalah composable Popup, yang menempatkan konten tanpa memengaruhi tata letak elemen lain.
  • selectedDate mengambil nilai tanggal yang dipilih dari objek DatePickerState dan memformatnya menggunakan fungsi convertMillisToDate.
  • Tanggal yang dipilih akan muncul di kolom teks.
  • Pemilih tanggal yang di-dock diposisikan di bawah kolom teks menggunakan pengubah offset.
  • Box digunakan sebagai penampung root untuk memungkinkan pelapisan kolom teks dan pemilih tanggal yang tepat.

Hasil

Setelah mengklik ikon kalender, implementasi ini akan muncul sebagai berikut:

Contoh pemilih tanggal yang di-dock.
Gambar 1. Pemilih tanggal yang di-dock.

Pemilih tanggal modal menampilkan dialog yang mengambang di atas layar. Untuk menerapkannya, buat DatePickerDialog dan teruskan 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)
    }
}

  • Fungsi composable DatePickerModal menampilkan pemilih tanggal modal.
  • Ekspresi lambda onDateSelected dieksekusi saat pengguna memilih tanggal.
    • Fungsi ini mengekspos tanggal yang dipilih ke composable induk.
  • Ekspresi lambda onDismiss dijalankan saat pengguna menutup dialog.

Hasil

Implementasi ini muncul sebagai berikut:

Contoh pemilih tanggal modal.
Gambar 2. Pemilih tanggal modal.

Pemilih tanggal modal input

Pemilih tanggal modal dengan input menampilkan dialog yang mengambang di atas layar dan memungkinkan pengguna memasukkan tanggal.

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

Ini sangat mirip dengan contoh pemilih tanggal modal. Perbedaan utama adalah sebagai berikut:

  • Parameter initialDisplayMode menetapkan mode tampilan awal ke DisplayMode.Input.
Pemilih tanggal modal dengan input.
Gambar 3. Pemilih tanggal modal dengan input.

Pemilih tanggal dengan rentang

Anda dapat membuat pemilih tanggal yang memungkinkan pengguna memilih rentang antara tanggal mulai dan akhir. Untuk melakukannya, gunakan DateRangePicker.

Penggunaan DateRangePicker pada dasarnya sama dengan DatePicker. Anda dapat menggunakannya untuk pemilih yang di-dock sebagai turunan PopUp, atau Anda dapat menggunakannya sebagai pemilih modal dan meneruskannya ke DatePickerDialog. Perbedaan utamanya adalah Anda menggunakan DateRangePickerState, bukan DatePickerState.

Cuplikan berikut menunjukkan cara membuat pemilih tanggal modal dengan rentang:

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

Poin penting tentang kode

  • Parameter onDateRangeSelected adalah callback yang menerima Pair<Long?, Long?> yang mewakili tanggal mulai dan akhir yang dipilih. Tindakan ini memberi composable induk akses ke rentang yang dipilih.
  • rememberDateRangePickerState() membuat status untuk pemilih rentang tanggal.
  • DatePickerDialog membuat penampung dialog modal.
  • Di pengendali onClick tombol konfirmasi, onDateRangeSelected meneruskan rentang yang dipilih ke composable induk.
  • Composable DateRangePicker berfungsi sebagai konten dialog.

Hasil

Implementasi ini muncul sebagai berikut:

Contoh pemilih tanggal rentang modal.
Gambar 4. Pemilih tanggal modal dengan rentang yang dipilih.

Gunakan tanggal yang dipilih

Untuk mengambil tanggal yang dipilih, lacak di composable induk sebagai Long dan teruskan nilai ke DatePicker di onDateSelected. Cuplikan berikut menunjukkan hal ini, meskipun Anda dapat melihat implementasi lengkapnya di aplikasi cuplikan resmi.

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

Pada dasarnya, hal yang sama berlaku untuk pemilih tanggal rentang, meskipun Anda perlu menggunakan Pair<Long?, Long?> atau class data untuk mengambil nilai awal dan akhir.

Lihat juga