17

デフォルト以外の最小日付が設定されている場合、 が 1964 年 10 月に移動するCalendarView問題をデバッグしています。DatePickerこれは、少なくとも 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();

スクリーンショット:

日付ピッカーの 1964 年 10 月のカレンダー ビュー

もちろん、スピナーとカレンダー ビューが同じ日付を表示することが期待されます。

私はこれをデバッグし続けますが、SO ユーザーが経験や他の洞察を持っている場合は、共有してください。

関連している:

4

5 に答える 5

21

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

分析

CalendarViewandroid.text.format.DateUtils.formatDateRange()ヘッダーで月/年の名前を生成するために使用します。カレンダー ビューは、月番号が変更されたときにのみヘッダーを更新します。たとえば、月番号は同じで年が変わる場合、ヘッダーは更新されません。

レイアウト フェーズのどこかで、リストが最後までスクロールされたかのように、基になるカレンダー ビューListViewが呼び出されます。OnScrollListener月番号が変更された場合、ヘッダーは上記のテスト コードで更新され、 に渡されるミリ秒値はformatDateRange()21004131043200000後半のどこかであり、2100 は のデフォルトの最大値ですDatePicker

formatDateRange()次にandroid.text.format.Time、内部カレンダー表現として使用し、具体的にはそのset()メソッドを使用してミリ秒を設定します。私は基礎となるネイティブ コードをチェックしませんでしたが、私の知識に基づく推測では、渡されたミリ秒の値time_tは固有の2038 年の問題で 32 ビットの秒の値に切り捨てられ、62 年を少し超える値にラップされます。Timeそれ自体は、1900 年以降の年struct tmを表すを使用しているため、ヘッダーでフォーマットされる 1960 年代になります。intTime

こちらは分日設定なしで無地DatePickerで再現できます。CalendarViewスピナーを使用して年を 2038 以降に移動し、月を変更して (ヘッダーの更新をトリガーする)、ヘッダー ラップの年が 1902 に表示されます。

ここで、最小の日付を設定すると、カレンダー ビューが最大の日付までスクロールする理由が疑問のままです。答えは、カレンダー ビューの週ListViewアダプターのようです。最小日付から週のインデックスを作成しますが、最小日付が変更されると、アダプターnotifyDataSetChanged()は呼び出されません。したがって、上記の回避策 #1 は次の理由で機能します。

  • 日付を 1 か月以上変更すると、月のヘッダーが変更されます。
  • 週の最初の曜日を変更すると、週のリストビューはアダプタのデータが変更されたことを通知します。
于 2013-05-21T13:58:15.213 に答える
13

上記の回答は、このバグの原因について正しいです。私のアプリケーションでは、次の回避策を使用しています。

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);
        }
    }
}
于 2013-10-01T20:50:55.573 に答える
6

私が最終的に使用したソリューション:

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

これは、CalendarView の最小/最大日付を設定した場合にも機能します

于 2015-06-22T04:41:54.000 に答える
0

上記の提案を実装する前に、シミュレーターが今日の日時で実行されていることを確認してください。しばらく開いたままにすると、数日遅れることがあります。シミュレーターを再起動するだけで問題を解決しました。

于 2016-04-26T23:44:03.377 に答える