根据我们的客户需求,我们希望在所有 Android 操作系统版本的 DatePickerDialog 上保留 HOLO 样式,例如:Android 7 上的日期选择器-
From our customer demand we want to keep the HOLO style on the DatePickerDialog for all Android OS version, sth like: DatePicker on Android 7-
但它似乎无法在 Android 7 上正常工作:
But it seems to not to work correctly on Android 7:
Android 7 上的日期选择器
来自我的实现:
new DatePickerDialog(getContext(), AlertDialog.THEME_HOLO_LIGHT,
mCalendarPickerListener,
calendar.get(Calendar.YEAR),
calendar.get(Calendar.MONTH),
calendar.get(Calendar.DAY_OF_MONTH));
在之前的 android 7 上运行良好.有人有同样的问题吗?
It's working fine for prior android 7. Anybody has the same issue?
已发现解决方案已在 API 25 中修复https://code.google.com/u/106133255289400340786/
Edited: solution is found to be fixed in API 25 https://code.google.com/u/106133255289400340786/
我使用 DatePickerDialog
来提示用户生日.不幸的是,我在尝试使用 Material 主题对话框时收到了很多用户投诉,因此切换到它不是我的选择:我必须坚持使用 Holo 主题对话框.
I use a DatePickerDialog
to prompt users for their birthdays. Unfortunately, I've received a number of complaints from users about the Material-themed dialog when trying it, so switching to it is not an option for me: I have to stick to the Holo-themed dialog.
事实证明,Android 7.0 附带了一个错误:尝试在此平台上使用 Holo 主题而不是为 DatePickerDialog
使用 broken Material 主题.请参阅以下两个错误报告:
It turns out that Android 7.0 shipped with a bug: trying to use the Holo theme on this platform instead falls back to using a broken Material theme for the DatePickerDialog
. See these two bug reports:
我使用了 Jeff Lockhart 提出的这个解决方法 的修改形式,在这些错误报告中引用:
I used a modified form of this workaround by Jeff Lockhart referenced in those bug reports:
private static final class FixedHoloDatePickerDialog extends DatePickerDialog {
private FixedHoloDatePickerDialog(Context context, OnDateSetListener callBack,
int year, int monthOfYear, int dayOfMonth) {
super(context, callBack, year, monthOfYear, dayOfMonth);
// Force spinners on Android 7.0 only (SDK 24).
// Note: I'm using a naked SDK value of 24 here, because I'm
// targeting SDK 23, and Build.VERSION_CODES.N is not available yet.
// But if you target SDK >= 24, you should have it.
if (Build.VERSION.SDK_INT == 24) {
try {
final Field field = this.findField(
DatePickerDialog.class,
DatePicker.class,
"mDatePicker"
);
final DatePicker datePicker = (DatePicker) field.get(this);
final Class<?> delegateClass = Class.forName(
"android.widget.DatePicker$DatePickerDelegate"
);
final Field delegateField = this.findField(
DatePicker.class,
delegateClass,
"mDelegate"
);
final Object delegate = delegateField.get(datePicker);
final Class<?> spinnerDelegateClass = Class.forName(
"android.widget.DatePickerSpinnerDelegate"
);
if (delegate.getClass() != spinnerDelegateClass) {
delegateField.set(datePicker, null);
datePicker.removeAllViews();
final Constructor spinnerDelegateConstructor =
spinnerDelegateClass.getDeclaredConstructor(
DatePicker.class,
Context.class,
AttributeSet.class,
int.class,
int.class
);
spinnerDelegateConstructor.setAccessible(true);
final Object spinnerDelegate = spinnerDelegateConstructor.newInstance(
datePicker,
context,
null,
android.R.attr.datePickerStyle,
0
);
delegateField.set(datePicker, spinnerDelegate);
datePicker.init(year, monthOfYear, dayOfMonth, this);
datePicker.setCalendarViewShown(false);
datePicker.setSpinnersShown(true);
}
} catch (Exception e) { /* Do nothing */ }
}
}
/**
* Find Field with expectedName in objectClass. If not found, find first occurrence of
* target fieldClass in objectClass.
*/
private Field findField(Class objectClass, Class fieldClass, String expectedName) {
try {
final Field field = objectClass.getDeclaredField(expectedName);
field.setAccessible(true);
return field;
} catch (NoSuchFieldException e) { /* Ignore */ }
// Search for it if it wasn't found under the expectedName.
for (final Field field : objectClass.getDeclaredFields()) {
if (field.getType() == fieldClass) {
field.setAccessible(true);
return field;
}
}
return null;
}
}
这是做什么的:
DatePicker mDatePicker
字段DatePickerDelegate mDelegate
字段DatePickerSpinnerDelegate
的实例(我们想要的委托类型)DatePicker
中移除所有视图,因为它们是 Material 日历小部件DatePickerSpinnerDelegate
实例,并将其分配给该对话框的mDatePicker
的mDelegate
字段mDatePicker
以使其为微调器充气DatePicker mDatePicker
field belonging to this dialogDatePickerDelegate mDelegate
field belonging to this dialogDatePickerSpinnerDelegate
(the type of delegate we want)DatePicker
, since they are the Material calendar widgetsDatePickerSpinnerDelegate
, and assign it to the mDelegate
field of mDatePicker
of this dialogmDatePicker
with calendar info and some params to get it to inflate the spinners为了使用这个解决方法,我在我的 Context
周围创建了一个 ContextThemeWrapper
,它允许我设置一个主题,在本例中是 Holo:
To use this workaround, I create a ContextThemeWrapper
around my Context
, which allows me to set a theme, in this case Holo:
final Context themedContext = new ContextThemeWrapper(
this.getContext(),
android.R.style.Theme_Holo_Light_Dialog
);
final DatePickerDialog dialog = new FixedHoloDatePickerDialog(
themedContext,
datePickerListener,
calender.get(Calendar.YEAR),
calendar.get(Calendar.MONTH),
calendar.get(Calendar.DAY_OF_MONTH)
);
<小时>
注意事项:
try {...} catch (Exception e) {/* NOP *