Android 7 Nougat上的DatePickerDialog Holo样式失败

根据我们的客户需求,我们希望在所有Android操作系统版本的DatePickerDialog上保留HOLO样式,例如: Android 7上的DatePicker-

但它似乎无法在Android 7上正常工作:

Android 7上的DatePicker

从我的实施:

new DatePickerDialog(getContext(), AlertDialog.THEME_HOLO_LIGHT, mCalendarPickerListener, calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH), calendar.get(Calendar.DAY_OF_MONTH)); 

它适用于之前的Android 7.任何人都有相同的问题?

已编辑:解决方案已在API 25中修复https://code.google.com/u/106133255289400340786/

我使用DatePickerDialog来提示用户他们的生日。 不幸的是,在收到材料主题对话框时,我收到了一些关于材料主题对话框的投诉,所以切换到它不是我的选择:我必须坚持以Holo为主题的对话框。

事实certificate,Android 7.0附带了一个错误:试图在这个平台上使用Holo主题,而不是使用DatePickerDialog破碎材质主题。 看到这两个错误报告:

  • 问题222808
  • 问题222208

我使用了Jeff Lockhart在这些错误报告中引用的这种变通方法的修改forms:

 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的实例(我们想要的委托types)
  • DatePicker删除所有视图,因为它们是材质日历窗口小部件
  • 创建DatePickerSpinnerDelegate的新实例,并将其分配给此对话框的mDatePickermDelegate字段
  • 使用日历信息和一些参数重新初始化mDatePicker以使其膨胀微调器

要使用此解决方法,我在Context创建了一个ContextThemeWrapper ,它允许我设置一个主题,在本例中为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) ); 

备注

  • 这使用reflection来访问私有字段。 一般来说,这不是一种强有力的方法,你不能指望它。 我通过1)将此限制为单个SDK版本v24来降低风险; 2)将整个reflection代码包装在try {...} catch (Exception e) {/* NOP */}块中,所以如果任何reflection失败,什么都不会发生,并且(遗憾地破坏)默认将使用材料后备。
  • 上面的错误报告声称此问题已在Android 7.1(SDK 25)中得到修复。 我没有测试过这个。
  • 最初的解决方法代码是针对遇到类似问题的TimePickerDialog 。 我已将其修改为与DatePickerDialog使用,并且还将解决方案简化为不太通用,并且更具体到我的确切用例。 但是,您可以使用更完整的原始版本,只需将其调整为Date而不是Time

日期选择器有自己独立的样式https://developer.android.com/reference/android/R.style.html#Widget_DatePicker

我认为你需要R.style.Widget.Holo.DatePicker而不是AlertDialog.THEME_HOLO_LIGHT 。 您可能需要创建自己的空样式,其中包含@android:style/Widget.Holo.DatePicker作为其父级并使用它。

尝试以下它是有效的。 我测试过了。 它没有空白。

mDatePickerListener也是我可以创建的DatePickerListner。

 //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();