OSDN Git Service

DatePicker is picking the wrong date
authorSvetoslav Ganov <svetoslavganov@google.com>
Mon, 24 Jan 2011 10:13:36 +0000 (02:13 -0800)
committerSvetoslav Ganov <svetoslavganov@google.com>
Mon, 24 Jan 2011 13:21:32 +0000 (05:21 -0800)
    bug:3375074

    1. The logic for updating the higher rank fields on wrapping of
       lower rank ones was incorrect. Updated that logic.

    2. On reaching the min/max date the spinners did not stop
       when reaching that date. Now spinners stop at min/max.

    3. Fixed a couple of edge case bugs while setting the
       the min and max dates of the DatePicker on the fly.

    4. Restricted the scrolling/flinging to end at the min
       and max dates.

Change-Id: Ibb588ea2cf951cf8d50328bbfbe7ebdad7ee8067

core/java/android/widget/CalendarView.java
core/java/android/widget/DatePicker.java
core/java/android/widget/NumberPicker.java

index 7ef61a8..899e872 100644 (file)
@@ -440,11 +440,18 @@ public class CalendarView extends FrameLayout {
             return;
         }
         mMinDate.setTimeInMillis(minDate);
+        // make sure the current date is not earlier than
+        // the new min date since the latter is used for
+        // calculating the indices in the adapter thus
+        // avoiding out of bounds error
+        Calendar date = mAdapter.mSelectedDate;
+        if (date.before(mMinDate)) {
+            mAdapter.setSelectedDay(mMinDate);
+        }
         // reinitialize the adapter since its range depends on min date
         mAdapter.init();
-        Calendar date = mAdapter.mSelectedDate;
         if (date.before(mMinDate)) {
-            setDate(mMinDate.getTimeInMillis());
+            setDate(mTempDate.getTimeInMillis());
         } else {
             // we go to the current date to force the ListView to query its
             // adapter for the shown views since we have changed the adapter
@@ -753,7 +760,13 @@ public class CalendarView extends FrameLayout {
             mFirstDayOfMonth.set(Calendar.DAY_OF_MONTH, 1);
 
             setMonthDisplayed(mFirstDayOfMonth);
-            position = getWeeksSinceMinDate(mFirstDayOfMonth);
+
+            // the earliest time we can scroll to is the min date
+            if (mFirstDayOfMonth.before(mMinDate)) {
+                position = 0;
+            } else {
+                position = getWeeksSinceMinDate(mFirstDayOfMonth);
+            }
 
             mPreviousScrollState = OnScrollListener.SCROLL_STATE_FLING;
             if (animate) {
index f1786e2..ea868a6 100644 (file)
@@ -34,6 +34,7 @@ import android.widget.NumberPicker.OnValueChangeListener;
 
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
+import java.util.Arrays;
 import java.util.Calendar;
 import java.util.Locale;
 import java.util.TimeZone;
@@ -156,8 +157,34 @@ public class DatePicker extends FrameLayout {
 
         OnValueChangeListener onChangeListener = new OnValueChangeListener() {
             public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
-                updateDate(mYearSpinner.getValue(), mMonthSpinner.getValue(), mDaySpinner
-                        .getValue());
+                mTempDate.setTimeInMillis(mCurrentDate.getTimeInMillis());
+                // take care of wrapping of days and months to update greater fields
+                if (picker == mDaySpinner) {
+                    int maxDayOfMonth = mTempDate.getActualMaximum(Calendar.DAY_OF_MONTH);
+                    if (oldVal == maxDayOfMonth && newVal == 1) {
+                        mTempDate.add(Calendar.DAY_OF_MONTH, 1);
+                    } else if (oldVal == 1 && newVal == maxDayOfMonth) {
+                        mTempDate.add(Calendar.DAY_OF_MONTH, -1);
+                    } else {
+                        mTempDate.add(Calendar.DAY_OF_MONTH, newVal - oldVal);
+                    }
+                } else 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),
+                        mTempDate.get(Calendar.DAY_OF_MONTH));
+                notifyDateChanged();
             }
         };
 
@@ -167,7 +194,8 @@ public class DatePicker extends FrameLayout {
         mCalendarView = (CalendarView) findViewById(R.id.calendar_view);
         mCalendarView.setOnDateChangeListener(new CalendarView.OnDateChangeListener() {
             public void onSelectedDayChange(CalendarView view, int year, int month, int monthDay) {
-                updateDate(year, month, monthDay);
+                setDate(year, month, monthDay);
+                notifyDateChanged();
             }
         });
 
@@ -260,10 +288,12 @@ public class DatePicker extends FrameLayout {
             return;
         }
         mMinDate.setTimeInMillis(minDate);
-        mYearSpinner.setMinValue(mMinDate.get(Calendar.YEAR));
-        mYearSpinner.setMaxValue(mMaxDate.get(Calendar.YEAR));
         mCalendarView.setMinDate(minDate);
-        updateSpinners(mYearSpinner.getValue(), mMonthSpinner.getValue(), mDaySpinner.getValue());
+        if (mCurrentDate.before(mMinDate)) {
+            mCurrentDate.setTimeInMillis(mMinDate.getTimeInMillis());
+            updateCalendarView();
+        }
+        updateSpinners();
     }
 
     /**
@@ -294,10 +324,12 @@ public class DatePicker extends FrameLayout {
             return;
         }
         mMaxDate.setTimeInMillis(maxDate);
-        mYearSpinner.setMinValue(mMinDate.get(Calendar.YEAR));
-        mYearSpinner.setMaxValue(mMaxDate.get(Calendar.YEAR));
         mCalendarView.setMaxDate(maxDate);
-        updateSpinners(mYearSpinner.getValue(), mMonthSpinner.getValue(), mDaySpinner.getValue());
+        if (mCurrentDate.after(mMaxDate)) {
+            mCurrentDate.setTimeInMillis(mMaxDate.getTimeInMillis());
+            updateCalendarView();
+        }
+        updateSpinners();
     }
 
     @Override
@@ -433,13 +465,11 @@ public class DatePicker extends FrameLayout {
      * @param dayOfMonth The day of the month.
      */
     public void updateDate(int year, int month, int dayOfMonth) {
-        if (mCurrentDate.get(Calendar.YEAR) != year
-                || mCurrentDate.get(Calendar.MONTH) != dayOfMonth
-                || mCurrentDate.get(Calendar.DAY_OF_MONTH) != month) {
-            updateSpinners(year, month, dayOfMonth);
-            updateCalendarView();
-            notifyDateChanged();
+        if (!isNewDate(year, month, dayOfMonth)) {
+            return;
         }
+        setDate(year, month, dayOfMonth);
+        notifyDateChanged();
     }
 
     // Override so we are in complete control of save / restore for this widget.
@@ -451,15 +481,14 @@ public class DatePicker extends FrameLayout {
     @Override
     protected Parcelable onSaveInstanceState() {
         Parcelable superState = super.onSaveInstanceState();
-        return new SavedState(superState, mYearSpinner.getValue(), mMonthSpinner.getValue(),
-                mDaySpinner.getValue());
+        return new SavedState(superState, getYear(), getMonth(), getDayOfMonth());
     }
 
     @Override
     protected void onRestoreInstanceState(Parcelable state) {
         SavedState ss = (SavedState) state;
         super.onRestoreInstanceState(ss.getSuperState());
-        updateSpinners(ss.mYear, ss.mMonth, ss.mDay);
+        setDate(ss.mYear, ss.mMonth, ss.mDay);
     }
 
     /**
@@ -474,10 +503,7 @@ public class DatePicker extends FrameLayout {
      */
     public void init(int year, int monthOfYear, int dayOfMonth,
             OnDateChangedListener onDateChangedListener) {
-        // make sure there is no callback
-        mOnDateChangedListener = null;
-        updateDate(year, monthOfYear, dayOfMonth);
-        // register the callback after updating the date
+        setDate(year, monthOfYear, dayOfMonth);
         mOnDateChangedListener = onDateChangedListener;
     }
 
@@ -514,104 +540,94 @@ public class DatePicker extends FrameLayout {
         }
     }
 
-    /**
-     * Updates the spinners with the given <code>year</code>, <code>month</code>
-     * , and <code>dayOfMonth</code>. If the provided values designate an
-     * inconsistent date the values are normalized before updating the spinners.
-     */
-    private void updateSpinners(int year, int month, int dayOfMonth) {
-        // compute the deltas before modifying the current date
-        int deltaMonths = getDelataMonth(month);
-        int deltaDays = getDelataDayOfMonth(dayOfMonth);
-        mCurrentDate.set(Calendar.YEAR, year);
-        mCurrentDate.add(Calendar.MONTH, deltaMonths);
-        mCurrentDate.add(Calendar.DAY_OF_MONTH, deltaDays);
+    private boolean isNewDate(int year, int month, int dayOfMonth) {
+        return (mCurrentDate.get(Calendar.YEAR) != year
+                || mCurrentDate.get(Calendar.MONTH) != dayOfMonth
+                || mCurrentDate.get(Calendar.DAY_OF_MONTH) != month);
+    }
 
+    private void setDate(int year, int month, int dayOfMonth) {
+        mCurrentDate.set(year, month, dayOfMonth);
         if (mCurrentDate.before(mMinDate)) {
             mCurrentDate.setTimeInMillis(mMinDate.getTimeInMillis());
         } else if (mCurrentDate.after(mMaxDate)) {
             mCurrentDate.setTimeInMillis(mMaxDate.getTimeInMillis());
         }
-
-        mYearSpinner.setValue(mCurrentDate.get(Calendar.YEAR));
-        mMonthSpinner.setValue(mCurrentDate.get(Calendar.MONTH));
-        mDaySpinner.setMinValue(1);
-        mDaySpinner.setMaxValue(mCurrentDate.getActualMaximum(Calendar.DAY_OF_MONTH));
-        mDaySpinner.setValue(mCurrentDate.get(Calendar.DAY_OF_MONTH));
+        updateSpinners();
+        updateCalendarView();
     }
 
-    /**
-     * @return The delta days of moth from the current date and the given
-     *         <code>dayOfMonth</code>.
-     */
-    private int getDelataDayOfMonth(int dayOfMonth) {
-        int prevDayOfMonth = mCurrentDate.get(Calendar.DAY_OF_MONTH);
-        if (prevDayOfMonth == dayOfMonth) {
-            return 0;
-        }
-        int maxDayOfMonth = mCurrentDate.getActualMaximum(Calendar.DAY_OF_MONTH);
-        if (dayOfMonth == 1 && prevDayOfMonth == maxDayOfMonth) {
-            return 1;
-        }
-        if (dayOfMonth == maxDayOfMonth && prevDayOfMonth == 1) {
-            return -1;
+    private void updateSpinners() {
+        // set the spinner ranges respecting the min and max dates
+        if (mCurrentDate.equals(mMinDate)) {
+            mDaySpinner.setMinValue(mCurrentDate.get(Calendar.DAY_OF_MONTH));
+            mDaySpinner.setMaxValue(mCurrentDate.getActualMaximum(Calendar.DAY_OF_MONTH));
+            mDaySpinner.setWrapSelectorWheel(false);
+            mMonthSpinner.setDisplayedValues(null);
+            mMonthSpinner.setMinValue(mCurrentDate.get(Calendar.MONTH));
+            mMonthSpinner.setMaxValue(mCurrentDate.getActualMaximum(Calendar.MONTH));
+            mMonthSpinner.setWrapSelectorWheel(false);
+        } else if (mCurrentDate.equals(mMaxDate)) {
+            mDaySpinner.setMinValue(mCurrentDate.getActualMinimum(Calendar.DAY_OF_MONTH));
+            mDaySpinner.setMaxValue(mCurrentDate.get(Calendar.DAY_OF_MONTH));
+            mDaySpinner.setWrapSelectorWheel(false);
+            mMonthSpinner.setDisplayedValues(null);
+            mMonthSpinner.setMinValue(mCurrentDate.getActualMinimum(Calendar.MONTH));
+            mMonthSpinner.setMaxValue(mCurrentDate.get(Calendar.MONTH));
+            mMonthSpinner.setWrapSelectorWheel(false);
+        } else {
+            mDaySpinner.setMinValue(1);
+            mDaySpinner.setMaxValue(mCurrentDate.getActualMaximum(Calendar.DAY_OF_MONTH));
+            mDaySpinner.setWrapSelectorWheel(true);
+            mMonthSpinner.setDisplayedValues(null);
+            mMonthSpinner.setMinValue(0);
+            mMonthSpinner.setMaxValue(11);
+            mMonthSpinner.setWrapSelectorWheel(true);
         }
-        return dayOfMonth - prevDayOfMonth;
-    }
 
-    /**
-     * @return The delta months from the current date and the given
-     *         <code>month</code>.
-     */
-    private int getDelataMonth(int month) {
-        int prevMonth = mCurrentDate.get(Calendar.MONTH);
-        if (prevMonth == month) {
-            return 0;
-        }
-        if (month == 0 && prevMonth == 11) {
-            return 1;
-        }
-        if (month == 11 && prevMonth == 0) {
-            return -1;
-        }
-        return month - prevMonth;
+        // make sure the month names are a zero based array
+        // with the months in the month spinner
+        String[] displayedValues = Arrays.copyOfRange(getShortMonths(),
+                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));
+        mDaySpinner.setValue(mCurrentDate.get(Calendar.DAY_OF_MONTH));
     }
 
     /**
-     * Updates the calendar view with the given year, month, and day selected by
-     * the number spinners.
+     * Updates the calendar view with the current date.
      */
     private void updateCalendarView() {
-        mTempDate.setTimeInMillis(mCalendarView.getDate());
-        if (mTempDate.get(Calendar.YEAR) != mYearSpinner.getValue()
-                || mTempDate.get(Calendar.MONTH) != mMonthSpinner.getValue()
-                || mTempDate.get(Calendar.DAY_OF_MONTH) != mDaySpinner.getValue()) {
-            mTempDate.clear();
-            mTempDate.set(mYearSpinner.getValue(), mMonthSpinner.getValue(),
-                    mDaySpinner.getValue());
-            mCalendarView.setDate(mTempDate.getTimeInMillis(), false, false);
-        }
+         mCalendarView.setDate(mCurrentDate.getTimeInMillis(), false, false);
     }
 
     /**
      * @return The selected year.
      */
     public int getYear() {
-        return mYearSpinner.getValue();
+        return mCurrentDate.get(Calendar.YEAR);
     }
 
     /**
      * @return The selected month.
      */
     public int getMonth() {
-        return mMonthSpinner.getValue();
+        return mCurrentDate.get(Calendar.MONTH);
     }
 
     /**
      * @return The selected day of month.
      */
     public int getDayOfMonth() {
-        return mDaySpinner.getValue();
+        return mCurrentDate.get(Calendar.DAY_OF_MONTH);
     }
 
     /**
@@ -619,8 +635,7 @@ public class DatePicker extends FrameLayout {
      */
     private void notifyDateChanged() {
         if (mOnDateChangedListener != null) {
-            mOnDateChangedListener.onDateChanged(DatePicker.this, mYearSpinner.getValue(),
-                    mMonthSpinner.getValue(), mDaySpinner.getValue());
+            mOnDateChangedListener.onDateChanged(this, getYear(), getMonth(), getDayOfMonth());
         }
     }
 
index 08db207..c5161bc 100644 (file)
@@ -917,6 +917,8 @@ public class NumberPicker extends LinearLayout {
             // force the selector indices array to be reinitialized
             mSelectorIndices[SELECTOR_MIDDLE_ITEM_INDEX] = Integer.MAX_VALUE;
             mWrapSelectorWheel = wrapSelector;
+            // force redraw since we might look different
+            updateIncrementAndDecrementButtonsVisibilityState();
         }
     }
 
@@ -972,7 +974,6 @@ public class NumberPicker extends LinearLayout {
         setWrapSelectorWheel(wrapSelectorWheel);
         resetSelectorWheelIndices();
         updateInputTextView();
-        updateIncrementAndDecrementButtonsVisibilityState();
     }
 
     /**
@@ -1004,7 +1005,6 @@ public class NumberPicker extends LinearLayout {
         setWrapSelectorWheel(wrapSelectorWheel);
         resetSelectorWheelIndices();
         updateInputTextView();
-        updateIncrementAndDecrementButtonsVisibilityState();
     }
 
     /**