DatePickerDialog.java revision f6de1f602ffac70987ebc9fc5e887494a23ddd35
1/* 2 * Copyright (C) 2013 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 com.android.datetimepicker.date; 18 19import android.app.Activity; 20import android.app.DialogFragment; 21import android.os.Bundle; 22import android.util.Log; 23import android.view.LayoutInflater; 24import android.view.View; 25import android.view.View.OnClickListener; 26import android.view.ViewGroup; 27import android.view.Window; 28import android.view.WindowManager; 29import android.view.animation.AlphaAnimation; 30import android.view.animation.Animation; 31import android.widget.Button; 32import android.widget.LinearLayout; 33import android.widget.TextView; 34import android.widget.ViewAnimator; 35 36import com.android.datetimepicker.R; 37import com.android.datetimepicker.Utils; 38import com.android.datetimepicker.date.SimpleMonthAdapter.CalendarDay; 39 40import java.text.SimpleDateFormat; 41import java.util.Calendar; 42import java.util.Locale; 43 44/** 45 * Dialog allowing users to select a date. 46 */ 47public class DatePickerDialog extends DialogFragment implements 48 OnClickListener, DatePickerController { 49 50 private static final String TAG = "DatePickerDialog"; 51 52 private static final int TOTAL_VIEWS = 2; 53 54 private static final int MONTH_AND_DAY_VIEW = 0; 55 private static final int YEAR_VIEW = 1; 56 57 private static final String KEY_SELECTED_YEAR = "year"; 58 private static final String KEY_SELECTED_MONTH = "month"; 59 private static final String KEY_SELECTED_DAY = "day"; 60 private static final String KEY_LIST_POSITION = "position"; 61 private static final String KEY_WEEK_START = "week_start"; 62 private static final String KEY_YEAR_START = "year_start"; 63 private static final String KEY_YEAR_END = "year_end"; 64 private static final String KEY_CURRENT_VIEW = "current_view"; 65 66 private static final int DEFAULT_START_YEAR = 1900; 67 private static final int DEFAULT_END_YEAR = 2100; 68 69 private static final int ANIMATION_DURATION = 500; 70 71 private static SimpleDateFormat YEAR_FORMAT = new SimpleDateFormat("yyyy", Locale.getDefault()); 72 private static SimpleDateFormat DAY_FORMAT = new SimpleDateFormat("dd", Locale.getDefault()); 73 74 private final Calendar mCalendar = Calendar.getInstance(); 75 private OnDateSetListener mCallBack; 76 77 private ViewAnimator mAnimator; 78 79 private TextView mDayOfWeekView; 80 private LinearLayout mMonthAndDayView; 81 private TextView mSelectedMonthTextView; 82 private TextView mSelectedDayTextView; 83 private TextView mYearView; 84 private DayPickerView mDayPickerView; 85 private YearPickerView mYearPickerView; 86 private Button mDoneButton; 87 88 private int mCurrentView; 89 90 private int mWeekStart = mCalendar.getFirstDayOfWeek(); 91 private int mMinYear = DEFAULT_START_YEAR; 92 private int mMaxYear = DEFAULT_END_YEAR; 93 94 private final View[] mViews = new View[TOTAL_VIEWS]; 95 96 /** 97 * The callback used to indicate the user is done filling in the date. 98 */ 99 public interface OnDateSetListener { 100 101 /** 102 * @param view The view associated with this listener. 103 * @param year The year that was set. 104 * @param monthOfYear The month that was set (0-11) for compatibility 105 * with {@link java.util.Calendar}. 106 * @param dayOfMonth The day of the month that was set. 107 */ 108 void onDateSet(DatePickerDialog dialog, int year, int monthOfYear, int dayOfMonth); 109 } 110 111 public DatePickerDialog() { 112 // Empty constructor required for dialog fragment. 113 } 114 115 /** 116 * @param callBack How the parent is notified that the date is set. 117 * @param year The initial year of the dialog. 118 * @param monthOfYear The initial month of the dialog. 119 * @param dayOfMonth The initial day of the dialog. 120 */ 121 public static DatePickerDialog newInstance(OnDateSetListener callBack, int year, 122 int monthOfYear, 123 int dayOfMonth) { 124 DatePickerDialog ret = new DatePickerDialog(); 125 ret.initialize(callBack, year, monthOfYear, dayOfMonth); 126 return ret; 127 } 128 129 public void initialize(OnDateSetListener callBack, int year, int monthOfYear, int dayOfMonth) { 130 mCallBack = callBack; 131 mCalendar.set(Calendar.YEAR, year); 132 mCalendar.set(Calendar.MONTH, monthOfYear); 133 mCalendar.set(Calendar.DAY_OF_MONTH, dayOfMonth); 134 } 135 136 @Override 137 public void onCreate(Bundle savedInstanceState) { 138 super.onCreate(savedInstanceState); 139 getActivity().getWindow().setSoftInputMode( 140 WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN); 141 if (savedInstanceState != null) { 142 mCalendar.set(Calendar.YEAR, savedInstanceState.getInt(KEY_SELECTED_YEAR)); 143 mCalendar.set(Calendar.MONTH, savedInstanceState.getInt(KEY_SELECTED_MONTH)); 144 mCalendar.set(Calendar.DAY_OF_MONTH, savedInstanceState.getInt(KEY_SELECTED_DAY)); 145 } 146 } 147 148 @Override 149 public void onSaveInstanceState(Bundle outState) { 150 super.onSaveInstanceState(outState); 151 outState.putInt(KEY_SELECTED_YEAR, mCalendar.get(Calendar.YEAR)); 152 outState.putInt(KEY_SELECTED_MONTH, mCalendar.get(Calendar.MONTH)); 153 outState.putInt(KEY_SELECTED_DAY, mCalendar.get(Calendar.DAY_OF_MONTH)); 154 outState.putInt(KEY_LIST_POSITION, mDayPickerView.getFirstVisiblePosition()); 155 outState.putInt(KEY_WEEK_START, mWeekStart); 156 outState.putInt(KEY_YEAR_START, mMinYear); 157 outState.putInt(KEY_YEAR_END, mMaxYear); 158 outState.putInt(KEY_CURRENT_VIEW, mCurrentView); 159 } 160 161 @Override 162 public View onCreateView(LayoutInflater inflater, ViewGroup container, 163 Bundle savedInstanceState) { 164 Log.d(TAG, "onCreateView: "); 165 getDialog().getWindow().requestFeature(Window.FEATURE_NO_TITLE); 166 167 View view = inflater.inflate(R.layout.date_picker_dialog, null); 168 169 mDayOfWeekView = (TextView) view.findViewById(R.id.date_picker_header); 170 mMonthAndDayView = (LinearLayout) view.findViewById(R.id.date_picker_month_and_day); 171 mMonthAndDayView.setOnClickListener(this); 172 mSelectedMonthTextView = (TextView) view.findViewById(R.id.date_picker_month); 173 mSelectedDayTextView = (TextView) view.findViewById(R.id.date_picker_day); 174 mYearView = (TextView) view.findViewById(R.id.date_picker_year); 175 mYearView.setOnClickListener(this); 176 final Activity activity = getActivity(); 177 178 int currentView = MONTH_AND_DAY_VIEW; 179 mDayPickerView = new DayPickerView(activity, this); 180 if (savedInstanceState != null) { 181 Log.d(TAG, 182 "Setting first visible position: " 183 + savedInstanceState.getInt(KEY_LIST_POSITION)); 184 mDayPickerView.setSelectionFromTop(savedInstanceState.getInt(KEY_LIST_POSITION), 185 DayPickerView.LIST_TOP_OFFSET); 186 mWeekStart = savedInstanceState.getInt(KEY_WEEK_START); 187 mMinYear = savedInstanceState.getInt(KEY_YEAR_START); 188 mMaxYear = savedInstanceState.getInt(KEY_YEAR_END); 189 currentView = savedInstanceState.getInt(KEY_CURRENT_VIEW); 190 } 191 mYearPickerView = new YearPickerView(activity, this); 192 193 mViews[MONTH_AND_DAY_VIEW] = mDayPickerView; 194 mViews[YEAR_VIEW] = mYearPickerView; 195 196 mAnimator = (ViewAnimator) view.findViewById(R.id.animator); 197 // TODO: Replace with animation decided upon by the design team. 198 Animation animation = new AlphaAnimation(0.0f, 1.0f); 199 animation.setDuration(ANIMATION_DURATION); 200 mAnimator.setInAnimation(animation); 201 // TODO: Replace with animation decided upon by the design team. 202 Animation animation2 = new AlphaAnimation(1.0f, 0.0f); 203 animation2.setDuration(ANIMATION_DURATION); 204 mAnimator.setOutAnimation(animation2); 205 mAnimator.addView(mDayPickerView); 206 mAnimator.addView(mYearPickerView); 207 208 mDoneButton = (Button) view.findViewById(R.id.done); 209 mDoneButton.setOnClickListener(new OnClickListener() { 210 211 @Override 212 public void onClick(View v) { 213 if (mCallBack != null) { 214 mCallBack.onDateSet(DatePickerDialog.this, mCalendar.get(Calendar.YEAR), 215 mCalendar.get(Calendar.MONTH), mCalendar.get(Calendar.DAY_OF_MONTH)); 216 } 217 dismiss(); 218 } 219 }); 220 221 updateDisplay(); 222 setCurrentView(currentView); 223 224 return view; 225 } 226 227 private void setCurrentView(final int viewIndex) { 228 switch (viewIndex) { 229 case MONTH_AND_DAY_VIEW: 230 mCurrentView = viewIndex; 231 mMonthAndDayView.setSelected(true); 232 mYearView.setSelected(false); 233 mAnimator.setDisplayedChild(MONTH_AND_DAY_VIEW); 234 break; 235 case YEAR_VIEW: 236 mCurrentView = viewIndex; 237 mMonthAndDayView.setSelected(false); 238 mYearView.setSelected(true); 239 mAnimator.setDisplayedChild(YEAR_VIEW); 240 break; 241 } 242 } 243 244 private void updateDisplay() { 245 mDayOfWeekView.setText(mCalendar.getDisplayName(Calendar.DAY_OF_WEEK, Calendar.LONG, 246 Locale.getDefault()).toUpperCase(Locale.getDefault())); 247 mSelectedMonthTextView.setText(mCalendar.getDisplayName(Calendar.MONTH, Calendar.SHORT, 248 Locale.getDefault()).toUpperCase(Locale.getDefault())); 249 mSelectedDayTextView.setText(DAY_FORMAT.format(mCalendar.getTime())); 250 mYearView.setText(YEAR_FORMAT.format(mCalendar.getTime())); 251 } 252 253 public void setFirstDayOfWeek(int startOfWeek) { 254 if (startOfWeek < Calendar.SUNDAY || startOfWeek > Calendar.SATURDAY) { 255 throw new IllegalArgumentException("Value must be between Calendar.SUNDAY and " + 256 "Calendar.SATURDAY"); 257 } 258 mWeekStart = startOfWeek; 259 if (mDayPickerView != null) { 260 mDayPickerView.onChange(); 261 } 262 } 263 264 public void setYearRange(int startYear, int endYear) { 265 if (endYear <= startYear) { 266 throw new IllegalArgumentException("Year end must be larger than year start"); 267 } 268 mMinYear = startYear; 269 mMaxYear = endYear; 270 if (mDayPickerView != null) { 271 mDayPickerView.onChange(); 272 mYearPickerView.onChange(); 273 } 274 } 275 276 public void setOnDateSetListener(OnDateSetListener listener) { 277 mCallBack = listener; 278 } 279 280 // If the newly selected month / year does not contain the currently selected day number, 281 // change the selected day number to the last day of the selected month or year. 282 // e.g. Switching from Mar to Apr when Mar 31 is selected -> Apr 30 283 // e.g. Switching from 2012 to 2013 when Feb 29, 2012 is selected -> Feb 28, 2013 284 private void adjustDayInMonthIfNeeded(int month, int year) { 285 int day = mCalendar.get(Calendar.DAY_OF_MONTH); 286 int daysInMonth = Utils.getDaysInMonth(month, year); 287 if (day > daysInMonth) { 288 mCalendar.set(Calendar.DAY_OF_MONTH, daysInMonth); 289 } 290 } 291 292 @Override 293 public void onClick(View v) { 294 if (v.getId() == R.id.date_picker_year) { 295 setCurrentView(YEAR_VIEW); 296 } else if (v.getId() == R.id.date_picker_month_and_day) { 297 setCurrentView(MONTH_AND_DAY_VIEW); 298 } 299 } 300 301 @Override 302 public void onYearPickerSelectionChanged(int year) { 303 adjustDayInMonthIfNeeded(mCalendar.get(Calendar.MONTH), year); 304 mCalendar.set(Calendar.YEAR, year); 305 mDayPickerView.setCalendarDate(getSelectedDay()); 306 updateDisplay(); 307 } 308 309 @Override 310 public void onMonthPickerSelectionChanged(int month) { 311 adjustDayInMonthIfNeeded(month, mCalendar.get(Calendar.YEAR)); 312 mCalendar.set(Calendar.MONTH, month); 313 mDayPickerView.setCalendarDate(getSelectedDay()); 314 setCurrentView(MONTH_AND_DAY_VIEW); 315 updateDisplay(); 316 } 317 318 @Override 319 public void onDayPickerSelectionChanged(int year, int month, int day) { 320 mCalendar.set(Calendar.YEAR, year); 321 mCalendar.set(Calendar.MONTH, month); 322 mCalendar.set(Calendar.DAY_OF_MONTH, day); 323 mYearPickerView.setValue(mCalendar.get(Calendar.YEAR)); 324 updateDisplay(); 325 } 326 327 328 @Override 329 public CalendarDay getSelectedDay() { 330 return new CalendarDay(mCalendar); 331 } 332 333 @Override 334 public int getMinYear() { 335 return mMinYear; 336 } 337 338 @Override 339 public int getMaxYear() { 340 return mMaxYear; 341 } 342 343 @Override 344 public int getFirstDayOfWeek() { 345 return mWeekStart; 346 } 347} 348