Datepicker Dialog Without Calendar Visualization in Lollipop [Spinner Mode]

How to fix DatePickerDialog in spinner mode for Android 7.0?

I ran into the same issue (users don't want to scroll back in time 40 years month by month to find their birth year and most don't know you can just click on the year in the android date picker to scroll through years). Like you, I couldn't get the spinner to work universally, so I figured out (with help from SO and Google) how to make it start in year selection mode.

The code for my DatePickerDialogFragment is pasted below.

public class DatePickerDialogFragment extends DialogFragment {

private DatePickerDialog.OnDateSetListener listener = null;

void setListener(DatePickerDialog.OnDateSetListener listener) {
this.listener = listener;
}

private static final String START_IN_YEARS = "com.myapp.picker.START_IN_YEARS";
private static final String YEAR = "com.myapp.picker.YEAR";
private static final String MONTH = "com.myapp.picker.MONTH";
private static final String DAY_OF_MONTH = "com.myapp.picker.DAY_OF_MONTH";

public static DatePickerDialogFragment newInstance(boolean startInYears, Calendar c) {
DatePickerDialogFragment f = new DatePickerDialogFragment();

int year = c.get(Calendar.YEAR);
int month = c.get(Calendar.MONTH);
int day = c.get(Calendar.DAY_OF_MONTH);

Bundle args = new Bundle();
args.putBoolean(START_IN_YEARS, startInYears);
args.putInt(YEAR, year);
args.putInt(MONTH, month);
args.putInt(DAY_OF_MONTH, day);

f.setArguments(args);
return f;
}

@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {

Bundle args = getArguments();
DatePickerDialog dpd = null;

if( listener != null && args != null) {
boolean startInYears = args.getBoolean(START_IN_YEARS);

Context context = getActivity();
boolean requireSpinnerMode = isBrokenSamsungDevice();
if (requireSpinnerMode) {
context = new ContextThemeWrapper(context, android.R.style.Theme_Holo_Light_Dialog);
}

int year = args.getInt(YEAR);
int month = args.getInt(MONTH);
int day = args.getInt(DAY_OF_MONTH);

dpd = new DatePickerDialog(context, listener, year, month, day);

if (startInYears && !requireSpinnerMode) {
boolean canOpenYearView = openYearView(dpd.getDatePicker());
if (!canOpenYearView) {
context = new ContextThemeWrapper(getActivity(), android.R.style.Theme_Holo_Light_Dialog);
dpd = new DatePickerDialog(context, listener, year, month, day);
}
}
}
else {
setShowsDialog(false);
dismissAllowingStateLoss();
}

return dpd;
}

private static boolean isBrokenSamsungDevice() {
return Build.MANUFACTURER.equalsIgnoreCase("samsung") &&
isBetweenAndroidVersions(
Build.VERSION_CODES.LOLLIPOP,
Build.VERSION_CODES.LOLLIPOP_MR1);
}

private static boolean isBetweenAndroidVersions(int min, int max) {
return Build.VERSION.SDK_INT >= min && Build.VERSION.SDK_INT <= max;
}

private static boolean openYearView(DatePicker datePicker) {
if( isBrokenSamsungDevice() ) {
return false;
}

View v = datePicker.findViewById(Resources.getSystem().getIdentifier("date_picker_header_year", "id", "android"));
if( v != null ) {
v.performClick();
}
else {
try {
Field mDelegateField = datePicker.getClass().getDeclaredField("mDelegate");
mDelegateField.setAccessible(true);
Object delegate = mDelegateField.get(datePicker);
Method setCurrentViewMethod = delegate.getClass().getDeclaredMethod("setCurrentView", int.class);
setCurrentViewMethod.setAccessible(true);
setCurrentViewMethod.invoke(delegate, 1);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
return true;
}
}

The code in the Activity (member variables and stuff in onCreate) to launch this (and preserve it on rotation) looks like this:

// Class member variables
private Calendar myCalendar = Calendar.getInstance();
private boolean birthday_is_set = false;


// this next part is in onCreate

// set the calendar date to a saved date if applicable
// and change birthday_is_set if they had saved a birthday


final DatePickerDialog.OnDateSetListener birthdayListener = new DatePickerDialog.OnDateSetListener() {

@Override
public void onDateSet(DatePicker view, int year, int monthOfYear,
int dayOfMonth) {
// I save the date in a calendar, replace this
// with whatever you want to do with the selected date
myCalendar.set(Calendar.YEAR, year);
myCalendar.set(Calendar.MONTH, monthOfYear);
myCalendar.set(Calendar.DAY_OF_MONTH, dayOfMonth);
birthday_is_set = true;
updateBirthdayLabel();
}
};

if (savedInstanceState != null) {
DatePickerDialogFragment dpf;

dpf = (DatePickerDialogFragment) getFragmentManager().findFragmentByTag("birthdayDatePicker");
if (dpf != null) {
// on rotation the listener will be referring to the old Activity,
// so we have to reset it here to act on the current Activity
dpf.setListener(birthdayListener);
}
}

birthdayDatePicker.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// Your logic may vary here. I chose not to start it in year
// mode if they've already selected a date.
boolean startInYears = !birthday_is_set;
DatePickerDialogFragment dpf = DatePickerDialogFragment.newInstance(startInYears, myCalendar);
dpf.setListener(birthdayListener);
dpf.show(getFragmentManager(), "birthdayDatePicker");
}
});

This includes both the hack to get it to start in year mode, and a fix for some random date picker failures on Samsung devices of a certain vintage. This version has been working without crashes or user complaints for API 15+ for a few months now.

EDIT: Updated openYearView to work on Android 10

datePickerDialog spinner instead od calendar java programatically api 24

In this API it is an error.
I found a workaround https://gist.github.com/jeffdgr8/6bc5f990bf0c13a7334ce385d482af9f



Related Topics



Leave a reply



Submit