Java Android 7牛轧糖上的DatePickerDialog Holo样式设置失败
根据我们的客户需求,我们希望在所有Android OS版本的DatePickerDialog上保持HOLO风格,例如: 但在Android 7上似乎无法正常工作: 来自我的实现:Java Android 7牛轧糖上的DatePickerDialog Holo样式设置失败,java,android,calendar,Java,Android,Calendar,根据我们的客户需求,我们希望在所有Android OS版本的DatePickerDialog上保持HOLO风格,例如: 但在Android 7上似乎无法正常工作: 来自我的实现: new DatePickerDialog(getContext(), AlertDialog.THEME_HOLO_LIGHT, mCalendarPickerListener, calendar.get(Calenda
new DatePickerDialog(getContext(), AlertDialog.THEME_HOLO_LIGHT,
mCalendarPickerListener,
calendar.get(Calendar.YEAR),
calendar.get(Calendar.MONTH),
calendar.get(Calendar.DAY_OF_MONTH));
它在之前的安卓7上运行良好。有人有同样的问题吗
已编辑:在API 25中发现解决方案是固定的
日期选择器有自己的独立样式
我认为您需要
R.style.Widget.Holo.DatePicker
而不是AlertDialog.THEME\u Holo\u LIGHT
。您可能需要创建自己的空样式,将@android:style/Widget.Holo.DatePicker
作为其父样式并使用它 我使用DatePickerDialog
提示用户生日。不幸的是,我在尝试使用材料主题对话框时收到了很多用户的投诉,因此切换到材料主题对话框对我来说不是一个选择:我必须坚持使用全息主题对话框
结果证明,Android 7.0附带了一个bug:尝试在这个平台上使用Holo主题,而不是在DatePickerDialog
中使用中断的材质主题。请参阅以下两个错误报告:
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;
}
}
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;
}
}
注释:
- 这使用反射来访问私有字段。一般来说,这不是一个健壮的方法,您不能指望它。我在这里通过1)将此限制为单个SDK版本v24来降低风险;和2)将反射代码的整个位包装在
块中,因此如果任何反射失败,将不会发生任何事情,并且将使用(令人遗憾地损坏的)默认材质回退try{…}catch(异常e){/*NOP*/}
- 上面的bug报告声称这个问题已经在Android 7.1(SDK 25)中修复。我还没有测试过这个
- 用于
的是遇到类似问题的。我将其修改为使用TimePickerDialog
,并简化了解决方案,使其不那么通用,更适合我的具体用例。但是,您可以使用更完整的原始版本,只需将其调整为DatePickerDialog
,而不是Date
Time
mDatePickerListener
是您可以创建的我的datepickerlistener
//Setting theme to android.R.style.Theme_Holo_Light_Dialog
Calendar cal = Calendar.getInstance(TimeZone.getDefault());
DatePickerDialog datePicker = new DatePickerDialog(getContext(), android.R.style.Theme_Holo_Light_Dialog,
mDatePickerListener,
cal.get(Calendar.YEAR),
cal.get(Calendar.MONTH),
cal.get(Calendar.DAY_OF_MONTH));
datePicker.setCancelable(false);
datePicker.setTitle(getString(R.string.select_your_dob));
//This line is important to remove blank gaps
datePicker.getWindow().setBackgroundDrawable(new ColorDrawable(android.graphics.Color.TRANSPARENT));
datePicker.show();
除此之外:自API 28起,全息灯主题已被弃用。如果您的目标是API 21/Lollipop设备和更新版本,则应使用材质设计样式,请选中。我定义了这些样式,然后链接到ContextThemeWrapper
中的R.style.dlg\u日期选择器
:
<style name="dlg_datePicker" parent="Theme.AppCompat.Light.Dialog">
<item name="android:datePickerStyle">@style/datePicker_style</item>
<item name="android:textSize">18sp</item>
</style>
<style name="datePicker_style" parent="android:Widget.Material.Light.DatePicker">
<item name="android:datePickerMode">spinner</item>
</style>
@样式/日期选择器\u样式
18便士
纺纱机
你更新了你的支持库吗?@Shuddh已经尝试过了,但还是一样:)我尝试了“24.2.1”,我也遇到了这个问题。有人有解决办法吗?我的解决办法是推回土生土长的拾荒者风格。不再强制HOLO_LIGHT…它不能正常工作,而是显示一个带有空白空白的全屏日历。尽管这是一个解决问题,但无论如何,谢谢,这听起来是可行的。听起来正确的修复应该使用API 25 set date listener不起作用,总是返回您保存的初始日期=)谢谢,它起作用了!但是文本和线条的颜色是白色的,我怎么能改变它呢?这在HTC One M或三星Galaxy A5-2016上不起作用,因为Android 7/API 24:它让日期选择器处于日历模式,而不是旋转模式。