TimePicker.java revision 25f84f323c607bbd9133432fd789ba29b2dcd4d4
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.os.Parcel; 24import android.os.Parcelable; 25import android.util.AttributeSet; 26import android.view.LayoutInflater; 27import android.view.View; 28import android.widget.NumberPicker.OnValueChangedListener; 29 30import java.text.DateFormatSymbols; 31import java.util.Calendar; 32 33/** 34 * A view for selecting the time of day, in either 24 hour or AM/PM mode. 35 * 36 * The hour, each minute digit, and AM/PM (if applicable) can be conrolled by 37 * vertical spinners. 38 * 39 * The hour can be entered by keyboard input. Entering in two digit hours 40 * can be accomplished by hitting two digits within a timeout of about a 41 * second (e.g. '1' then '2' to select 12). 42 * 43 * The minutes can be entered by entering single digits. 44 * 45 * Under AM/PM mode, the user can hit 'a', 'A", 'p' or 'P' to pick. 46 * 47 * For a dialog using this view, see {@link android.app.TimePickerDialog}. 48 * 49 * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-timepicker.html">Time Picker 50 * tutorial</a>.</p> 51 */ 52@Widget 53public class TimePicker extends FrameLayout { 54 55 private static final boolean DEFAULT_ENABLED_STATE = true; 56 57 /** 58 * A no-op callback used in the constructor to avoid null checks 59 * later in the code. 60 */ 61 private static final OnTimeChangedListener NO_OP_CHANGE_LISTENER = new OnTimeChangedListener() { 62 public void onTimeChanged(TimePicker view, int hourOfDay, int minute) { 63 } 64 }; 65 66 // state 67 private int mCurrentHour = 0; // 0-23 68 private int mCurrentMinute = 0; // 0-59 69 private Boolean mIs24HourView = false; 70 private boolean mIsAm; 71 72 // ui components 73 private final NumberPicker mHourSpinner; 74 private final NumberPicker mMinuteSpinner; 75 private final NumberPicker mAmPmSpinner; 76 private final TextView mDivider; 77 78 private final String[] mAmPmStrings; 79 80 private boolean mIsEnabled = DEFAULT_ENABLED_STATE; 81 82 // callbacks 83 private OnTimeChangedListener mOnTimeChangedListener; 84 85 /** 86 * The callback interface used to indicate the time has been adjusted. 87 */ 88 public interface OnTimeChangedListener { 89 90 /** 91 * @param view The view associated with this listener. 92 * @param hourOfDay The current hour. 93 * @param minute The current minute. 94 */ 95 void onTimeChanged(TimePicker view, int hourOfDay, int minute); 96 } 97 98 public TimePicker(Context context) { 99 this(context, null); 100 } 101 102 public TimePicker(Context context, AttributeSet attrs) { 103 this(context, attrs, 0); 104 } 105 106 public TimePicker(Context context, AttributeSet attrs, int defStyle) { 107 super(context, attrs, defStyle); 108 109 LayoutInflater inflater = 110 (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 111 inflater.inflate(R.layout.time_picker, 112 this, // we are the parent 113 true); 114 115 // hour 116 mHourSpinner = (NumberPicker) findViewById(R.id.hour); 117 mHourSpinner.setOnValueChangedListener(new NumberPicker.OnValueChangedListener() { 118 public void onValueChange(NumberPicker spinner, int oldVal, int newVal) { 119 mCurrentHour = newVal; 120 if (!mIs24HourView) { 121 // adjust from [1-12] to [0-11] internally, with the times 122 // written "12:xx" being the start of the half-day 123 if (mCurrentHour == 12) { 124 mCurrentHour = 0; 125 } 126 if (!mIsAm) { 127 // PM means 12 hours later than nominal 128 mCurrentHour += 12; 129 } 130 } 131 onTimeChanged(); 132 } 133 }); 134 135 // divider 136 mDivider = (TextView) findViewById(R.id.divider); 137 mDivider.setText(R.string.time_picker_separator); 138 139 // digits of minute 140 mMinuteSpinner = (NumberPicker) findViewById(R.id.minute); 141 mMinuteSpinner.setMinValue(0); 142 mMinuteSpinner.setMaxValue(59); 143 mMinuteSpinner.setOnLongPressUpdateInterval(100); 144 mMinuteSpinner.setFormatter(NumberPicker.TWO_DIGIT_FORMATTER); 145 mMinuteSpinner.setOnValueChangedListener(new NumberPicker.OnValueChangedListener() { 146 public void onValueChange(NumberPicker spinner, int oldVal, int newVal) { 147 mCurrentMinute = newVal; 148 onTimeChanged(); 149 } 150 }); 151 152 // am/pm 153 mAmPmSpinner = (NumberPicker) findViewById(R.id.amPm); 154 mAmPmSpinner.setOnValueChangedListener(new OnValueChangedListener() { 155 public void onValueChange(NumberPicker picker, int oldVal, int newVal) { 156 picker.requestFocus(); 157 if (mIsAm) { 158 // Currently AM switching to PM 159 if (mCurrentHour < 12) { 160 mCurrentHour += 12; 161 } 162 } else { 163 // Currently PM switching to AM 164 if (mCurrentHour >= 12) { 165 mCurrentHour -= 12; 166 } 167 } 168 mIsAm = !mIsAm; 169 onTimeChanged(); 170 } 171 }); 172 173 /* Get the localized am/pm strings and use them in the spinner */ 174 mAmPmStrings = new DateFormatSymbols().getAmPmStrings(); 175 176 // now that the hour/minute picker objects have been initialized, set 177 // the hour range properly based on the 12/24 hour display mode. 178 configurePickerRanges(); 179 180 // initialize to current time 181 Calendar cal = Calendar.getInstance(); 182 setOnTimeChangedListener(NO_OP_CHANGE_LISTENER); 183 184 // by default we're not in 24 hour mode 185 setCurrentHour(cal.get(Calendar.HOUR_OF_DAY)); 186 setCurrentMinute(cal.get(Calendar.MINUTE)); 187 188 if (!isEnabled()) { 189 setEnabled(false); 190 } 191 } 192 193 @Override 194 public void setEnabled(boolean enabled) { 195 if (mIsEnabled == enabled) { 196 return; 197 } 198 super.setEnabled(enabled); 199 mMinuteSpinner.setEnabled(enabled); 200 mDivider.setEnabled(enabled); 201 mHourSpinner.setEnabled(enabled); 202 mAmPmSpinner.setEnabled(enabled); 203 mIsEnabled = enabled; 204 } 205 206 @Override 207 public boolean isEnabled() { 208 return mIsEnabled; 209 } 210 211 /** 212 * Used to save / restore state of time picker 213 */ 214 private static class SavedState extends BaseSavedState { 215 216 private final int mHour; 217 private final int mMinute; 218 219 private SavedState(Parcelable superState, int hour, int minute) { 220 super(superState); 221 mHour = hour; 222 mMinute = minute; 223 } 224 225 private SavedState(Parcel in) { 226 super(in); 227 mHour = in.readInt(); 228 mMinute = in.readInt(); 229 } 230 231 public int getHour() { 232 return mHour; 233 } 234 235 public int getMinute() { 236 return mMinute; 237 } 238 239 @Override 240 public void writeToParcel(Parcel dest, int flags) { 241 super.writeToParcel(dest, flags); 242 dest.writeInt(mHour); 243 dest.writeInt(mMinute); 244 } 245 246 @SuppressWarnings("unused") 247 public static final Parcelable.Creator<SavedState> CREATOR 248 = new Creator<SavedState>() { 249 public SavedState createFromParcel(Parcel in) { 250 return new SavedState(in); 251 } 252 253 public SavedState[] newArray(int size) { 254 return new SavedState[size]; 255 } 256 }; 257 } 258 259 @Override 260 protected Parcelable onSaveInstanceState() { 261 Parcelable superState = super.onSaveInstanceState(); 262 return new SavedState(superState, mCurrentHour, mCurrentMinute); 263 } 264 265 @Override 266 protected void onRestoreInstanceState(Parcelable state) { 267 SavedState ss = (SavedState) state; 268 super.onRestoreInstanceState(ss.getSuperState()); 269 setCurrentHour(ss.getHour()); 270 setCurrentMinute(ss.getMinute()); 271 } 272 273 /** 274 * Set the callback that indicates the time has been adjusted by the user. 275 * @param onTimeChangedListener the callback, should not be null. 276 */ 277 public void setOnTimeChangedListener(OnTimeChangedListener onTimeChangedListener) { 278 mOnTimeChangedListener = onTimeChangedListener; 279 } 280 281 /** 282 * @return The current hour (0-23). 283 */ 284 public Integer getCurrentHour() { 285 return mCurrentHour; 286 } 287 288 /** 289 * Set the current hour. 290 */ 291 public void setCurrentHour(Integer currentHour) { 292 this.mCurrentHour = currentHour; 293 updateHourDisplay(); 294 } 295 296 /** 297 * Set whether in 24 hour or AM/PM mode. 298 * @param is24HourView True = 24 hour mode. False = AM/PM. 299 */ 300 public void setIs24HourView(Boolean is24HourView) { 301 if (mIs24HourView != is24HourView) { 302 mIs24HourView = is24HourView; 303 configurePickerRanges(); 304 updateHourDisplay(); 305 } 306 } 307 308 /** 309 * @return true if this is in 24 hour view else false. 310 */ 311 public boolean is24HourView() { 312 return mIs24HourView; 313 } 314 315 /** 316 * @return The current minute. 317 */ 318 public Integer getCurrentMinute() { 319 return mCurrentMinute; 320 } 321 322 /** 323 * Set the current minute (0-59). 324 */ 325 public void setCurrentMinute(Integer currentMinute) { 326 this.mCurrentMinute = currentMinute; 327 updateMinuteDisplay(); 328 } 329 330 @Override 331 public int getBaseline() { 332 return mHourSpinner.getBaseline(); 333 } 334 335 /** 336 * Set the state of the spinners appropriate to the current hour. 337 */ 338 private void updateHourDisplay() { 339 int currentHour = mCurrentHour; 340 if (!mIs24HourView) { 341 // convert [0,23] ordinal to wall clock display 342 if (currentHour > 12) { 343 currentHour -= 12; 344 } else if (currentHour == 0) { 345 currentHour = 12; 346 } 347 } 348 mHourSpinner.setValue(currentHour); 349 mIsAm = mCurrentHour < 12; 350 mAmPmSpinner.setValue(mIsAm ? Calendar.AM : Calendar.PM); 351 onTimeChanged(); 352 } 353 354 private void configurePickerRanges() { 355 if (mIs24HourView) { 356 mHourSpinner.setMinValue(0); 357 mHourSpinner.setMaxValue(23); 358 mHourSpinner.setFormatter(NumberPicker.TWO_DIGIT_FORMATTER); 359 mAmPmSpinner.setVisibility(View.GONE); 360 } else { 361 mHourSpinner.setMinValue(1); 362 mHourSpinner.setMaxValue(12); 363 mHourSpinner.setFormatter(null); 364 mAmPmSpinner.setVisibility(View.VISIBLE); 365 mAmPmSpinner.setMinValue(0); 366 mAmPmSpinner.setMaxValue(1); 367 mAmPmSpinner.setDisplayedValues(mAmPmStrings); 368 } 369 } 370 371 private void onTimeChanged() { 372 if (mOnTimeChangedListener != null) { 373 mOnTimeChangedListener.onTimeChanged(this, getCurrentHour(), getCurrentMinute()); 374 } 375 } 376 377 /** 378 * Set the state of the spinners appropriate to the current minute. 379 */ 380 private void updateMinuteDisplay() { 381 mMinuteSpinner.setValue(mCurrentMinute); 382 onTimeChanged(); 383 } 384} 385