デフォルト以外の最小日付が設定されている場合、CalendarView
のDatePicker
が1964年10月に移動する問題をデバッグしています。これは少なくともAPI17Nexus7エミュレーターで再現されますが、プラットフォームスタイルにスピナーと日付ピッカーのカレンダービューの両方が含まれている他のデバイスでこの問題に関する報告があります。
この問題を示すコードスニペットは次のとおりです。
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();
スクリーンショット:
もちろん、スピナーとカレンダービューに同じ日付が表示されることが期待されます。
これをデバッグし続けますが、SOユーザーが経験やその他の洞察を持っている場合は、共有してください。
関連:
1964年ではなく、2100のフォーマットが正しくありません。 CalendarView
にはバグがあります。 formatDateRange()
は2038年以降は機能しません。
_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);
}
}
_
_// 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年問題 で32ビットの_time_t
_秒の値に切り捨てられます。 62年強の価値。 Time
自体は1900年以降の年をint
として表す_struct tm
_を使用しているため、1960年代にはTime
になり、ヘッダーでフォーマットされます。
これは、最小日付を設定せずにDatePicker
を使用したプレーンなCalendarView
で再現できます。スピナーを使用して年を2038以降に移動し、月を変更して(ヘッダーの更新をトリガーするため)、ヘッダーの折り返しに年が1902に表示されます。
さて、最小の日付を設定するとカレンダービューが最大の日付までスクロールするのはなぜかという疑問が残ります。答えは、カレンダービューの週ListView
アダプターのようです。最小日からの週にインデックスを付けますが、最小日が変更されると、アダプターのnotifyDataSetChanged()
は呼び出されません。したがって、上記の回避策#1は、次の理由で機能します。
上記の答えは、このバグの原因について正しいものです。私のアプリケーションでは、次の回避策を使用しています。
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);
}
}
}
私が使用することになったソリューション:
private void fixBuggyCalendarview(CalendarView cv) {
long current = cv.getDate();
cv.setDate(cv.getMaxDate(), false, true);
cv.setDate(current, false, true);
}
これは、CalendarViewの最小/最大日付を設定した場合にも機能します
@laaltoに同意します。元のCalendarView
は完全に混乱しています。また、ロケール、Android 4.1.2(そうそう、シフトダウン)のフォントサイズ)、アクセシビリティ機能の欠如などの問題もありました。そこで、コピーすることにしました- ソースコード そして自分のカレンダーを実装します。たとえば、ヘッダーテキストが正しく表示されるようになりました。
/**
* 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);
}
上記の提案を実装する前に、シミュレータが今日の日時に実行されていることを確認してください。しばらく開いたままにしておくと、数日遅れることがあります。シミュレータを再起動するだけで問題を修正しました。