Android 7 Nougat上的DatePickerDialog Holo样式失败 [英] DatePickerDialog Holo styling failed on Android 7 Nougat
问题描述
根据我们的客户需求,我们希望在所有Android操作系统版本的DatePickerDialog上保留HOLO样式,例如:
DatePicker -
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:
来自我的实施:
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
提示用户过生日。不幸的是,我在收到材料主题对话框时收到了一些关于材料主题对话框的投诉,所以切换到它不是我的选择:我必须坚持以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
。请参阅以下两个错误报告:
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:
- Issue 222808
- Issue 222208
我使用了 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
中删除所有视图,因为它们是材料日历小部件 - 创建
DatePickerSpinnerDelegate
的新实例,并将其分配给mDelegate
此对话框的mDatePicker
字段 - 重新初始化
mDatePicker
使用日历信息和一些参数来使其膨胀旋转器
- Get the private
DatePicker mDatePicker
field belonging to this dialog - Get the private
DatePickerDelegate mDelegate
field belonging to this dialog - Check that the delegate is not already an instance of
DatePickerSpinnerDelegate
(the type of delegate we want) - Remove all views from the
DatePicker
, since they are the Material calendar widgets - Create a new instance of
DatePickerSpinnerDelegate
, and assign it to themDelegate
field ofmDatePicker
of this dialog - Re-initialize
mDatePicker
with calendar info and some params to get it to inflate the spinners
使用这个解决方法,我在我的 Context
周围创建一个 ContextThemeWrapper
,这允许我设置为heme,在这种情况下是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)
);
注释:
- 这使用反射访问私有字段。一般来说,这不是一种强有力的方法,你不能指望它。我通过1)将此限制为单个SDK版本v24来降低风险; 2)将整个反射代码包裹在
try {...} catch(例外e){/ * NOP * /}
块中,所以如果有的话反射失败,什么都不会发生,并且将使用(遗憾地破坏)默认材料后备。 - 上述错误报告声称此问题已在Android 7.1(SDK 25)中修复。我没有对此进行测试。
- 原始解决方法代码用于
TimePickerDialog
遭遇类似问题。我已将其修改为与DatePickerDialog
一起使用,并且还将解决方案简化为不太通用,并且更具体到我的确切用例。但是,您可以使用更完整的原始版本,只需将其调整为日期
而不是时间
。
- This uses reflection to access private fields. Generally, this is not a robust approach and you can't count on it. I'm mitigating the risk here by 1) restricting this to a single SDK version, v24; and 2) wrapping the entire bit of reflection code in a
try {...} catch (Exception e) {/* NOP */}
block, so if any of the reflection fails, nothing will happen and the (sadly broken) default Material fallback will be used. - The bug reports above claim that this issue has been fixed in Android 7.1 (SDK 25). I have not tested this.
- The original workaround code was for
TimePickerDialog
that suffered from a similar problem. I've modified it to work withDatePickerDialog
instead, and also simplified the solution to be less generic, and more specific to my exact use case. However, you could use the more complete original version and just tweak it forDate
instead ofTime
.
这篇关于Android 7 Nougat上的DatePickerDialog Holo样式失败的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!