TL;博士
这不是 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 是DatePicker
.
formatDateRange()
反过来android.text.format.Time
用作其内部日历表示,特别是使用其set()
方法设置毫秒。我没有检查底层的本机代码,但我有根据的猜测是,由于固有的2038 年问题time_t
,以毫秒为单位的值被截断为 32 位秒值,包装为略高于 62 年的值。它本身使用which 表示自 1900 年以来的年份,因此导致1960 年代的 a 然后在标题中格式化。Time
struct tm
int
Time
这可以在没有设置最小日期的情况下使用纯文本进行DatePicker
复制CalendarView
。只需使用微调器将年份移动到 2038 年或更晚,更改月份(以触发标题更新),您就会看到标题换行中的年份为 1902。
现在,问题仍然是为什么设置最小日期会使日历视图滚动到最大日期。答案似乎是日历视图的周ListView
适配器。它索引从最小日期开始的周数,但是当最小日期更改时,notifyDataSetChanged()
不会调用适配器。所以上面的解决方法 #1 有效,因为:
- 将日期更改超过一个月会使月份标题更改。
- 更改一周的第一天会使一周列表视图注意到其适配器的数据已更改。