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

我正在debuggingDatePicker中的CalendarView移动到1964年10月(如果有非默认的最小date集)的问题。 这至less在API17 Nexus7仿真器上重现,但在其他平台样式包括date选取器中的旋转器和日历视图的其他设备上有关于此问题的报告。

这是一个代码片段,展示了这个问题:

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

屏幕截图:

日期选择器1964年10月日历视图

当然,期望是纺纱人和日历视图将显示相同的date。

我会继续debugging,但如果有任何SO用户有经验或其他见解,请分享。

有关:

  • 问题42750:CalendarView最初滚动到1964年
  • 使用CalendarView将来selectdate (android-developers)

Solutions Collecting From Web of "在DatePicker上设置MinDate将CalendarView移动到1964年"

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就好像列表滚动到最后一样。 如果月份编号被改变了,那么头文件用上面的testing代码进行更新,传递给formatDateRange()4131043200000值是4131043200000 ,它在2100年晚些时候,而2100是DatePicker的默认最大值。

formatDateRange()依次使用android.text.format.Time作为其内部日历表示forms,具体使用set()方法设置millis。 我没有检查底层的本机代码,但我的教育猜测是,传递的毫秒数值被截断为32位time_t秒的值与固有的2038年的问题 ,包装到一个有点超过62年的价值。 Time本身就是使用struct tm ,它代表了自1900年以来的年代,因此导致了1960年代的一个Time ,然后在头文件中被格式化。

这可以与简单的DatePickerCalendarView没有最小date设置复制。 只需使用旋钮将年份移动到2038年或更晚,更改月份(以触发标题更新),您将在标题换行中看到年份为1902年。

现在,问题仍然是为什么设置最短date使日历视图滚动到最大date。 答案似乎是日历视图的星期ListView适配器。 它从最短的date开始计算几周,但是当最小date改变时,适配器的notifyDataSetChanged()不会被调用。 所以上面的解决方法#1工作是因为:

  • 更改date超过一个月会使月份标题更改。
  • 更改一周的第一天使星期的listview注意到它的适配器的数据已经改变。

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

每次更改DatePicker的date或分钟date时,都应该在dateselect器中应用date来调用以下例程:

 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); } } } 

我最终使用的解决scheme:

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

当您为CalendarView设置最小/最大date时,这也适用

我同意@laalto。 原来的CalendarView是一团糟。 我也遇到了语言环境问题,Android 4.1.2上的字体大小问题(噢,是向下移动),缺乏辅助function等等。所以我决定复制源代码并实现自己的日历。 例如,现在标题文本显示正确。

 /** * 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); } 

在执行上述任何build议之前,请检查以确保您的模拟器在今天的date和时间运行。 如果开放一段时间,可能会滞后几天。 我通过重新启动模拟器解决了我的问题。