我正在寻找一种方法来设置 Android 的
TimePickerDialog
可选择的最大和最小时间,并将默认分钟间隔从 1 分钟更改为 5 分钟。我以为这很容易,但是我找不到方法!TimePickerDialog
可选择的最大和最小时间,并将默认分钟间隔从 1 分钟更改为 5 分钟。我以为这很容易,但是我找不到方法!此答案已过时
它将无法在 Android 5 或更高版本上工作。
这是对 fiddler 答案的扩展。 某些 Android 版本中,TimePickerDialog 没有设置 onTimeChangedListener:
因此,onTimeChanged 方法永远不会被调用!目前解决此问题的最佳方法可能是使用来自 fiddler 的 min/max 逻辑编写自定义 TimePickerDialog。
更丑陋的解决方法是这样做。(使用反射添加侦听器) 我还更新了标题以显示当前选定的时间。
package com.mysuger.android.dialogs;
import java.lang.reflect.Field;
import java.text.DateFormat;
import java.util.Calendar;
import android.app.TimePickerDialog;
import android.content.Context;
import android.widget.TimePicker;
/**
* A time dialog that allows setting a min and max time.
*
*/
public class RangeTimePickerDialog extends TimePickerDialog {
private int minHour = -1;
private int minMinute = -1;
private int maxHour = 25;
private int maxMinute = 25;
private int currentHour = 0;
private int currentMinute = 0;
private Calendar calendar = Calendar.getInstance();
private DateFormat dateFormat;
public RangeTimePickerDialog(Context context, OnTimeSetListener callBack, int hourOfDay, int minute, boolean is24HourView) {
super(context, callBack, hourOfDay, minute, is24HourView);
currentHour = hourOfDay;
currentMinute = minute;
dateFormat = DateFormat.getTimeInstance(DateFormat.SHORT);
try {
Class<?> superclass = getClass().getSuperclass();
Field mTimePickerField = superclass.getDeclaredField("mTimePicker");
mTimePickerField.setAccessible(true);
TimePicker mTimePicker = (TimePicker) mTimePickerField.get(this);
mTimePicker.setOnTimeChangedListener(this);
} catch (NoSuchFieldException e) {
} catch (IllegalArgumentException e) {
} catch (IllegalAccessException e) {
}
}
public void setMin(int hour, int minute) {
minHour = hour;
minMinute = minute;
}
public void setMax(int hour, int minute) {
maxHour = hour;
maxMinute = minute;
}
@Override
public void onTimeChanged(TimePicker view, int hourOfDay, int minute) {
boolean validTime = true;
if (hourOfDay < minHour || (hourOfDay == minHour && minute < minMinute)){
validTime = false;
}
if (hourOfDay > maxHour || (hourOfDay == maxHour && minute > maxMinute)){
validTime = false;
}
if (validTime) {
currentHour = hourOfDay;
currentMinute = minute;
}
updateTime(currentHour, currentMinute);
updateDialogTitle(view, currentHour, currentMinute);
}
private void updateDialogTitle(TimePicker timePicker, int hourOfDay, int minute) {
calendar.set(Calendar.HOUR_OF_DAY, hourOfDay);
calendar.set(Calendar.MINUTE, minute);
String title = dateFormat.format(calendar.getTime());
setTitle(title);
}
}
public Dialog onCreateDialog(final Bundle savedInstanceState)
中将 RangeTimePickerDialog
替换为返回类型即可。 - AllDayAmazingif ((minCalendar nextDay Calendar.getInstance())) { validTime = true }
- Mudassir Zulfiqar对于Lollipop及更高版本的Android,您可以使用这个修改过的RangeTimePickerDialog类。
(从Lollipop开始,Timepicker默认使用时钟模式(Material Design),因此旧的自定义类将无法工作。我们可以将模式更改为Spinner以适应最新版本,并重用该类)
public class RangeTimePickerDialog extends TimePickerDialog {
private int minHour = -1;
private int minMinute = -1;
private int maxHour = 25;
private int maxMinute = 25;
private int currentHour = 0;
private int currentMinute = 0;
private Calendar calendar = Calendar.getInstance();
private DateFormat dateFormat;
public RangeTimePickerDialog(Context context, int dialogTheme, OnTimeSetListener callBack, int hourOfDay, int minute, boolean is24HourView) {
super(context, callBack, hourOfDay, minute, is24HourView);
currentHour = hourOfDay;
currentMinute = minute;
dateFormat = DateFormat.getTimeInstance(DateFormat.SHORT);
fixSpinner(context, hourOfDay, minute, is24HourView);
try {
Class<?> superclass = getClass().getSuperclass();
Field mTimePickerField = superclass.getDeclaredField("mTimePicker");
mTimePickerField.setAccessible(true);
TimePicker mTimePicker = (TimePicker) mTimePickerField.get(this);
mTimePicker.setOnTimeChangedListener(this);
} catch (NoSuchFieldException e) {
} catch (IllegalArgumentException e) {
} catch (IllegalAccessException e) {
}
}
public void setMin(int hour, int minute) {
minHour = hour;
minMinute = minute;
}
public void setMax(int hour, int minute) {
maxHour = hour;
maxMinute = minute;
}
@Override
public void onTimeChanged(TimePicker view, int hourOfDay, int minute) {
boolean validTime = true;
if (hourOfDay < minHour || (hourOfDay == minHour && minute < minMinute)){
validTime = false;
}
if (hourOfDay > maxHour || (hourOfDay == maxHour && minute > maxMinute)){
validTime = false;
}
if (validTime) {
currentHour = hourOfDay;
currentMinute = minute;
}
updateTime(currentHour, currentMinute);
updateDialogTitle(view, currentHour, currentMinute);
}
private void updateDialogTitle(TimePicker timePicker, int hourOfDay, int minute) {
calendar.set(Calendar.HOUR_OF_DAY, hourOfDay);
calendar.set(Calendar.MINUTE, minute);
String title = dateFormat.format(calendar.getTime());
setTitle(title);
}
private void fixSpinner(Context context, int hourOfDay, int minute, boolean is24HourView) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { // android:timePickerMode spinner and clock began in Lollipop
try {
// Get the theme's android:timePickerMode
//two modes are available clock mode and spinner mode ... selecting spinner mode for latest versions
final int MODE_SPINNER = 2;
Class<?> styleableClass = Class.forName("com.android.internal.R$styleable");
Field timePickerStyleableField = styleableClass.getField("TimePicker");
int[] timePickerStyleable = (int[]) timePickerStyleableField.get(null);
final TypedArray a = context.obtainStyledAttributes(null, timePickerStyleable, android.R.attr.timePickerStyle, 0);
Field timePickerModeStyleableField = styleableClass.getField("TimePicker_timePickerMode");
int timePickerModeStyleable = timePickerModeStyleableField.getInt(null);
final int mode = a.getInt(timePickerModeStyleable, MODE_SPINNER);
a.recycle();
if (mode == MODE_SPINNER) {
TimePicker timePicker = (TimePicker) findField(TimePickerDialog.class, TimePicker.class, "mTimePicker").get(this);
Class<?> delegateClass = Class.forName("android.widget.TimePicker$TimePickerDelegate");
Field delegateField = findField(TimePicker.class, delegateClass, "mDelegate");
Object delegate = delegateField.get(timePicker);
Class<?> spinnerDelegateClass;
if (Build.VERSION.SDK_INT != Build.VERSION_CODES.LOLLIPOP) {
spinnerDelegateClass = Class.forName("android.widget.TimePickerSpinnerDelegate");
} else {
spinnerDelegateClass = Class.forName("android.widget.TimePickerClockDelegate");
}
if (delegate.getClass() != spinnerDelegateClass) {
delegateField.set(timePicker, null); // throw out the TimePickerClockDelegate!
timePicker.removeAllViews(); // remove the TimePickerClockDelegate views
Constructor spinnerDelegateConstructor = spinnerDelegateClass.getConstructor(TimePicker.class, Context.class, AttributeSet.class, int.class, int.class);
spinnerDelegateConstructor.setAccessible(true);
// Instantiate a TimePickerSpinnerDelegate
delegate = spinnerDelegateConstructor.newInstance(timePicker, context, null, android.R.attr.timePickerStyle, 0);
delegateField.set(timePicker, delegate); // set the TimePicker.mDelegate to the spinner delegate
// Set up the TimePicker again, with the TimePickerSpinnerDelegate
timePicker.setIs24HourView(is24HourView);
timePicker.setCurrentHour(hourOfDay);
timePicker.setCurrentMinute(minute);
timePicker.setOnTimeChangedListener(this);
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
private static Field findField(Class objectClass, Class fieldClass, String expectedName) {
try {
Field field = objectClass.getDeclaredField(expectedName);
field.setAccessible(true);
return field;
} catch (NoSuchFieldException e) {} // ignore
// search for it if it wasn't found under the expected ivar name
for (Field searchField : objectClass.getDeclaredFields()) {
if (searchField.getType() == fieldClass) {
searchField.setAccessible(true);
return searchField;
}
}
return null;
}
}
styleableClass.getField("TimePicker")
,报错信息为E/UncaughtException: java.lang.RuntimeException: java.lang.NoSuchFieldException: TimePicker
。 - apj123您可以将此用作起点。
我扩展了TimePickerDialog并添加了2个方法setMin
和setMax
。
在onTimeChanged
方法中,我检查新时间是否符合最小/最大时间的要求。
但它仍然需要一些优化...
public class BoundTimePickerDialog extends TimePickerDialog {
private int minHour = -1, minMinute = -1, maxHour = 100, maxMinute = 100;
private int currentHour, currentMinute;
public BoundTimePickerDialog(Context context, OnTimeSetListener callBack, int hourOfDay, int minute, boolean is24HourView) {
super(context, callBack, hourOfDay, minute, is24HourView);
}
public void setMin(int hour, int minute) {
minHour = hour;
minMinute = minute;
}
public void setMax(int hour, int minute) {
maxHour = hour;
maxMinute = minute;
}
@Override
public void onTimeChanged(TimePicker view, int hourOfDay, int minute) {
super.onTimeChanged(view, hourOfDay, minute);
boolean validTime;
if(hourOfDay < minHour) {
validTime = false;
}
else if(hourOfDay == minHour) {
validTime = minute >= minMinute;
}
else if(hourOfDay == maxHour) {
validTime = minute <= maxMinute;
}
else {
validTime = true;
}
if(validTime) {
currentHour = hourOfDay;
currentMinute = minute;
}
else {
updateTime(currentHour, currentMinute);
}
}
}
重构 Fiddler 代码后:
public class RangeTimePickerDialog extends TimePickerDialog {
private int mMinHour = -1;
private int mMinMinute = -1;
private int mMaxHour = 100;
private int mMaxMinute = 100;
private int mCurrentHour;
private int mCurrentMinute;
public RangeTimePickerDialog(Context context, OnTimeSetListener callBack, int hourOfDay,
int minute, boolean is24HourView) {
super(context, callBack, hourOfDay, minute, is24HourView);
mCurrentHour = hourOfDay;
mCurrentMinute = minute;
// Somehow the onTimeChangedListener is not set by TimePickerDialog
// in some Android Versions, so, Adding the listener using
// reflections
try {
Class<?> superclass = getClass().getSuperclass();
Field mTimePickerField = superclass.getDeclaredField("mTimePicker");
mTimePickerField.setAccessible(true);
TimePicker mTimePicker = (TimePicker) mTimePickerField.get(this);
mTimePicker.setOnTimeChangedListener(this);
} catch (NoSuchFieldException e) {
} catch (IllegalArgumentException e) {
} catch (IllegalAccessException e) {
}
}
public void setMin(int hour, int minute) {
mMinHour = hour;
mMinMinute = minute;
}
public void setMax(int hour, int minute) {
mMaxHour = hour;
mMaxMinute = minute;
}
@Override
public void onTimeChanged(TimePicker view, int hourOfDay, int minute) {
super.onTimeChanged(view, hourOfDay, minute);
boolean validTime;
if (((hourOfDay < mMinHour ) || (hourOfDay == mMinHour && minute < mMinMinute))
|| ((hourOfDay > mMaxHour) || (hourOfDay == mMaxHour && minute > mMaxMinute))) {
validTime = false;
} else {
validTime = true;
}
if (validTime) {
mCurrentHour = hourOfDay;
mCurrentMinute = minute;
} else {
updateTime(mCurrentHour, mCurrentMinute);
}
}
}
没有本地方法来设置最大/最小时间,但是也许如果您扩展onTimeChanged
或updateTime
,您可以防止时间被设置为超出您的范围的值。
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
try {
Class<?> classForid = Class.forName("com.android.internal.R$id");
Field field = classForid.getField("minute");
NumberPicker mMinuteSpinner = (NumberPicker) findViewById(field.getInt(null));
mMinuteSpinner.setMinValue(5);
mMinuteSpinner.setMaxValue((60 / timeInterval) - 1);
List<String> displayedValues = new ArrayList<>();
for (int i = 0; i < 60; i += timeInterval)
displayedValues.add(String.format("%02d", i));
mMinuteSpinner.setDisplayedValues(displayedValues.toArray(new String[0]));
mMinuteSpinner.setWrapSelectorWheel(true);
} catch (Exception e) {
e.printStackTrace();
}
}
因此,您可以设置所需的值。
public class RangeTimePicker extends TimePicker implements
TimePicker.OnTimeChangedListener {
private int mMinHour = -1;
private int mMinMinute = -1;
private int mMaxHour = 25;
private int mMaxMinute = 61;
private int mCurrentHour;
private int mCurrentMinute;
public RangeTimePicker(Context context) {
super(context);
setOnTimeChangedListener(this);
}
public RangeTimePicker(Context context, AttributeSet attrs) {
super(context, attrs);
setOnTimeChangedListener(this);
}
public RangeTimePicker(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
setOnTimeChangedListener(this);
}
public void setMaxTime(int hourIn24, int minute) {
mMaxHour = hourIn24;
mMaxMinute = minute;
LocalTime currentTime = LocalTime.now();
LocalTime maxTime = new LocalTime(mMaxHour, mMaxMinute);
if (currentTime.isAfter(maxTime)) {
this.setCurrentHour(mCurrentHour = currentTime.getHourOfDay());
this.setCurrentMinute(
mCurrentMinute = currentTime.getMinuteOfHour());
}
}
public void setMinTime(int hourIn24, int minute) {
mMinHour = hourIn24;
mMinMinute = minute;
LocalTime currentTime = LocalTime.now();
LocalTime minTime = new LocalTime(mMinHour, mMinMinute);
if (currentTime.isBefore(minTime)) {
this.setCurrentHour(mCurrentHour = minTime.getHourOfDay());
this.setCurrentMinute(mCurrentMinute = minTime.getMinuteOfHour());
}
}
@Override
public void onTimeChanged(TimePicker view, int hourOfDay, int minute) {
boolean validTime = true;
if (hourOfDay < mMinHour || (hourOfDay == mMinHour
&& minute < mMinMinute)) {
validTime = false;
}
if (hourOfDay > mMaxHour || (hourOfDay == mMaxHour
&& minute > mMaxMinute)) {
validTime = false;
}
if (validTime) {
mCurrentHour = hourOfDay;
mCurrentMinute = minute;
}
setCurrentHour(mCurrentHour);
setCurrentMinute(mCurrentMinute);
}
}
timePicker.setOnTimeChangedListener(new TimePicker.OnTimeChangedListener() {
@Override
public void onTimeChanged(TimePicker view, int hourOfDay, int minute) {
if (!mIsOnTimeChanged) {
mIsOnTimeChanged = true;
Calendar c = Calendar.getInstance();
//get the selected day if it is before today allow AM and PM trick
//else apply rules
int selected_year = datePicker.getYear();
int selected_month = datePicker.getMonth();
int selected_day = datePicker.getDayOfMonth();
int today = c.get(Calendar.DAY_OF_MONTH);
int year = c.get(Calendar.YEAR);
int month = c.get(Calendar.MONTH);
int hour = c.get(Calendar.HOUR);
hour += 12;
int min = c.get(Calendar.MINUTE);
//Check if user enter hh | mm | more than current reset the timePicker with the current time
//https://dev59.com/anE85IYBdhLWcg3wr1ke
if ((hourOfDay == hour | hour < hourOfDay | hourOfDay > hour | hour > hourOfDay | minute > min) && (selected_month == month && selected_day == today && selected_year == year)) {
int AM_PM_Value = hourOfDay;
Calendar datetime = Calendar.getInstance();
int hour_without_24 = (hour - 12);
if ((datetime.get(Calendar.AM_PM) == Calendar.AM) && (hourOfDay > hour_without_24)) {
//set AM
timePicker.setCurrentHour(hour - 12);
}
if (hourOfDay == (hour - 12) && minute > min) {
timePicker.setCurrentMinute(min);
}
if (hourOfDay > hour && datetime.get(Calendar.AM_PM) != Calendar.AM) {
timePicker.setCurrentHour(hour);
if (minute > min) {
timePicker.setCurrentMinute(min);
}
}
} else if (selected_month != month | selected_day != today | selected_year != year) {
} else if (hourOfDay == hour && minute > min) {
timePicker.setCurrentMinute(min);
} else if (hourOfDay > hour) {
timePicker.setCurrentHour(hour);
if (minute > min) {
timePicker.setCurrentMinute(min);
}
}
//
isTimePickerChanged = true;
String AM_PM;
if (hourOfDay < 12) {
AM_PM = "AM";
} else {
AM_PM = "PM";
}
//to set as 12 hour
time_format = (hourOfDay + ":" + minute + ":" + "00");
if (hourOfDay > 12) {
hourOfDay = (hourOfDay - 12);
}
st_time = (hourOfDay + ":" + pad(minute) + " " + AM_PM);
mIsOnTimeChanged = false;
}
}
});
package de.appdream.garooda.view;
import java.util.Calendar;
import java.util.Date;
import android.app.TimePickerDialog;
import android.content.Context;
import android.text.format.DateFormat;
import android.widget.TimePicker;
import java.lang.reflect.Field;
public class GaroodaTimePickerDialog extends TimePickerDialog {
private int minHour = -1;
private int minMinute = -1;
private int maxHour = 25;
private int maxMinute = 61;
private int currentHour = 0;
private int currentMinute = 0;
private Calendar calendar = Calendar.getInstance();
//private DateFormat dateFormat;
public GaroodaTimePickerDialog(Context context, OnTimeSetListener callBack, int hour, int minute,
boolean is24HourView) {
super(context, callBack, hour, minute, is24HourView);
currentHour = hour;
currentMinute = minute;
//dateFormat = DateFormat.getTimeInstance(DateFormat.SHORT);
try {
Class<?> superclass = getClass().getSuperclass();
Field mTimePickerField = superclass.getDeclaredField("mTimePicker");
mTimePickerField.setAccessible(true);
TimePicker mTimePicker = (TimePicker) mTimePickerField.get(this);
mTimePicker.setOnTimeChangedListener(this);
} catch (NoSuchFieldException e) {
} catch (IllegalArgumentException e) {
} catch (IllegalAccessException e) {
}
}
public void setMin(int hour, int minute) {
minHour = hour;
minMinute = minute;
Calendar min = Calendar.getInstance();
Calendar existing = Calendar.getInstance();
min.set(Calendar.HOUR_OF_DAY, minHour);
min.set(Calendar.MINUTE, minMinute);
existing.set(Calendar.HOUR_OF_DAY, currentHour);
existing.set(Calendar.MINUTE, currentMinute);
if (existing.before(min)) {
currentHour = minHour;
currentMinute = minMinute;
updateTime(currentHour, currentMinute);
}
}
public void setMax(int hour, int minute) {
maxHour = hour;
maxMinute = minute;
Calendar max = Calendar.getInstance();
Calendar existing = Calendar.getInstance();
max.set(Calendar.HOUR_OF_DAY, maxHour);
max.set(Calendar.MINUTE, maxMinute);
existing.set(Calendar.HOUR_OF_DAY, currentHour);
existing.set(Calendar.MINUTE, currentMinute);
if (existing.after(max)) {
currentHour = maxHour;
currentMinute = maxMinute;
updateTime(currentHour, currentMinute);
}
}
@Override
public void onTimeChanged(TimePicker view, int hourOfDay, int minute) {
boolean validTime = true;
if (hourOfDay < minHour || (hourOfDay == minHour && minute < minMinute)){
validTime = false;
}
if (hourOfDay > maxHour || (hourOfDay == maxHour && minute > maxMinute)){
validTime = false;
}
if (validTime) {
currentHour = hourOfDay;
currentMinute = minute;
}
updateTime(currentHour, currentMinute);
updateDialogTitle(view, currentHour, currentMinute);
}
private void updateDialogTitle(TimePicker timePicker, int hourOfDay, int minute) {
calendar.set(Calendar.HOUR_OF_DAY, hourOfDay);
calendar.set(Calendar.MINUTE, minute);
//String title = dateFormat.format(calendar.getTime());
//setTitle(title);
}
}