Android TimePickerDialog
で選択可能な最大時間と最小時間を設定し、デフォルトの分間隔を1分から5分に変更する方法を探しています。
これは簡単だと思ったが、方法が見つからない!
この回答IS OUTDATED
Android 5以降では動作しません。
これは、シオマネキの答えの拡張です。何らかの理由で、TimePickerDialogによってonTimeChangedListenerが設定されていないAndroidバージョン:
そのための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);
}
}
Androidのロリポップ以降のバージョンでは、この変更されたRangeTimePickerDialogクラスを使用できます
(lolipopから、Timepickerはクロックモード(マテリアルデザイン)をデフォルトとして使用するため、古いカスタムクラスは動作しません。モードを最新バージョンのスピナーに変更し、クラスを再利用できます)
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;
}
}
これを出発点として使用できます。
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);
}
}
}
フィドラーのコードをリファクタリングした後:
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
を拡張すると動作する可能性があります。時間を境界外の値に設定しないようにすることができます。
リフレクト付きのカスタムTimePickerを使用して値を設定できます。
@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();
}
}
必要な値を設定できます
この作品を願って、それは私と完全に動作します...
タイムピッカーの最大時間を設定します。
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
//http://stackoverflow.com/questions/2659954/timepickerdialog-and-am-or-pm
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;
}
}
});
これが役に立てば幸いです。私はJodaタイムライブラリを使用しました。
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);
}
}