DatePicker.java revision bc006dc0aa1c149508a3196a40bac62cf1b33b84
1/* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.widget; 18 19import com.android.internal.R; 20 21import android.annotation.Widget; 22import android.content.Context; 23import android.content.res.TypedArray; 24import android.os.Parcel; 25import android.os.Parcelable; 26import android.text.TextUtils; 27import android.text.format.DateFormat; 28import android.text.format.DateUtils; 29import android.util.AttributeSet; 30import android.util.Log; 31import android.util.SparseArray; 32import android.view.LayoutInflater; 33import android.view.accessibility.AccessibilityEvent; 34import android.widget.NumberPicker.OnValueChangeListener; 35 36import java.text.ParseException; 37import java.text.SimpleDateFormat; 38import java.util.Arrays; 39import java.util.Calendar; 40import java.util.Locale; 41import java.util.TimeZone; 42 43/** 44 * This class is a widget for selecting a date. The date can be selected by a 45 * year, month, and day spinners or a {@link CalendarView}. The set of spinners 46 * and the calendar view are automatically synchronized. The client can 47 * customize whether only the spinners, or only the calendar view, or both to be 48 * displayed. Also the minimal and maximal date from which dates to be selected 49 * can be customized. 50 * <p> 51 * See the <a href="{@docRoot}resources/tutorials/views/hello-datepicker.html">Date 52 * Picker tutorial</a>. 53 * </p> 54 * <p> 55 * For a dialog using this view, see {@link android.app.DatePickerDialog}. 56 * </p> 57 * 58 * @attr ref android.R.styleable#DatePicker_startYear 59 * @attr ref android.R.styleable#DatePicker_endYear 60 * @attr ref android.R.styleable#DatePicker_maxDate 61 * @attr ref android.R.styleable#DatePicker_minDate 62 * @attr ref android.R.styleable#DatePicker_spinnersShown 63 * @attr ref android.R.styleable#DatePicker_calendarViewShown 64 */ 65@Widget 66public class DatePicker extends FrameLayout { 67 68 private static final String LOG_TAG = DatePicker.class.getSimpleName(); 69 70 private static final String DATE_FORMAT = "MM/dd/yyyy"; 71 72 private static final int DEFAULT_START_YEAR = 1900; 73 74 private static final int DEFAULT_END_YEAR = 2100; 75 76 private static final boolean DEFAULT_CALENDAR_VIEW_SHOWN = true; 77 78 private static final boolean DEFAULT_SPINNERS_SHOWN = true; 79 80 private static final boolean DEFAULT_ENABLED_STATE = true; 81 82 private final NumberPicker mDaySpinner; 83 84 private final LinearLayout mSpinners; 85 86 private final NumberPicker mMonthSpinner; 87 88 private final NumberPicker mYearSpinner; 89 90 private final CalendarView mCalendarView; 91 92 private OnDateChangedListener mOnDateChangedListener; 93 94 private Locale mMonthLocale; 95 96 private final Calendar mTempDate = Calendar.getInstance(); 97 98 private final int mNumberOfMonths = mTempDate.getActualMaximum(Calendar.MONTH) + 1; 99 100 private final String[] mShortMonths = new String[mNumberOfMonths]; 101 102 private final java.text.DateFormat mDateFormat = new SimpleDateFormat(DATE_FORMAT); 103 104 private final Calendar mMinDate = Calendar.getInstance(); 105 106 private final Calendar mMaxDate = Calendar.getInstance(); 107 108 private final Calendar mCurrentDate = Calendar.getInstance(); 109 110 private boolean mIsEnabled = DEFAULT_ENABLED_STATE; 111 112 /** 113 * The callback used to indicate the user changes\d the date. 114 */ 115 public interface OnDateChangedListener { 116 117 /** 118 * Called upon a date change. 119 * 120 * @param view The view associated with this listener. 121 * @param year The year that was set. 122 * @param monthOfYear The month that was set (0-11) for compatibility 123 * with {@link java.util.Calendar}. 124 * @param dayOfMonth The day of the month that was set. 125 */ 126 void onDateChanged(DatePicker view, int year, int monthOfYear, int dayOfMonth); 127 } 128 129 public DatePicker(Context context) { 130 this(context, null); 131 } 132 133 public DatePicker(Context context, AttributeSet attrs) { 134 this(context, attrs, R.attr.datePickerStyle); 135 } 136 137 public DatePicker(Context context, AttributeSet attrs, int defStyle) { 138 super(context, attrs, defStyle); 139 140 TypedArray attributesArray = context.obtainStyledAttributes(attrs, R.styleable.DatePicker, 141 defStyle, 0); 142 boolean spinnersShown = attributesArray.getBoolean(R.styleable.DatePicker_spinnersShown, 143 DEFAULT_SPINNERS_SHOWN); 144 boolean calendarViewShown = attributesArray.getBoolean( 145 R.styleable.DatePicker_calendarViewShown, DEFAULT_CALENDAR_VIEW_SHOWN); 146 int startYear = attributesArray.getInt(R.styleable.DatePicker_startYear, 147 DEFAULT_START_YEAR); 148 int endYear = attributesArray.getInt(R.styleable.DatePicker_endYear, DEFAULT_END_YEAR); 149 String minDate = attributesArray.getString(R.styleable.DatePicker_minDate); 150 String maxDate = attributesArray.getString(R.styleable.DatePicker_maxDate); 151 int layoutResourceId = attributesArray.getResourceId(R.styleable.DatePicker_layout, 152 R.layout.date_picker); 153 attributesArray.recycle(); 154 155 LayoutInflater inflater = (LayoutInflater) context 156 .getSystemService(Context.LAYOUT_INFLATER_SERVICE); 157 inflater.inflate(layoutResourceId, this, true); 158 159 OnValueChangeListener onChangeListener = new OnValueChangeListener() { 160 public void onValueChange(NumberPicker picker, int oldVal, int newVal) { 161 mTempDate.setTimeInMillis(mCurrentDate.getTimeInMillis()); 162 // take care of wrapping of days and months to update greater fields 163 if (picker == mDaySpinner) { 164 int maxDayOfMonth = mTempDate.getActualMaximum(Calendar.DAY_OF_MONTH); 165 if (oldVal == maxDayOfMonth && newVal == 1) { 166 mTempDate.add(Calendar.DAY_OF_MONTH, 1); 167 } else if (oldVal == 1 && newVal == maxDayOfMonth) { 168 mTempDate.add(Calendar.DAY_OF_MONTH, -1); 169 } else { 170 mTempDate.add(Calendar.DAY_OF_MONTH, newVal - oldVal); 171 } 172 } else if (picker == mMonthSpinner) { 173 if (oldVal == 11 && newVal == 0) { 174 mTempDate.add(Calendar.MONTH, 1); 175 } else if (oldVal == 0 && newVal == 11) { 176 mTempDate.add(Calendar.MONTH, -1); 177 } else { 178 mTempDate.add(Calendar.MONTH, newVal - oldVal); 179 } 180 } else if (picker == mYearSpinner) { 181 mTempDate.set(Calendar.YEAR, newVal); 182 } else { 183 throw new IllegalArgumentException(); 184 } 185 // now set the date to the adjusted one 186 setDate(mTempDate.get(Calendar.YEAR), mTempDate.get(Calendar.MONTH), 187 mTempDate.get(Calendar.DAY_OF_MONTH)); 188 updateSpinners(); 189 updateCalendarView(); 190 notifyDateChanged(); 191 } 192 }; 193 194 mSpinners = (LinearLayout) findViewById(R.id.pickers); 195 196 // calendar view day-picker 197 mCalendarView = (CalendarView) findViewById(R.id.calendar_view); 198 mCalendarView.setOnDateChangeListener(new CalendarView.OnDateChangeListener() { 199 public void onSelectedDayChange(CalendarView view, int year, int month, int monthDay) { 200 setDate(year, month, monthDay); 201 updateSpinners(); 202 notifyDateChanged(); 203 } 204 }); 205 206 // day 207 mDaySpinner = (NumberPicker) findViewById(R.id.day); 208 mDaySpinner.setFormatter(NumberPicker.TWO_DIGIT_FORMATTER); 209 mDaySpinner.setOnLongPressUpdateInterval(100); 210 mDaySpinner.setOnValueChangedListener(onChangeListener); 211 212 // month 213 mMonthSpinner = (NumberPicker) findViewById(R.id.month); 214 mMonthSpinner.setMinValue(0); 215 mMonthSpinner.setMaxValue(mNumberOfMonths - 1); 216 mMonthSpinner.setDisplayedValues(getShortMonths()); 217 mMonthSpinner.setOnLongPressUpdateInterval(200); 218 mMonthSpinner.setOnValueChangedListener(onChangeListener); 219 220 // year 221 mYearSpinner = (NumberPicker) findViewById(R.id.year); 222 mYearSpinner.setOnLongPressUpdateInterval(100); 223 mYearSpinner.setOnValueChangedListener(onChangeListener); 224 225 // show only what the user required but make sure we 226 // show something and the spinners have higher priority 227 if (!spinnersShown && !calendarViewShown) { 228 setSpinnersShown(true); 229 } else { 230 setSpinnersShown(spinnersShown); 231 setCalendarViewShown(calendarViewShown); 232 } 233 234 // set the min date giving priority of the minDate over startYear 235 mTempDate.clear(); 236 if (!TextUtils.isEmpty(minDate)) { 237 if (!parseDate(minDate, mTempDate)) { 238 mTempDate.set(startYear, 0, 1); 239 } 240 } else { 241 mTempDate.set(startYear, 0, 1); 242 } 243 setMinDate(mTempDate.getTimeInMillis()); 244 245 // set the max date giving priority of the maxDate over endYear 246 mTempDate.clear(); 247 if (!TextUtils.isEmpty(maxDate)) { 248 if (!parseDate(maxDate, mTempDate)) { 249 mTempDate.set(endYear, 11, 31); 250 } 251 } else { 252 mTempDate.set(endYear, 11, 31); 253 } 254 setMaxDate(mTempDate.getTimeInMillis()); 255 256 // initialize to current date 257 mCurrentDate.setTimeInMillis(System.currentTimeMillis()); 258 init(mCurrentDate.get(Calendar.YEAR), mCurrentDate.get(Calendar.MONTH), mCurrentDate 259 .get(Calendar.DAY_OF_MONTH), null); 260 261 // re-order the number spinners to match the current date format 262 reorderSpinners(); 263 } 264 265 /** 266 * Gets the minimal date supported by this {@link DatePicker} in 267 * milliseconds since January 1, 1970 00:00:00 in 268 * {@link TimeZone#getDefault()} time zone. 269 * <p> 270 * Note: The default minimal date is 01/01/1900. 271 * <p> 272 * 273 * @return The minimal supported date. 274 */ 275 public long getMinDate() { 276 return mCalendarView.getMinDate(); 277 } 278 279 /** 280 * Sets the minimal date supported by this {@link NumberPicker} in 281 * milliseconds since January 1, 1970 00:00:00 in 282 * {@link TimeZone#getDefault()} time zone. 283 * 284 * @param minDate The minimal supported date. 285 */ 286 public void setMinDate(long minDate) { 287 mTempDate.setTimeInMillis(minDate); 288 if (mTempDate.get(Calendar.YEAR) == mMinDate.get(Calendar.YEAR) 289 && mTempDate.get(Calendar.DAY_OF_YEAR) != mMinDate.get(Calendar.DAY_OF_YEAR)) { 290 return; 291 } 292 mMinDate.setTimeInMillis(minDate); 293 mCalendarView.setMinDate(minDate); 294 if (mCurrentDate.before(mMinDate)) { 295 mCurrentDate.setTimeInMillis(mMinDate.getTimeInMillis()); 296 updateCalendarView(); 297 } 298 updateSpinners(); 299 } 300 301 /** 302 * Gets the maximal date supported by this {@link DatePicker} in 303 * milliseconds since January 1, 1970 00:00:00 in 304 * {@link TimeZone#getDefault()} time zone. 305 * <p> 306 * Note: The default maximal date is 12/31/2100. 307 * <p> 308 * 309 * @return The maximal supported date. 310 */ 311 public long getMaxDate() { 312 return mCalendarView.getMaxDate(); 313 } 314 315 /** 316 * Sets the maximal date supported by this {@link DatePicker} in 317 * milliseconds since January 1, 1970 00:00:00 in 318 * {@link TimeZone#getDefault()} time zone. 319 * 320 * @param maxDate The maximal supported date. 321 */ 322 public void setMaxDate(long maxDate) { 323 mTempDate.setTimeInMillis(maxDate); 324 if (mTempDate.get(Calendar.YEAR) == mMaxDate.get(Calendar.YEAR) 325 && mTempDate.get(Calendar.DAY_OF_YEAR) != mMaxDate.get(Calendar.DAY_OF_YEAR)) { 326 return; 327 } 328 mMaxDate.setTimeInMillis(maxDate); 329 mCalendarView.setMaxDate(maxDate); 330 if (mCurrentDate.after(mMaxDate)) { 331 mCurrentDate.setTimeInMillis(mMaxDate.getTimeInMillis()); 332 updateCalendarView(); 333 } 334 updateSpinners(); 335 } 336 337 @Override 338 public void setEnabled(boolean enabled) { 339 if (mIsEnabled == enabled) { 340 return; 341 } 342 super.setEnabled(enabled); 343 mDaySpinner.setEnabled(enabled); 344 mMonthSpinner.setEnabled(enabled); 345 mYearSpinner.setEnabled(enabled); 346 mCalendarView.setEnabled(enabled); 347 mIsEnabled = enabled; 348 } 349 350 @Override 351 public boolean isEnabled() { 352 return mIsEnabled; 353 } 354 355 @Override 356 public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { 357 int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_WEEKDAY 358 | DateUtils.FORMAT_SHOW_YEAR; 359 String selectedDateUtterance = DateUtils.formatDateTime(mContext, 360 mCurrentDate.getTimeInMillis(), flags); 361 event.getText().add(selectedDateUtterance); 362 return true; 363 } 364 365 /** 366 * Gets whether the {@link CalendarView} is shown. 367 * 368 * @return True if the calendar view is shown. 369 * @see #getCalendarView() 370 */ 371 public boolean getCalendarViewShown() { 372 return mCalendarView.isShown(); 373 } 374 375 /** 376 * Gets the {@link CalendarView}. 377 * 378 * @return The calendar view. 379 * @see #getCalendarViewShown() 380 */ 381 public CalendarView getCalendarView () { 382 return mCalendarView; 383 } 384 385 /** 386 * Sets whether the {@link CalendarView} is shown. 387 * 388 * @param shown True if the calendar view is to be shown. 389 */ 390 public void setCalendarViewShown(boolean shown) { 391 mCalendarView.setVisibility(shown ? VISIBLE : GONE); 392 } 393 394 /** 395 * Gets whether the spinners are shown. 396 * 397 * @return True if the spinners are shown. 398 */ 399 public boolean getSpinnersShown() { 400 return mSpinners.isShown(); 401 } 402 403 /** 404 * Sets whether the spinners are shown. 405 * 406 * @param shown True if the spinners are to be shown. 407 */ 408 public void setSpinnersShown(boolean shown) { 409 mSpinners.setVisibility(shown ? VISIBLE : GONE); 410 } 411 412 /** 413 * Reorders the spinners according to the date format in the current 414 * {@link Locale}. 415 */ 416 private void reorderSpinners() { 417 java.text.DateFormat format; 418 String order; 419 420 /* 421 * If the user is in a locale where the medium date format is still 422 * numeric (Japanese and Czech, for example), respect the date format 423 * order setting. Otherwise, use the order that the locale says is 424 * appropriate for a spelled-out date. 425 */ 426 427 if (getShortMonths()[0].startsWith("1")) { 428 format = DateFormat.getDateFormat(getContext()); 429 } else { 430 format = DateFormat.getMediumDateFormat(getContext()); 431 } 432 433 if (format instanceof SimpleDateFormat) { 434 order = ((SimpleDateFormat) format).toPattern(); 435 } else { 436 // Shouldn't happen, but just in case. 437 order = new String(DateFormat.getDateFormatOrder(getContext())); 438 } 439 440 /* 441 * Remove the 3 spinners from their parent and then add them back in the 442 * required order. 443 */ 444 LinearLayout parent = mSpinners; 445 parent.removeAllViews(); 446 447 boolean quoted = false; 448 boolean didDay = false, didMonth = false, didYear = false; 449 450 for (int i = 0; i < order.length(); i++) { 451 char c = order.charAt(i); 452 453 if (c == '\'') { 454 quoted = !quoted; 455 } 456 457 if (!quoted) { 458 if (c == DateFormat.DATE && !didDay) { 459 parent.addView(mDaySpinner); 460 didDay = true; 461 } else if ((c == DateFormat.MONTH || c == 'L') && !didMonth) { 462 parent.addView(mMonthSpinner); 463 didMonth = true; 464 } else if (c == DateFormat.YEAR && !didYear) { 465 parent.addView(mYearSpinner); 466 didYear = true; 467 } 468 } 469 } 470 471 // Shouldn't happen, but just in case. 472 if (!didMonth) { 473 parent.addView(mMonthSpinner); 474 } 475 if (!didDay) { 476 parent.addView(mDaySpinner); 477 } 478 if (!didYear) { 479 parent.addView(mYearSpinner); 480 } 481 } 482 483 /** 484 * Updates the current date. 485 * 486 * @param year The year. 487 * @param month The month which is <strong>starting from zero</strong>. 488 * @param dayOfMonth The day of the month. 489 */ 490 public void updateDate(int year, int month, int dayOfMonth) { 491 if (!isNewDate(year, month, dayOfMonth)) { 492 return; 493 } 494 setDate(year, month, dayOfMonth); 495 updateSpinners(); 496 updateCalendarView(); 497 notifyDateChanged(); 498 } 499 500 // Override so we are in complete control of save / restore for this widget. 501 @Override 502 protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) { 503 dispatchThawSelfOnly(container); 504 } 505 506 @Override 507 protected Parcelable onSaveInstanceState() { 508 Parcelable superState = super.onSaveInstanceState(); 509 return new SavedState(superState, getYear(), getMonth(), getDayOfMonth()); 510 } 511 512 @Override 513 protected void onRestoreInstanceState(Parcelable state) { 514 SavedState ss = (SavedState) state; 515 super.onRestoreInstanceState(ss.getSuperState()); 516 setDate(ss.mYear, ss.mMonth, ss.mDay); 517 updateSpinners(); 518 updateCalendarView(); 519 } 520 521 /** 522 * Initialize the state. If the provided values designate an inconsistent 523 * date the values are normalized before updating the spinners. 524 * 525 * @param year The initial year. 526 * @param monthOfYear The initial month <strong>starting from zero</strong>. 527 * @param dayOfMonth The initial day of the month. 528 * @param onDateChangedListener How user is notified date is changed by 529 * user, can be null. 530 */ 531 public void init(int year, int monthOfYear, int dayOfMonth, 532 OnDateChangedListener onDateChangedListener) { 533 setDate(year, monthOfYear, dayOfMonth); 534 updateSpinners(); 535 updateCalendarView(); 536 mOnDateChangedListener = onDateChangedListener; 537 } 538 539 /** 540 * Parses the given <code>date</code> and in case of success sets the result 541 * to the <code>outDate</code>. 542 * 543 * @return True if the date was parsed. 544 */ 545 private boolean parseDate(String date, Calendar outDate) { 546 try { 547 outDate.setTime(mDateFormat.parse(date)); 548 return true; 549 } catch (ParseException e) { 550 Log.w(LOG_TAG, "Date: " + date + " not in format: " + DATE_FORMAT); 551 return false; 552 } 553 } 554 555 /** 556 * @return The short month abbreviations. 557 */ 558 private String[] getShortMonths() { 559 final Locale currentLocale = Locale.getDefault(); 560 if (currentLocale.equals(mMonthLocale)) { 561 return mShortMonths; 562 } else { 563 for (int i = 0; i < mNumberOfMonths; i++) { 564 mShortMonths[i] = DateUtils.getMonthString(Calendar.JANUARY + i, 565 DateUtils.LENGTH_MEDIUM); 566 } 567 mMonthLocale = currentLocale; 568 return mShortMonths; 569 } 570 } 571 572 private boolean isNewDate(int year, int month, int dayOfMonth) { 573 return (mCurrentDate.get(Calendar.YEAR) != year 574 || mCurrentDate.get(Calendar.MONTH) != dayOfMonth 575 || mCurrentDate.get(Calendar.DAY_OF_MONTH) != month); 576 } 577 578 private void setDate(int year, int month, int dayOfMonth) { 579 mCurrentDate.set(year, month, dayOfMonth); 580 if (mCurrentDate.before(mMinDate)) { 581 mCurrentDate.setTimeInMillis(mMinDate.getTimeInMillis()); 582 } else if (mCurrentDate.after(mMaxDate)) { 583 mCurrentDate.setTimeInMillis(mMaxDate.getTimeInMillis()); 584 } 585 } 586 587 private void updateSpinners() { 588 // set the spinner ranges respecting the min and max dates 589 if (mCurrentDate.equals(mMinDate)) { 590 mDaySpinner.setMinValue(mCurrentDate.get(Calendar.DAY_OF_MONTH)); 591 mDaySpinner.setMaxValue(mCurrentDate.getActualMaximum(Calendar.DAY_OF_MONTH)); 592 mDaySpinner.setWrapSelectorWheel(false); 593 mMonthSpinner.setDisplayedValues(null); 594 mMonthSpinner.setMinValue(mCurrentDate.get(Calendar.MONTH)); 595 mMonthSpinner.setMaxValue(mCurrentDate.getActualMaximum(Calendar.MONTH)); 596 mMonthSpinner.setWrapSelectorWheel(false); 597 } else if (mCurrentDate.equals(mMaxDate)) { 598 mDaySpinner.setMinValue(mCurrentDate.getActualMinimum(Calendar.DAY_OF_MONTH)); 599 mDaySpinner.setMaxValue(mCurrentDate.get(Calendar.DAY_OF_MONTH)); 600 mDaySpinner.setWrapSelectorWheel(false); 601 mMonthSpinner.setDisplayedValues(null); 602 mMonthSpinner.setMinValue(mCurrentDate.getActualMinimum(Calendar.MONTH)); 603 mMonthSpinner.setMaxValue(mCurrentDate.get(Calendar.MONTH)); 604 mMonthSpinner.setWrapSelectorWheel(false); 605 } else { 606 mDaySpinner.setMinValue(1); 607 mDaySpinner.setMaxValue(mCurrentDate.getActualMaximum(Calendar.DAY_OF_MONTH)); 608 mDaySpinner.setWrapSelectorWheel(true); 609 mMonthSpinner.setDisplayedValues(null); 610 mMonthSpinner.setMinValue(0); 611 mMonthSpinner.setMaxValue(11); 612 mMonthSpinner.setWrapSelectorWheel(true); 613 } 614 615 // make sure the month names are a zero based array 616 // with the months in the month spinner 617 String[] displayedValues = Arrays.copyOfRange(getShortMonths(), 618 mMonthSpinner.getMinValue(), mMonthSpinner.getMaxValue() + 1); 619 mMonthSpinner.setDisplayedValues(displayedValues); 620 621 // year spinner range does not change based on the current date 622 mYearSpinner.setMinValue(mMinDate.get(Calendar.YEAR)); 623 mYearSpinner.setMaxValue(mMaxDate.get(Calendar.YEAR)); 624 mYearSpinner.setWrapSelectorWheel(false); 625 626 // set the spinner values 627 mYearSpinner.setValue(mCurrentDate.get(Calendar.YEAR)); 628 mMonthSpinner.setValue(mCurrentDate.get(Calendar.MONTH)); 629 mDaySpinner.setValue(mCurrentDate.get(Calendar.DAY_OF_MONTH)); 630 } 631 632 /** 633 * Updates the calendar view with the current date. 634 */ 635 private void updateCalendarView() { 636 mCalendarView.setDate(mCurrentDate.getTimeInMillis(), false, false); 637 } 638 639 /** 640 * @return The selected year. 641 */ 642 public int getYear() { 643 return mCurrentDate.get(Calendar.YEAR); 644 } 645 646 /** 647 * @return The selected month. 648 */ 649 public int getMonth() { 650 return mCurrentDate.get(Calendar.MONTH); 651 } 652 653 /** 654 * @return The selected day of month. 655 */ 656 public int getDayOfMonth() { 657 return mCurrentDate.get(Calendar.DAY_OF_MONTH); 658 } 659 660 /** 661 * Notifies the listener, if such, for a change in the selected date. 662 */ 663 private void notifyDateChanged() { 664 sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED); 665 if (mOnDateChangedListener != null) { 666 mOnDateChangedListener.onDateChanged(this, getYear(), getMonth(), getDayOfMonth()); 667 } 668 } 669 670 /** 671 * Class for managing state storing/restoring. 672 */ 673 private static class SavedState extends BaseSavedState { 674 675 private final int mYear; 676 677 private final int mMonth; 678 679 private final int mDay; 680 681 /** 682 * Constructor called from {@link DatePicker#onSaveInstanceState()} 683 */ 684 private SavedState(Parcelable superState, int year, int month, int day) { 685 super(superState); 686 mYear = year; 687 mMonth = month; 688 mDay = day; 689 } 690 691 /** 692 * Constructor called from {@link #CREATOR} 693 */ 694 private SavedState(Parcel in) { 695 super(in); 696 mYear = in.readInt(); 697 mMonth = in.readInt(); 698 mDay = in.readInt(); 699 } 700 701 @Override 702 public void writeToParcel(Parcel dest, int flags) { 703 super.writeToParcel(dest, flags); 704 dest.writeInt(mYear); 705 dest.writeInt(mMonth); 706 dest.writeInt(mDay); 707 } 708 709 @SuppressWarnings("all") 710 // suppress unused and hiding 711 public static final Parcelable.Creator<SavedState> CREATOR = new Creator<SavedState>() { 712 713 public SavedState createFromParcel(Parcel in) { 714 return new SavedState(in); 715 } 716 717 public SavedState[] newArray(int size) { 718 return new SavedState[size]; 719 } 720 }; 721 } 722} 723 724