2013-05-21 17 views
16

CalendarViewDatePickerが1964年10月に移動し、デフォルト以外の日付が設定されている場合、デバッグします。これは、少なくともAPI17 Nexus7エミュレータを再現しますが、日付ピッカーでプラットフォームスタイルにスピナーとカレンダービューの両方が含まれている他のデバイスでこの問題に関するレポートがあります。ここでDatePickerでMinDateを設定すると、CalendarViewが1964に移動します

は、問題を実証コードスニペットです:

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ユーザーが経験や他の洞察力を持っている場合は、共有してください。

関連:

+0

私の最初の本能は、廃止予定のメソッドを使用しているときにバグの挙動が現れた場合は、廃止予定のメソッドなしでやり直してください。カレンダーを使用して日付を設定しても同じ問題が発生しますか? – Jules

+0

これは非推奨です。 'setMinDate()'は 'long'ミリ秒の値をとり、' DatePicker'に渡されたことを確認しました。単にこの廃止予定の 'Date'コンストラクタを使用して、この問題を示すサンプルを調理してください。 – laalto

+1

あなたが固定されたminDateで暮らすことができれば、XMLを介して設定することは実際には機能します。それ以外の場合は、CalendarView.javaをAOSPから取り出すことで、より洗練された修正を利用できます。 – lilbyrdie

答えて

19

TL; DR

それは1964ではないが、2100はひどくフォーマット。 CalendarViewにバグがあります。 formatDateRange()は動作しません過去2038

回避策#1、私は実際に分析

CalendarView

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

を使用回避策#2は、月/年の名でを生成するためのandroid.text.format.DateUtils.formatDateRange()を使用しています

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

ヘッダーカレンダービューは、月の番号が変更されたときにのみヘッダーを更新します。たとえば、月の数字が同じで年が変更された場合、ヘッダーは更新されません。

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

formatDateRange()は、具体的にはset()メソッドを使用してミリ秒を内部のカレンダー表現として使用します(android.text.format.Time)。基本的なネイティブコードはチェックしませんでしたが、渡されたミリ秒の値が、​​の32ビットのtime_t秒の値に切り捨てられ、62年を少し超える値にラップされます。 Time自体は、1900年からintとして年を表すstruct tmを使用しているため、1960年にはTimeとなり、ヘッダでフォーマットされます。

これは、CalendarViewと平文で再現することができます。スピナーを使用して年を2038年以降に移動し、ヘッダーの更新をトリガーするために月を変更すると、1902年にヘッダーラップされた年が表示されます。

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

  • 月を1か月以上変更すると、月のヘッダーが変更されます。
  • 週の最初の曜日を変更すると、週のリストビューにアダプタのデータが変更されたことが通知されます。

+0

1月を1月に設定すると、2月が返されます。 –

+1

@deepakgoelそれは別の問題です。 1月は0、2月は1などです。ゼロベースのインデックス作成。 – laalto

+0

どうすればこの問題を解決できるのでしょうか。私はあなたのコードを試しました。この場合、スピナービューは1月に月が与えられ、カレンダービューには2月が与えられます –

12

上記の回答は、このバグの原因に関するものです。私のアプリケーションのために、私はこの回避策を使用しています:

日付ピッカーの日付や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); 
     } 
    } 
} 
+0

これは完全に動作します..ありがとうございます。 – SSS

+0

これは、1年前に27 februariであり、それが最低の日付よりも低く、それも設定された日付ではないため、29 februari 2016で私のアプリケーションがクラッシュすることになります。 – Roel

+0

実際、月の選択は恣意的ではありません。 2月29日の問題を防ぐには、24ではなく48にする必要があります。 –

2

私が使用して終了ソリューション:あなたはCalendarView

0

ための最小/最大日付を設定しているとき

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

また、これは動作します@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); 
} 
0

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

関連する問題