Android 7 Nougat上的DatePickerDialog Holo样式失败 [英] DatePickerDialog Holo styling failed on Android 7 Nougat

查看:334
本文介绍了Android 7 Nougat上的DatePickerDialog Holo样式失败的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

根据我们的客户需求,我们希望在所有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:

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.任何人都有同样的问题吗?

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 the mDelegate field of mDatePicker 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 with DatePickerDialog 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 for Date instead of Time.

这篇关于Android 7 Nougat上的DatePickerDialog Holo样式失败的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆