在DatePicker上设置MinDate会将CalendarView移动到1964

时间:2013-05-21 07:36:32

标签: android android-datepicker

如果设置了非默认的最小日期,我正在调试CalendarViewDatePicker移至1964年10月的问题。这至少在API17 Nexus7仿真器上重现,但在其他设备上有关于此问题的报告,其中平台样式包括日期选择器中的微调器和日历视图。

以下是演示此问题的代码段:

class DatePickerDialog1964 extends DatePickerDialog {
    DatePickerDialog1964(Context c) {
        super(c, null, 2013, 4, 21);

        @SuppressWarnings("deprecation")
        Date min = new Date(2013-1900, 4, 21);

        DatePicker p = getDatePicker();
        p.setMinDate(min.getTime());
    }
}

...

new DatePickerDialog1964(context).show();

屏幕截图:

October 1964 calendar view in date picker

当然,期望旋转器和日历视图显示相同的日期。

我将继续对此进行调试,但如果有任何SO用户有经验或其他见解,请分享。

相关:

5 个答案:

答案 0 :(得分:19)

TL; DR

这不是1964年,但2100格式严重。 CalendarView有错误。 formatDateRange()在2038年之后无效。

解决方法#1

class DatePickerDialog1964 extends DatePickerDialog {
    DatePickerDialog1964(Context c) {
        super(c, null, 2013, 4, 21);

        @SuppressWarnings("deprecation")
        Date min = new Date(2013-1900, 4, 21);

        DatePicker p = getDatePicker();
        CalendarView cv = p.getCalendarView(); // should check for null
        long cur = cv.getDate();
        int d = cv.getFirstDayOfWeek();
        p.setMinDate(min.getTime());
        cv.setDate(cur + 1000L*60*60*24*40);
        cv.setFirstDayOfWeek((d + 1) % 7);
        cv.setDate(cur);
        cv.setFirstDayOfWeek(d);
    }
}

解决方法#2我实际使用了

// Calendar view is a cascade of bugs.
// Work around that by explicitly disabling it.
datePicker.setCalendarViewShown(false);

分析

CalendarView使用android.text.format.DateUtils.formatDateRange()在标头中生成月份/年份名称。日历视图仅在月份数更改时更新标题。例如,如果月份数相同但年份更改,则不更新标题。

在布局阶段的某个地方,基础ListView会在日历视图上调用OnScrollListener,就好像列表已滚动到最后一样。如果更改了月份数,则使用上面的测试代码更新标题,传递给formatDateRange()的毫秒值为4131043200000,这是2100年末的某个地方,2100是{{1的默认最大值}}

DatePicker反过来使用formatDateRange()作为其内部日历表示,特别是使用其android.text.format.Time方法设置millis。我没有检查底层的本机代码,但我的有根据的猜测是,传入的毫秒值被截断为32位set()秒值与固有Year 2038 problem,包装到一个更多的值超过62年。 time_t本身正在使用Time,它代表自1900年以来的struct tm年,因此在20世纪60年代导致int,然后在标题中进行格式化。

这可以使用TimeDatePicker进行复制,而无需设置最小日期。只需使用微调器将年份移动到2038或更高版本,更改月份(触发标题更新),您将看到标题换行中的年份为1902。

现在,问题仍然是为什么设置最小日期会使日历视图滚动到最大日期。答案似乎是日历视图的周CalendarView适配器。它从最小日期开始索引周数,但是当最小日期更改时,不会调用适配器的ListView。所以上面的解决方法#1有效,因为:

  • 将日期更改超过一个月会使月份标题发生变化。
  • 更改一周的第一天会使周列表视图通知其适配器的数据已更改。

答案 1 :(得分:12)

以上答案是关于此错误的来源。对于我的应用程序,我正在使用此解决方法:

每次更改DatePicker的日期或minDate时,请使用应在选择器/日历中选择的日期调用以下例程:

private void fixUpDatePickerCalendarView(Calendar date) {
    // Workaround for CalendarView bug relating to setMinDate():
    // https://code.google.com/p/android/issues/detail?id=42750
    // Set then reset the date on the calendar so that it properly
    // shows today's date. The choice of 24 months is arbitrary.
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
        final CalendarView cal = datePicker.getDatePicker().getCalendarView();
        if (cal != null) {
            date.add(Calendar.MONTH, 24);
            cal.setDate(date.getTimeInMillis(), false, true);
            date.add(Calendar.MONTH, -24);
            cal.setDate(date.getTimeInMillis(), false, true);
        }
    }
}

答案 2 :(得分:2)

我最终使用的解决方案:

private void fixBuggyCalendarview(CalendarView cv) {
    long current = cv.getDate();
    cv.setDate(cv.getMaxDate(), false, true);
    cv.setDate(current, false, true);
}

当您设置CalendarView的最小/最大日期

时,这也适用

答案 3 :(得分:0)

我同意@laalto。最初的CalendarView是一团糟。我也遇到了语言环境,Android 4.1.2上的字体大小问题(哦,是的,它已经向下移动),缺少可访问性功能等等。所以我决定复制the source code并实现我自己的日历。例如,现在正确显示标题文本。

/**
 * The source code has the Year 2038 problem and doesn't honor CalendarView's mCurrentLocale.
 */
private void setMonthDisplayed(Calendar calendar) {
    mMonthName.setText(DateFormat.format("MMMM, yyyy", calendar));
    mMonthName.invalidate();

    mCurrentMonthDisplayed = calendar.get(Calendar.MONTH);
    mAdapter.setFocusMonth(mCurrentMonthDisplayed);
}

/**
 * This imlementation formats the date correctly and uses the stand-alone form of a month (for the languages where it's important).
 */
private void setMonthDisplayed(Calendar calendar) {
    mMonthName.setText(new SimpleDateFormat("LLLL yyyy", mCurrentLocale).format(calendar.getTime()));
    mMonthName.invalidate();

    mCurrentMonthDisplayed = calendar.get(Calendar.MONTH);
    mAdapter.setFocusMonth(mCurrentMonthDisplayed);
}

答案 4 :(得分:0)

在实施上述任何建议之前,请检查以确保您的模拟器在今天的日期和时间运行。如果打开一段时间,可能会滞后几天。我只是通过重启模拟器来修复我的问题。