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