Search code examples
androidkotlinandroid-datepicker

Spinner DatePicker with numeric months instead of named


I have a basic DatePicker that's spinner-style, without any layouts. It looks like this:

class DatePickerFragment: DialogFragment(), DatePickerDialog.OnDateSetListener {

    private lateinit var date: Date

    interface Callbacks {
        fun onDateSelected(date: Date)
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        date = arguments?.getSerializable(ARG_DATE) as Date
    }

    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
        val calendar = Calendar.getInstance().apply {
            time = date
        }

        val initialYear = calendar.get(Calendar.YEAR)
        val initialMonth = calendar.get(Calendar.MONTH)
        val initialDay = calendar.get(Calendar.DAY_OF_MONTH)

        return DatePickerDialog(
            requireContext(),
            this,
            initialYear,
            initialMonth,
            initialDay
        )
    }

    override fun onDateSet(view: DatePicker?, year: Int, month: Int, dayOfMonth: Int) {
        val selectedDate = GregorianCalendar(year, month, dayOfMonth).time
        targetFragment?.let { fragment ->
            (fragment as Callbacks).onDateSelected(selectedDate)
        }
    }

    companion object {
        fun getInstance(date: Date): DatePickerFragment {
            return DatePickerFragment().apply {
                arguments = Bundle().apply {
                    putSerializable(ARG_DATE, date)
                }
            }
        }
    }
}

private const val ARG_DATE = "date"

The picker works as it should. The problem is that it displays the month name (three letter format), see:

enter image description here

How do I make the DatePicker to display months numerically (two-digit format)? In other words instead of Mar it would display as 03. Ideally, I'd like to avoid having to implement custom spinners, if possible.


Solution

  • You can achieve this by finding the month NumberPicker from DatePicker and setting the displayed values to numeric strings.

    Make the below changes:

    1.Create your DatePickerDialog in onCreateDialog(savedInstanceState: Bundle?) using a custom Style like below:

    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
            val calendar = Calendar.getInstance().apply {
                time = date
            }
    
            val initialYear = calendar.get(Calendar.YEAR)
            val initialMonth = calendar.get(Calendar.MONTH)
            val initialDay = calendar.get(Calendar.DAY_OF_MONTH)
    
            val mPickerDialog: DatePickerDialog = object : DatePickerDialog(requireContext(), R.style.MyDatePickerDialogStyle, this, initialYear, initialMonth, initialDay) {
                override fun onDateChanged(view: DatePicker, year: Int, month: Int, dayOfMonth: Int) {
                    super.onDateChanged(view, year, month, dayOfMonth)
                    setNumericMonth(view)
                }
            }
            setNumericMonth(mPickerDialog.datePicker)
            return mPickerDialog
        }
    

    where R.style.MyDatePickerDialogStyle is a custom style to set the Spinner Style like below:

    <style name="MyDatePickerDialogStyle" parent="android:Theme.Material.Dialog">
          <item name="android:datePickerStyle">@style/MyDatePickerStyle</item>
    </style>
        
    <style name="MyDatePickerStyle" parent="android:Widget.Material.DatePicker">
         <item name="android:datePickerMode">spinner</item>
     </style>
    

    2.Use the below helper functions in DatePickerFragment to set month to be numeric like below:

    private fun setNumericMonth(datePicker: DatePicker) {
            val monthNumbers = arrayOf("01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12")
            val monthPicker = getMonthNumberPicker(datePicker)
            if (monthPicker != null) {
                monthPicker.displayedValues = monthNumbers
            }
        }
    
    private fun getMonthNumberPicker(datePicker: DatePicker?): NumberPicker? {
            try {
                if (datePicker != null && datePicker.childCount > 0 && datePicker.getChildAt(0) is ViewGroup) {
                    val vg = datePicker.getChildAt(0) as ViewGroup
                    if (vg.childCount > 0 && vg.getChildAt(0) is ViewGroup) {
                        val vgPickers = vg.getChildAt(0) as ViewGroup
                        for (i in 0 until vgPickers.childCount) {
                            if (vgPickers.getChildAt(i) is NumberPicker && i == 1) {
                                return vgPickers.getChildAt(i) as NumberPicker
                            }
                        }
                    }
                }
            } catch (e: Exception) {
            }
            return null
        }
    

    Result:

    date_numeric_month_picker