DatePickerFragment を setFragmentResultListener を使って実装する
以前は setTargetFragment を使った実装をどこかのサンプルを元に作っていたのですが、久し振りにアプリを Kotlin で実装し直してたら deprecated になっていたので、またしても先人のサンプルを参考に作り直してみました。
数年前の実装(Java)
DatePickerDialog をそのまま使うのは非推奨ということで、DialogFragment と合わせて使うようにしていたとものと思います。 こうしないと、画面が回転した時に日付選択のダイアログが閉じてしまうんですよね。import android.app.DatePickerDialog; import android.app.Dialog; import android.os.Bundle; import androidx.fragment.app.DialogFragment; import androidx.fragment.app.Fragment; import android.widget.DatePicker; public class DatePickerFragment extends DialogFragment implements DatePickerDialog.OnDateSetListener { public static DatePickerFragment newInstance(DatePickerFragment.OnDateSetListener targetFragment, int dataId, int year, int month, int day) { if (targetFragment != null && !(targetFragment instanceof Fragment)) { throw new RuntimeException("targetFragment is not Fragment"); } Bundle bundle = new Bundle(); bundle.putInt("dataId", dataId); bundle.putInt("year", year); bundle.putInt("month", month); bundle.putInt("day", day); DatePickerFragment picker = new DatePickerFragment(); picker.setArguments(bundle); picker.setTargetFragment((Fragment)targetFragment, 0); return picker; } public int getDataId() { return getArguments().getInt("dataId", -1); } public int getYear() { return getArguments().getInt("year", 0); } public int getMonth() { return getArguments().getInt("month", 0); } public int getDay() { return getArguments().getInt("day", 0); } @Override public Dialog onCreateDialog(Bundle savedInstanceState) { int year = getYear(); int month = getMonth(); int day = getDay(); return new DatePickerDialog(getActivity(), this, year, month, day); } public void onDateSet(DatePicker view, int year, int month, int day) { OnDateSetListener listener = (OnDateSetListener)getTargetFragment(); listener.onDateSet(getDataId(), view, year, month, day); } static public interface OnDateSetListener { void onDateSet(int dataId, DatePicker view, int year, int month, int day); } }
今回の実装(Kotlin)
単純に Kotlin に焼き直そうとしたのですが、setTargetFragment が deprecated になってしまってました。(いつの話よ、というツッコミが) いつもお世話になっている Qiita に良さげな実装提案が1年も前にアップされていたので、参考にさせていただきました。import android.app.DatePickerDialog import android.app.Dialog import android.os.Bundle import android.widget.DatePicker import androidx.core.os.bundleOf import androidx.fragment.app.DialogFragment import androidx.fragment.app.Fragment import androidx.fragment.app.setFragmentResult import org.threeten.bp.LocalDate import java.util.* class DatePickerFragment : DialogFragment(), DatePickerDialog.OnDateSetListener { companion object { private const val REQUEST_KEY = "request_key_date_picker_fragment" private const val RESULT_KEY_SET_DATE = "result_key_date_picker_set_date" fun show( target: Fragment, timeInMillis: Long, onSetDate: ((year: Int, month: Int, dayOfMonth: Int) -> Unit)? = null ) { val bundle = Bundle() val cal = Calendar.getInstance() cal.timeInMillis = timeInMillis bundle.putInt("year", cal[Calendar.YEAR]) bundle.putInt("month", cal[Calendar.MONTH]) bundle.putInt("dayOfMonth", cal[Calendar.DAY_OF_MONTH]) newInstance().run { arguments = bundle target .childFragmentManager .setFragmentResultListener( REQUEST_KEY, target.viewLifecycleOwner ) { requestKey, bundle -> if (requestKey != REQUEST_KEY) return@setFragmentResultListener when { bundle.containsKey(RESULT_KEY_SET_DATE) -> { val picked = bundle[RESULT_KEY_SET_DATE] as LocalDate onSetDate?.invoke( picked.year, picked.monthValue - 1, picked.dayOfMonth ) } } } show(target.childFragmentManager, "DatePickerFragment") } } private fun newInstance() = DatePickerFragment() } override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { val year = arguments?.getInt("year", 0) ?: 0 val month = arguments?.getInt("month", 0) ?: 0 val dayOfMonth = arguments?.getInt("dayOfMonth", 1) ?: 1 return DatePickerDialog(requireContext(), this, year, month, dayOfMonth) } override fun onDateSet(picker: DatePicker?, year: Int, month: Int, dayOfMonth: Int) { setFragmentResult( REQUEST_KEY, bundleOf(RESULT_KEY_SET_DATE to LocalDate.of(year, month + 1, dayOfMonth)) ) } }アプリから使うとこんな感じです。
binding.date.setOnClickListener { DatePickerFragment.show( target = this, timeInMillis = viewModel.date.value?.time ?: 0, onSetDate = { year, month, dayOfMonth -> val cal = Calendar.getInstance() cal.time = viewModel.date.value ?: Date() cal[Calendar.YEAR] = year cal[Calendar.MONTH] = month cal[Calendar.DAY_OF_MONTH] = dayOfMonth viewModel.date.value = cal.time } ) }最後にスクショです。見た目は元のままですけど、Android Studio でワーニングが出なくなるのがいいですよね。