日付選択ツール

日付選択ツールを使用すると、ユーザーは日付、日付の範囲、またはその両方を選択できます。カレンダー ダイアログまたはテキスト入力を使用して、ユーザーが日付を選択できるようにします。

日付選択ツールには次の 3 種類があります。

  • ドッキング: レイアウト内にインラインで表示されます。これは、専用のダイアログが煩わしく感じる可能性があるコンパクト レイアウトに適しています。
  • モーダル: アプリのコンテンツにオーバーレイするダイアログとして表示されます。これにより、日付選択に明確な焦点が提供されます。
  • モーダル入力: テキスト フィールドとモーダル日付選択ツールを組み合わせたものです。

これらの日付選択ツールは、次のコンポーザブルを使用してアプリに実装できます。

  • 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 をクリックすると、日付選択ツールが表示されます。
    • アイコンボタンは、OutlinedTextFieldtrailingIcon パラメータの引数として機能します。
    • 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 は、モーダル日付選択ツールを表示します。
  • onDateSelected ラムダ式は、ユーザーが日付を選択すると実行されます。
    • 選択した日付を親コンポーザブルに公開します。
  • 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 に渡すこともできます。主な違いは、DatePickerState ではなく DateRangePickerState を使用する点です。

次のスニペットは、範囲を指定してモーダル日付選択ツールを作成する方法を示しています。

@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 として追跡し、値を onDateSelectedDatePicker に渡します。次のスニペットはこれを示していますが、完全な実装は公式スニペット アプリで確認できます。

// ...
    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?> またはデータクラスを使用する必要があります。

関連ドキュメント