import android.view.View; import android.widget.NumberPicker; import com.myapp.android.us.R; import java.text.DateFormatSymbols; import java.util.Arrays; import java.util.Calendar; import java.util.Locale; /** * A delegate implementing the simple Date Picker functionality and takes care of the spinner UI. */ public class SimpleDatePickerDelegate { private static final int DEFAULT_START_YEAR = 1900; private static final int DEFAULT_END_YEAR = 2100; private final NumberPicker mMonthSpinner; private final NumberPicker mYearSpinner; private Calendar mTempDate; private Calendar mCurrentDate; private Calendar mMinDate; private Calendar mMaxDate; private String[] mShortMonths; private int mNumberOfMonths; private Locale mCurrentLocale; private OnDateChangedListener mOnDateChangedListener; public SimpleDatePickerDelegate(View parent) { setCurrentLocale(Locale.getDefault()); NumberPicker.OnValueChangeListener onChangeListener = new NumberPicker .OnValueChangeListener() { public void onValueChange(NumberPicker picker, int oldVal, int newVal) { mTempDate.setTimeInMillis(mCurrentDate.getTimeInMillis()); // take care of wrapping of days and months to update greater fields if (picker == mMonthSpinner) { if (oldVal == 11 && newVal == 0) { mTempDate.add(Calendar.MONTH, 1); } else if (oldVal == 0 && newVal == 11) { mTempDate.add(Calendar.MONTH, -1); } else { mTempDate.add(Calendar.MONTH, newVal - oldVal); } } else if (picker == mYearSpinner) { mTempDate.set(Calendar.YEAR, newVal); } else { throw new IllegalArgumentException(); } // now set the date to the adjusted one setDate(mTempDate.get(Calendar.YEAR), mTempDate.get(Calendar.MONTH)); updateSpinners(); notifyDateChanged(); } }; // month mMonthSpinner = (NumberPicker) parent.findViewById(R.id.month); mMonthSpinner.setMinValue(0); mMonthSpinner.setMaxValue(mNumberOfMonths - 1); mMonthSpinner.setDisplayedValues(mShortMonths); mMonthSpinner.setOnLongPressUpdateInterval(200); mMonthSpinner.setOnValueChangedListener(onChangeListener); // year mYearSpinner = (NumberPicker) parent.findViewById(R.id.year); mYearSpinner.setOnLongPressUpdateInterval(100); mYearSpinner.setOnValueChangedListener(onChangeListener); // set the min date giving priority of the minDate over startYear mTempDate.clear(); mTempDate.set(DEFAULT_START_YEAR, 0, 1); setMinDate(mTempDate.getTimeInMillis()); // set the max date giving priority of the maxDate over endYear mTempDate.clear(); mTempDate.set(DEFAULT_END_YEAR, 11, 31); setMaxDate(mTempDate.getTimeInMillis()); // initialize to current date mCurrentDate.setTimeInMillis(System.currentTimeMillis()); init(mCurrentDate.get(Calendar.YEAR), mCurrentDate.get(Calendar.MONTH), null); } public void init(int year, int monthOfYear, OnDateChangedListener onDateChangedListener) { setDate(year, monthOfYear); updateSpinners(); mOnDateChangedListener = onDateChangedListener; } public void setMinDate(long minDate) { mTempDate.setTimeInMillis(minDate); if (mTempDate.get(Calendar.YEAR) == mMinDate.get(Calendar.YEAR) && mTempDate.get(Calendar.DAY_OF_YEAR) != mMinDate.get(Calendar.DAY_OF_YEAR)) { return; } mMinDate.setTimeInMillis(minDate); if (mCurrentDate.before(mMinDate)) { mCurrentDate.setTimeInMillis(mMinDate.getTimeInMillis()); } updateSpinners(); } public void setMaxDate(long maxDate) { mTempDate.setTimeInMillis(maxDate); if (mTempDate.get(Calendar.YEAR) == mMaxDate.get(Calendar.YEAR) && mTempDate.get(Calendar.DAY_OF_YEAR) != mMaxDate.get(Calendar.DAY_OF_YEAR)) { return; } mMaxDate.setTimeInMillis(maxDate); if (mCurrentDate.after(mMaxDate)) { mCurrentDate.setTimeInMillis(mMaxDate.getTimeInMillis()); } updateSpinners(); } public int getYear() { return mCurrentDate.get(Calendar.YEAR); } public int getMonth() { return mCurrentDate.get(Calendar.MONTH); } /** * Sets the current locale. * * @param locale The current locale. */ protected void setCurrentLocale(Locale locale) { if (!locale.equals(mCurrentLocale)) { mCurrentLocale = locale; } mTempDate = getCalendarForLocale(mTempDate, locale); mMinDate = getCalendarForLocale(mMinDate, locale); mMaxDate = getCalendarForLocale(mMaxDate, locale); mCurrentDate = getCalendarForLocale(mCurrentDate, locale); mNumberOfMonths = mTempDate.getActualMaximum(Calendar.MONTH) + 1; mShortMonths = new DateFormatSymbols().getShortMonths(); if (usingNumericMonths()) { // We're in a locale where a date should either be all-numeric, or all-text. // All-text would require custom NumberPicker formatters for day and year. mShortMonths = new String[mNumberOfMonths]; for (int i = 0; i < mNumberOfMonths; ++i) { mShortMonths[i] = String.format("%d", i + 1); } } } private void setDate(int year, int month) { mCurrentDate.set(Calendar.YEAR, year); mCurrentDate.set(Calendar.MONTH, month); if (mCurrentDate.before(mMinDate)) { mCurrentDate.setTimeInMillis(mMinDate.getTimeInMillis()); } else if (mCurrentDate.after(mMaxDate)) { mCurrentDate.setTimeInMillis(mMaxDate.getTimeInMillis()); } } private void updateSpinners() { // set the spinner ranges respecting the min and max dates if (mCurrentDate.equals(mMinDate)) { mMonthSpinner.setDisplayedValues(null); mMonthSpinner.setMinValue(mCurrentDate.get(Calendar.MONTH)); mMonthSpinner.setMaxValue(mCurrentDate.getActualMaximum(Calendar.MONTH)); mMonthSpinner.setWrapSelectorWheel(false); } else if (mCurrentDate.equals(mMaxDate)) { mMonthSpinner.setDisplayedValues(null); mMonthSpinner.setMinValue(mCurrentDate.getActualMinimum(Calendar.MONTH)); mMonthSpinner.setMaxValue(mCurrentDate.get(Calendar.MONTH)); mMonthSpinner.setWrapSelectorWheel(false); } else { mMonthSpinner.setDisplayedValues(null); mMonthSpinner.setMinValue(0); mMonthSpinner.setMaxValue(11); mMonthSpinner.setWrapSelectorWheel(true); } // make sure the month names are a zero based array // with the months in the month spinner String[] displayedValues = Arrays.copyOfRange( mShortMonths, mMonthSpinner.getMinValue(), mMonthSpinner.getMaxValue() + 1); mMonthSpinner.setDisplayedValues(displayedValues); // year spinner range does not change based on the current date mYearSpinner.setMinValue(mMinDate.get(Calendar.YEAR)); mYearSpinner.setMaxValue(mMaxDate.get(Calendar.YEAR)); mYearSpinner.setWrapSelectorWheel(false); // set the spinner values mYearSpinner.setValue(mCurrentDate.get(Calendar.YEAR)); mMonthSpinner.setValue(mCurrentDate.get(Calendar.MONTH)); } private boolean usingNumericMonths() { return Character.isDigit(mShortMonths[Calendar.JANUARY].charAt(0)); } /** * Gets a calendar for locale bootstrapped with the value of a given calendar. * * @param oldCalendar The old calendar. * @param locale The locale. */ private Calendar getCalendarForLocale(Calendar oldCalendar, Locale locale) { if (oldCalendar == null) { return Calendar.getInstance(locale); } else { long currentTimeMillis = oldCalendar.getTimeInMillis(); Calendar newCalendar = Calendar.getInstance(locale); newCalendar.setTimeInMillis(currentTimeMillis); return newCalendar; } } /** * Notifies the listener, if such, for a change in the selected date. */ private void notifyDateChanged() { if (mOnDateChangedListener != null) { mOnDateChangedListener.onDateChanged(getYear(), getMonth()); } } /** * The callback used to indicate the user changed the date. */ public interface OnDateChangedListener { /** * Called upon a date change. * * @param year The year that was set. * @param monthOfYear The month that was set (0-11) for compatibility with {@link * java.util.Calendar}. */ void onDateChanged(int year, int monthOfYear); } }