【问题标题】:How to fix DatePickerDialog in spinner mode for Android 7.0?如何在 Android 7.0 的微调器模式下修复 DatePickerDialog?
【发布时间】:2018-07-20 18:37:38
【问题描述】:

我目前正在开发一个简单的项目,其中包含一个包含在 Web 视图中的网站,并通过少量交互来提高网站本身与 Android 移动设备之间的交互性。

由于该网站包含用户生日的日期输入字段,因此我希望实现一个与所有设备兼容的微调器格式的日期选择器。 我尝试实施以下解决方案:

`<style name="MyAppTheme" parent="android:Theme.Material">
    <item name="android:dialogTheme">@style/MyDialogTheme</item>
    <item name="android:datePickerStyle">@style/MyDatePicker</item>
</style>
<style name="MyDialogTheme" parent="android:Theme.Material.Dialog">
    <item name="android:datePickerStyle">@style/MyDatePicker</item>
</style>
<style name="MyDatePicker" parent="android:Widget.Material.DatePicker">
    <item name="android:datePickerMode">spinner</item>
</style>`

如图: Datepicker dialog without calendar visualization in lollipop [spinner mode]?

但是,正如答案所指出的,由于以下已知错误,此解决方案不适用于 Android 7.0 Nougat / API 24:https://issuetracker.google.com/issues/37119315

做一些研究,我发现了一些建议的解决方案,例如:

https://gist.github.com/uqmessias/d9a8dc624af935f344dfa2e8928490ec

https://gist.github.com/lognaturel/232395ee1079ff9e4b1b8e7096c3afaf

DatePickerDialog Holo styling failed on Android 7 Nougat

但就我而言,它们似乎都不适用于搭载 Android 7.0 的设备或任何其他设备。

我在 android 开发方面非常缺乏经验,所以我想知道是什么导致了这个问题以及我该如何解决它,因为一些解决方案直到最近才得到维护,几乎丢弃了它们可能已经过时的问题。

我是否需要实现我发布的代码中没有指出但我不知道的东西?

是不是直接从 Web 视图调用 datepicker 元素,而不是从应用程序本身调用这些解决方案对我来说毫无用处?如果是这样,是否有任何解决方法?

【问题讨论】:

  • 你想要一个微调器的原因是为了更容易选择遥远过去的一年(方便生日)?如果是这样,您还可以强制日期选择器首先以年份选择模式启动(这对我有用,因为正如您所指出的,在 API 24 中强制微调器似乎不起作用)
  • @TylerV 听起来是个不错的选择。我并没有真正意识到这是一种可能性。你有什么我可以遵循的步骤来完成吗?也感谢您的快速响应。
  • 当然,我会发布一个答案。

标签: java android calendar android-datepicker


【解决方案1】:

我遇到了同样的问题(用户不想逐月回溯 40 年以找到他们的出生年份,而且大多数人不知道您可以在 android 日期选择器中单击年份来滚动多年来)。和你一样,我无法让微调器通用,所以我想出了(在 SO 和 Google 的帮助下)如何让它在年份选择模式下启动。

我的DatePickerDialogFragment 的代码粘贴在下面。

public class DatePickerDialogFragment extends DialogFragment {

    private DatePickerDialog.OnDateSetListener listener = null;

    void setListener(DatePickerDialog.OnDateSetListener listener) {
        this.listener = listener;
    }

    private static final String START_IN_YEARS = "com.myapp.picker.START_IN_YEARS";
    private static final String YEAR = "com.myapp.picker.YEAR";
    private static final String MONTH = "com.myapp.picker.MONTH";
    private static final String DAY_OF_MONTH = "com.myapp.picker.DAY_OF_MONTH";

    public static DatePickerDialogFragment newInstance(boolean startInYears, Calendar c) {
        DatePickerDialogFragment f = new DatePickerDialogFragment();

        int year = c.get(Calendar.YEAR);
        int month = c.get(Calendar.MONTH);
        int day = c.get(Calendar.DAY_OF_MONTH);

        Bundle args = new Bundle();
        args.putBoolean(START_IN_YEARS, startInYears);
        args.putInt(YEAR, year);
        args.putInt(MONTH, month);
        args.putInt(DAY_OF_MONTH, day);

        f.setArguments(args);
        return f;
    }

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {

        Bundle args = getArguments();
        DatePickerDialog dpd = null;

        if( listener != null && args != null) {
            boolean startInYears = args.getBoolean(START_IN_YEARS);

            Context context = getActivity();
            boolean requireSpinnerMode = isBrokenSamsungDevice();
            if (requireSpinnerMode) {
                context = new ContextThemeWrapper(context, android.R.style.Theme_Holo_Light_Dialog);
            }

            int year = args.getInt(YEAR);
            int month = args.getInt(MONTH);
            int day = args.getInt(DAY_OF_MONTH);

            dpd = new DatePickerDialog(context, listener, year, month, day);

            if (startInYears && !requireSpinnerMode) {
                boolean canOpenYearView = openYearView(dpd.getDatePicker());
                if (!canOpenYearView) {
                    context = new ContextThemeWrapper(getActivity(), android.R.style.Theme_Holo_Light_Dialog);
                    dpd = new DatePickerDialog(context, listener, year, month, day);
                }
            }
        }
        else {
            setShowsDialog(false);
            dismissAllowingStateLoss();
        }

        return dpd;
    }

    private static boolean isBrokenSamsungDevice() {
        return Build.MANUFACTURER.equalsIgnoreCase("samsung") &&
                isBetweenAndroidVersions(
                        Build.VERSION_CODES.LOLLIPOP,
                        Build.VERSION_CODES.LOLLIPOP_MR1);
    }

    private static boolean isBetweenAndroidVersions(int min, int max) {
        return Build.VERSION.SDK_INT >= min && Build.VERSION.SDK_INT <= max;
    }

    private static boolean openYearView(DatePicker datePicker) {
        if( isBrokenSamsungDevice() ) {
            return false;
        }

        View v = datePicker.findViewById(Resources.getSystem().getIdentifier("date_picker_header_year", "id", "android"));
        if( v != null ) {
            v.performClick();
        }
        else {
            try {
                Field mDelegateField = datePicker.getClass().getDeclaredField("mDelegate");
                mDelegateField.setAccessible(true);
                Object delegate = mDelegateField.get(datePicker);
                Method setCurrentViewMethod = delegate.getClass().getDeclaredMethod("setCurrentView", int.class);
                setCurrentViewMethod.setAccessible(true);
                setCurrentViewMethod.invoke(delegate, 1);
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
        }
        return true;
    }
}

Activity 中的代码(onCreate 中的成员变量和内容)启动它(并在旋转时保留它)如下所示:

// Class member variables
private Calendar myCalendar = Calendar.getInstance();
private boolean birthday_is_set = false;


// this next part is in onCreate

// set the calendar date to a saved date if applicable
// and change birthday_is_set if they had saved a birthday


final DatePickerDialog.OnDateSetListener birthdayListener = new DatePickerDialog.OnDateSetListener() {

    @Override
    public void onDateSet(DatePicker view, int year, int monthOfYear,
                          int dayOfMonth) {
        // I save the date in a calendar, replace this
        // with whatever you want to do with the selected date
        myCalendar.set(Calendar.YEAR, year);
        myCalendar.set(Calendar.MONTH, monthOfYear);
        myCalendar.set(Calendar.DAY_OF_MONTH, dayOfMonth);
        birthday_is_set = true;
        updateBirthdayLabel();
    }
};

if (savedInstanceState != null) {
    DatePickerDialogFragment dpf;

    dpf = (DatePickerDialogFragment) getFragmentManager().findFragmentByTag("birthdayDatePicker");
    if (dpf != null) {
        // on rotation the listener will be referring to the old Activity,
        // so we have to reset it here to act on the current Activity
        dpf.setListener(birthdayListener);
    }
}

birthdayDatePicker.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        // Your logic may vary here. I chose not to start it in year
        // mode if they've already selected a date.
        boolean startInYears = !birthday_is_set;
        DatePickerDialogFragment dpf = DatePickerDialogFragment.newInstance(startInYears, myCalendar);
        dpf.setListener(birthdayListener);
        dpf.show(getFragmentManager(), "birthdayDatePicker");
    }
});

这包括让它在年份模式下启动的 hack,以及针对特定年份三星设备上的一些随机日期选择器故障的修复。此版本已经运行了几个月,没有出现针对 API 15+ 的崩溃或用户投诉。

编辑:更新 openYearView 以在 Android 10 上运行

【讨论】:

  • 我注意到您调用了 onClick 事件来设置和调用片段。鉴于我使用来自 webView 的输入字段,我没有这样的事件。在这种情况下,我可以从我的 WebViewInterface 中的函数发出呼叫吗?或者移动birthdayDatePicker.setOnClickListener 的内容会破坏整个事情?
  • 另外,我不知道你也可以点击年份,所以在最坏的情况下,我只会提示用户这样做。这比我拥有的领先联盟。
  • 您可以将 onClick 中的内容移动到您启动 DatePicker 的任何位置。 birthdayListener(在 onDateSet 方法中)中的内容将处理用户从日期选择器中选择日期后要执行的操作。
【解决方案2】:

对于任何有兴趣使 WebView 中的 DatePicker 正常工作而不添加 JS 接口来手动调用您自己的 DatePicker 的人,这里有一个可以覆盖您应用中所有 DatePicker 的解决方案:

https://github.com/Noisyfox/DatePickerForceSpinner

此修复也适用于 LayoutInflater 创建的任何 DatePicker,因此您无需担心更改现有布局文件。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多