-
-
Save smaugho/14dae83f3284fa05455ee0a9e4f13099 to your computer and use it in GitHub Desktop.
public class CustomTimePickerDialog extends TimePickerDialog { | |
private final static int TIME_PICKER_INTERVAL = 15; | |
private TimePicker timePicker; | |
private final OnTimeSetListener callback; | |
public HorekoTimePicker(Context context, | |
OnTimeSetListener callBack, int hourOfDay, int minute, boolean is24HourView) { | |
super(context, callBack, hourOfDay, minute/TIME_PICKER_INTERVAL, is24HourView); | |
this.callback = callBack; | |
fixSpinner(context, hourOfDay, minute, is24HourView); | |
} | |
/** | |
* Workaround for this bug: https://code.google.com/p/android/issues/detail?id=222208 | |
* In Android 7.0 Nougat, spinner mode for the TimePicker in TimePickerDialog is | |
* incorrectly displayed as clock, even when the theme specifies otherwise, such as: | |
* | |
* <resources> | |
* <style name="Theme.MyApp" parent="Theme.AppCompat.Light.NoActionBar"> | |
* <item name="android:timePickerStyle">@style/Widget.MyApp.TimePicker</item> | |
* </style> | |
* | |
* <style name="Widget.MyApp.TimePicker" parent="android:Widget.Material.TimePicker"> | |
* <item name="android:timePickerMode">spinner</item> | |
* </style> | |
* </resources> | |
* | |
* May also pass TimePickerDialog.THEME_HOLO_LIGHT as an argument to the constructor, | |
* as this theme has the TimePickerMode set to spinner. | |
* | |
* Taken from: https://gist.github.com/jeffdgr8/6bc5f990bf0c13a7334ce385d482af9f | |
*/ | |
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 | |
final int MODE_SPINNER = 1; | |
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) 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 { | |
// TimePickerSpinnerDelegate was initially misnamed TimePickerClockDelegate in API 21! | |
spinnerDelegateClass = Class.forName("android.widget.TimePickerClockDelegate"); | |
} | |
// In 7.0 Nougat for some reason the timePickerMode is ignored and the delegate is 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); | |
} | |
setTimeIntervals(); | |
} | |
} 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; | |
} | |
@Override | |
protected void onStop() { } | |
/* | |
* Feature #363: (Un)availability times in 15min interval | |
* https://abix.webhop.net/redmine/issues/363 | |
* Solution extracted from | |
* http://stackoverflow.com/questions/20214547/show-timepicker-with-minutes-intervals-in-android | |
*/ | |
@Override | |
public void onClick(DialogInterface dialog, int which) { | |
super.onClick(dialog, which); | |
if (callback != null && timePicker != null) { | |
timePicker.clearFocus(); | |
callback.onTimeSet(timePicker, timePicker.getCurrentHour(), | |
timePicker.getCurrentMinute()*TIME_PICKER_INTERVAL); | |
} | |
} | |
private void setTimeIntervals() { | |
try { | |
Class<?> classForid = Class.forName("com.android.internal.R$id"); | |
Field field = classForid.getField("minute"); | |
NumberPicker mMinuteSpinner = (NumberPicker) timePicker.findViewById(field.getInt(null)); | |
mMinuteSpinner.setMinValue(0); | |
mMinuteSpinner.setMaxValue((60 / TIME_PICKER_INTERVAL) - 1); | |
List<String> displayedValues = new ArrayList<String>(); | |
for (int i = 0; i < 60; i += TIME_PICKER_INTERVAL) { | |
displayedValues.add(String.format("%02d", i)); | |
} | |
mMinuteSpinner.setDisplayedValues(displayedValues.toArray(new String[0])); | |
} catch (Exception e) { | |
e.printStackTrace(); | |
} | |
} | |
} |
how to call this class???
Android 11 is not working with onAttachedToWindow ,my code is -------
import android.annotation.SuppressLint;
import android.app.TimePickerDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.res.TypedArray;
import android.os.Build;
import android.util.AttributeSet;
import android.widget.NumberPicker;
import android.widget.TimePicker;
import com.splunk.mint.Mint;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
public class CustomTimePickerDialog extends TimePickerDialog {
private final static int TIME_PICKER_INTERVAL = 15;
private TimePicker mTimePicker;
private final OnTimeSetListener mTimeSetListener;
public CustomTimePickerDialog(Context context,
OnTimeSetListener listener,
int hourOfDay,
int minute,
boolean is24HourView) {
super(context, TimePickerDialog.THEME_HOLO_LIGHT, null, hourOfDay,
minute / TIME_PICKER_INTERVAL, is24HourView);
fixSpinner(context, hourOfDay, minute, is24HourView);
mTimeSetListener = listener;
}
@Override
public void updateTime(int hourOfDay, int minuteOfHour) {
mTimePicker.setCurrentHour(hourOfDay);
mTimePicker.setCurrentMinute(minuteOfHour / TIME_PICKER_INTERVAL);
}
private void fixSpinner(Context context, int hourOfDay, int minute, boolean is24HourView) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && Build.VERSION.SDK_INT<Build.VERSION_CODES.P) { // android:timePickerMode spinner and clock began in Lollipop
try {
// Get the theme's android:timePickerMode
final int MODE_SPINNER = 1;
Class<?> styleableClass = Class.forName("com.android.internal.R$styleable");
Field timePickerStyleableField = styleableClass.getDeclaredField("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) {
mTimePicker = (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(mTimePicker);
Class<?> spinnerDelegateClass;
if (Build.VERSION.SDK_INT != Build.VERSION_CODES.LOLLIPOP) {
spinnerDelegateClass = Class.forName("android.widget.TimePickerSpinnerDelegate");
} else {
// TimePickerSpinnerDelegate was initially misnamed TimePickerClockDelegate in API 21!
spinnerDelegateClass = Class.forName("android.widget.TimePickerClockDelegate");
}
// In 7.0 Nougat for some reason the timePickerMode is ignored and the delegate is TimePickerClockDelegate
if (delegate.getClass() != spinnerDelegateClass) {
delegateField.set(mTimePicker, null); // throw out the TimePickerClockDelegate!
mTimePicker.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(mTimePicker, context, null, android.R.attr.timePickerStyle, 0);
delegateField.set(mTimePicker, delegate); // set the TimePicker.mDelegate to the spinner delegate
// Set up the TimePicker again, with the TimePickerSpinnerDelegate
mTimePicker.setIs24HourView(is24HourView);
mTimePicker.setCurrentHour(hourOfDay);
mTimePicker.setCurrentMinute(minute);
mTimePicker.setOnTimeChangedListener(this);
}
}
} catch (Exception e) {
Mint.logException(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) {
Mint.logException(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;
}
@Override
public void onClick(DialogInterface dialog, int which) {
switch (which) {
case BUTTON_POSITIVE:
if (mTimeSetListener != null && mTimePicker!=null) {
mTimeSetListener.onTimeSet(mTimePicker, mTimePicker.getCurrentHour(),
mTimePicker.getCurrentMinute() * TIME_PICKER_INTERVAL);
}
break;
case BUTTON_NEGATIVE:
cancel();
break;
}
}
@SuppressLint("DefaultLocale")
@Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
try {
@SuppressLint("PrivateApi")
Class<?> classForid = Class.forName("com.android.internal.R$id");
Field timePickerField = classForid.getField("timePicker");
mTimePicker = findViewById(timePickerField.getInt(null));
Field field = classForid.getField("minute");
NumberPicker minuteSpinner = mTimePicker
.findViewById(field.getInt(null));
minuteSpinner.setMinValue(0);
minuteSpinner.setMaxValue((60 / TIME_PICKER_INTERVAL) - 1);
List<String> displayedValues = new ArrayList<>();
for (int i = 0; i < 60; i += TIME_PICKER_INTERVAL) {
displayedValues.add(String.format("%02d", i));
}
minuteSpinner.setDisplayedValues(displayedValues
.toArray(new String[displayedValues.size()]));
} catch (Exception e) {
Mint.logException(e);
e.printStackTrace();
}
}
}
My code is not working on android 9 (On other versions it is working). It showing this error
" java.lang.RuntimeException: java.lang.NoSuchFieldException: mTimePicker "
Please suggest me any solution
This is the code for CustomTimePickerDialog.java class
import android.app.TimePickerDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.os.Build;
import android.os.Bundle;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.widget.NumberPicker;
import android.widget.TimePicker;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
public class CustomTimePickerDialog extends TimePickerDialog {
final OnTimeSetListener mCallback;
TimePicker timePicker;
final int increment ;
boolean isMinuteVisible = true;
private TimePicker.OnTimeChangedListener mOnTimeChangedListener = null;
public CustomTimePickerDialog(Context context, OnTimeSetListener callBack, int hourOfDay, int minute, boolean is24HourView, int increment) {
super(context, TimePickerDialog.THEME_HOLO_LIGHT, callBack, hourOfDay, minute / increment, is24HourView);
fixSpinner(context, hourOfDay, minute / increment , is24HourView);
this.mCallback = callBack;
this.increment = increment;
}
public CustomTimePickerDialog(Context context, OnTimeSetListener callBack, int hourOfDay, int minute, boolean is24HourView, int increment, boolean isMinuteVisible) {
this(context, callBack, hourOfDay, minute, is24HourView, increment);
this.isMinuteVisible = isMinuteVisible;
}
public void setOnTimeChangedListener(TimePicker.OnTimeChangedListener onTimeChangedListener) {
this.mOnTimeChangedListener = onTimeChangedListener;
}
private void fixSpinner(Context context, int hourOfDay, int minute, boolean is24HourView) {
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.P) { // fixes the bug in API 24
try {
// Get the theme's android:timePickerMode
final int MODE_SPINNER = 1;
Class<?> styleableClass = Class.forName("com.android.internal.R$styleable");
Field timePickerStyleableField = styleableClass.getField("mTimePicker");
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) 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 {
// TimePickerSpinnerDelegate was initially misnamed TimePickerClockDelegate in API 21!
spinnerDelegateClass = Class.forName("android.widget.TimePickerClockDelegate");
}
// spinnerDelegateClass = Class.forName("android.widget.TimePickerSpinnerDelegate");
// In 7.0 Nougat for some reason the timePickerMode is ignored and the delegate is 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;
}
@Override
public void onClick(DialogInterface dialog, int which) {
switch (which) {
case BUTTON_POSITIVE:
if (mCallback != null && timePicker != null) {
timePicker.clearFocus();
mCallback.onTimeSet(timePicker, timePicker.getCurrentHour(),
timePicker.getCurrentMinute() * increment);
}
}
}
@Override
protected void onStop() {
// override and do nothing
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
try {
// Class<?> rClass = Class.forName("com.android.internal.R$id");
//getClass().getSuperclass();
//Class.forName("com.android.internal.R$id");
//Class<?> rClass = Class.forName("com.android.internal.R$styleable");
/* Field timepicker = rClass.getDeclaredField("mTimePicker");
TimePicker timePicker = (TimePicker) findViewById(timepicker.getInt(this));/
/ Class rClass = getClass().getSuperclass();
Field mTimePickerField = rClass.getDeclaredField("mTimePicker");
mTimePickerField.setAccessible(true);*/
// TimePicker mTimePicker = (TimePicker) mTimePickerField.get(this);
timePicker = findViewById (Resources.getSystem().getIdentifier("mTimePicker", "id", "android"));
timePicker.setOnTimeChangedListener(new TimePicker.OnTimeChangedListener() {
@OverRide
public void onTimeChanged(TimePicker view, int hourOfDay, int minute) {
if(mOnTimeChangedListener != null) {
mOnTimeChangedListener.onTimeChanged(view, hourOfDay, minute);
}
}
});
/*Class classForid = Class.forName("com.android.internal.R$id");
// Field m = classForid.getField("minute");
Field m = classForid.getField("minute");*/
NumberPicker mMinuteSpinner = timePicker.findViewById(Resources.getSystem().getIdentifier(
"minute",
"id",
"android"
));
// NumberPicker mMinuteSpinner = (NumberPicker) mTimePicker.findViewById(m.getInt(this));
if (this.isMinuteVisible) {
mMinuteSpinner.setVisibility(View.VISIBLE);
mMinuteSpinner.setMinValue(0);
mMinuteSpinner.setMaxValue((60 / increment) - 1);
List displayedValues = new ArrayList();
for (int i = 0; i < 60; i += increment) {
displayedValues.add(String.format("%02d", i));
}
mMinuteSpinner.setDescendantFocusability(NumberPicker.FOCUS_BLOCK_DESCENDANTS);
mMinuteSpinner.setDisplayedValues(displayedValues.toArray(new String[0]));
} else {
mMinuteSpinner.setVisibility(View.GONE);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
Please provide full source code. I mean how i can call this class?