Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to change the order of the NumberPickers in DatePickerDialog

Tags:

android

enter image description here

I want to swap the Month And Date (Spinners). I want to send the Date column (Spinner) to the left and the Month column (Spinner) to the middle. Is it possible?

like image 771
MD TAHMID HOSSAIN Avatar asked Dec 10 '22 20:12

MD TAHMID HOSSAIN


2 Answers

According to notes in the source, the order of those NumberPickers is determined by the system-wide date format chosen by the user, falling back on a determination of the best order given the user's locale. There is no public method available to easily change this. However, I've figured out a way to change the order regardless.

In the following example, we obtain the IDs for the DatePickerDialog's NumberPickers from the System Resources. The names for these identifiers I found by examining the layout file for the DatePicker class.

Once we've got the IDs, we find the NumberPickers and their parent LinearLayout in the dialog just like we would any other View. We can then remove the spinners from the layout, and add them back in any order desired. The DatePicker class uses the same method to reorder the spinners, and some of the code below is pulled from its source.

After adding back each spinner, we need to call setImeOptions() with it and its new index in the order. This will correct the focus chain and keyboard feedback for the spinners' input TextViews.

This all got a little unwieldy, so I wrapped it up in a couple of methods. You need only call the orderDate() method after your dialog is shown, with a char array indicating the desired date order. For example:

DatePickerDialog dialog = new DatePickerDialog(...);
...
dialog.show();

orderDate(dialog, new char[]{'d', 'm', 'y'});

We keep an int constant for the number of spinners, simplifying some of the code pulled from the source:

private static final int SPINNER_COUNT = 3;

private void orderDate(DatePickerDialog dialog, char[] ymdOrder) {
    if(!dialog.isShowing()) {
        throw new IllegalStateException("Dialog must be showing");
    }

    final int idYear = Resources.getSystem().getIdentifier("year", "id", "android");
    final int idMonth = Resources.getSystem().getIdentifier("month", "id", "android");
    final int idDay = Resources.getSystem().getIdentifier("day", "id", "android");
    final int idLayout = Resources.getSystem().getIdentifier("pickers", "id", "android");

    final NumberPicker spinnerYear = (NumberPicker) dialog.findViewById(idYear);
    final NumberPicker spinnerMonth = (NumberPicker) dialog.findViewById(idMonth);
    final NumberPicker spinnerDay = (NumberPicker) dialog.findViewById(idDay);
    final LinearLayout layout = (LinearLayout) dialog.findViewById(idLayout);

    layout.removeAllViews();
    for (int i = 0; i < SPINNER_COUNT; i++) {
        switch (ymdOrder[i]) {
            case 'y':
                layout.addView(spinnerYear);
                setImeOptions(spinnerYear, i);                  
                break;
            case 'm':
                layout.addView(spinnerMonth);
                setImeOptions(spinnerMonth, i);
                break;
            case 'd':
                layout.addView(spinnerDay);
                setImeOptions(spinnerDay, i);
                break;
            default:
                throw new IllegalArgumentException("Invalid char[] ymdOrder");
        }
    }
}

private void setImeOptions(NumberPicker spinner, int spinnerIndex) {
    final int imeOptions;
    if (spinnerIndex < SPINNER_COUNT - 1) {
        imeOptions = EditorInfo.IME_ACTION_NEXT;
    }
    else {
        imeOptions = EditorInfo.IME_ACTION_DONE;
    }
    int idPickerInput = Resources.getSystem().getIdentifier("numberpicker_input", "id", "android");
    TextView input = (TextView) spinner.findViewById(idPickerInput);
    input.setImeOptions(imeOptions);
}
like image 66
Mike M. Avatar answered May 25 '23 04:05

Mike M.


Based on @Mike M. solution I wrote an extension function in Kotlin.

Example:

datePicker.formatDate("dmy") // show picker in Day, Month, Year format

Kotlin extension function:

import android.content.res.Resources
import android.view.View
import android.view.inputmethod.EditorInfo
import android.widget.DatePicker
import android.widget.LinearLayout
import android.widget.NumberPicker
import android.widget.TextView

private const val SPINNER_COUNT = 3

/**
 * Changes the [DatePicker] date format.
 * Example: dmy will show the date picker in day, month, year order
 * */
fun DatePicker.formatDate(ymdOrder: String) {
  val system = Resources.getSystem()
  val idYear = system.getIdentifier("year", "id", "android")
  val idMonth = system.getIdentifier("month", "id", "android")
  val idDay = system.getIdentifier("day", "id", "android")
  val idLayout = system.getIdentifier("pickers", "id", "android")
  val spinnerYear = findViewById<View>(idYear) as NumberPicker
  val spinnerMonth = findViewById<View>(idMonth) as NumberPicker
  val spinnerDay = findViewById<View>(idDay) as NumberPicker
  val layout = findViewById<View>(idLayout) as LinearLayout
  layout.removeAllViews()
  for (i in 0 until SPINNER_COUNT) {
    when (ymdOrder[i]) {
      'y' -> {
        layout.addView(spinnerYear)
        setImeOptions(spinnerYear, i)
      }
      'm' -> {
        layout.addView(spinnerMonth)
        setImeOptions(spinnerMonth, i)
      }
      'd' -> {
        layout.addView(spinnerDay)
        setImeOptions(spinnerDay, i)
      }
      else -> throw IllegalArgumentException("Invalid char[] ymdOrder")
    }
  }
}

private fun setImeOptions(spinner: NumberPicker, spinnerIndex: Int) {
  val imeOptions: Int = if (spinnerIndex < SPINNER_COUNT - 1) {
    EditorInfo.IME_ACTION_NEXT
  } else {
    EditorInfo.IME_ACTION_DONE
  }
  val idPickerInput: Int = Resources.getSystem().getIdentifier("numberpicker_input", "id", "android")
  val input = spinner.findViewById<View>(idPickerInput) as TextView
  input.imeOptions = imeOptions
}
like image 29
vovahost Avatar answered May 25 '23 03:05

vovahost