Uwaga: Naprawiono od Lollipop , źródło tutaj . Zautomatyzowana klasa do użytku w klientach (kompatybilna ze wszystkimi wersjami Androida) również została zaktualizowana.
TL; DR: 1-2-3 martwe łatwe kroki dla globalnego rozwiązania:
- Pobierz tę klasę.
- Zaimplementuj
OnDateSetListener
w swojej działalności (lub zmień klasę, aby odpowiadała Twoim potrzebom).
Uruchom okno dialogowe za pomocą tego kodu (w tym przykładzie używam go wewnątrz a Fragment
):
Bundle b = new Bundle();
b.putInt(DatePickerDialogFragment.YEAR, 2012);
b.putInt(DatePickerDialogFragment.MONTH, 6);
b.putInt(DatePickerDialogFragment.DATE, 17);
DialogFragment picker = new DatePickerDialogFragment();
picker.setArguments(b);
picker.show(getActivity().getSupportFragmentManager(), "frag_date_picker");
I to wszystko, czego potrzeba! Powodem, dla którego nadal utrzymuję moją odpowiedź jako „zaakceptowaną”, jest to, że nadal wolę moje rozwiązanie, ponieważ ma bardzo mały ślad w kodzie klienta, rozwiązuje podstawowy problem (wywołanie odbiornika w klasie frameworka), działa dobrze przy zmianach konfiguracji i kieruje logikę kodu do domyślnej implementacji w poprzednich wersjach Androida, które nie są nękane przez ten błąd (zobacz źródło klasy).
Oryginalna odpowiedź (zachowana ze względów historycznych i dydaktycznych):
Źródło błędu
OK, wygląda na to, że to rzeczywiście błąd i ktoś już go wypełnił. Wydanie 34833 .
Odkryłem, że problem prawdopodobnie tkwi w DatePickerDialog.java
. Gdzie brzmi:
private void tryNotifyDateSet() {
if (mCallBack != null) {
mDatePicker.clearFocus();
mCallBack.onDateSet(mDatePicker, mDatePicker.getYear(),
mDatePicker.getMonth(), mDatePicker.getDayOfMonth());
}
}
@Override
protected void onStop() {
tryNotifyDateSet();
super.onStop();
}
Myślę, że mogło to być:
@Override
protected void onStop() {
// instead of the full tryNotifyDateSet() call:
if (mCallBack != null) mDatePicker.clearFocus();
super.onStop();
}
Teraz, jeśli ktoś może mi powiedzieć, jak mogę zaproponować raport o poprawce / błędzie do Androida, z przyjemnością. W międzyczasie zasugerowałem możliwą poprawkę (prostą) jako załączoną wersję DatePickerDialog.java
w tamtym numerze.
Koncepcja, aby uniknąć błędu
Ustaw odbiornik null
w konstruktorze na, a BUTTON_POSITIVE
później utwórz własny przycisk . To wszystko, szczegóły poniżej.
Problem DatePickerDialog.java
pojawia się, ponieważ , jak widać w źródle, wywołuje zmienną globalną ( mCallBack
), która przechowuje odbiornik przekazany w konstruktorze:
/**
* @param context The context the dialog is to run in.
* @param callBack How the parent is notified that the date is set.
* @param year The initial year of the dialog.
* @param monthOfYear The initial month of the dialog.
* @param dayOfMonth The initial day of the dialog.
*/
public DatePickerDialog(Context context,
OnDateSetListener callBack,
int year,
int monthOfYear,
int dayOfMonth) {
this(context, 0, callBack, year, monthOfYear, dayOfMonth);
}
/**
* @param context The context the dialog is to run in.
* @param theme the theme to apply to this dialog
* @param callBack How the parent is notified that the date is set.
* @param year The initial year of the dialog.
* @param monthOfYear The initial month of the dialog.
* @param dayOfMonth The initial day of the dialog.
*/
public DatePickerDialog(Context context,
int theme,
OnDateSetListener callBack,
int year,
int monthOfYear,
int dayOfMonth) {
super(context, theme);
mCallBack = callBack;
// ... rest of the constructor.
}
Tak więc sztuczka polega na zapewnieniu null
słuchacza, który będzie przechowywany jako słuchacz, a następnie zwinięcie własnego zestawu przycisków (poniżej znajduje się oryginalny kod z # 1, zaktualizowany):
DatePickerDialog picker = new DatePickerDialog(
this,
null, // instead of a listener
2012, 6, 15);
picker.setCancelable(true);
picker.setCanceledOnTouchOutside(true);
picker.setButton(DialogInterface.BUTTON_POSITIVE, "OK",
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Log.d("Picker", "Correct behavior!");
}
});
picker.setButton(DialogInterface.BUTTON_NEGATIVE, "Cancel",
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Log.d("Picker", "Cancel!");
}
});
picker.show();
Teraz będzie działać z uwagi na ewentualną korektę, którą zamieściłem powyżej.
A ponieważ DatePickerDialog.java
sprawdza, czy za null
każdym razem, gdy czyta mCallback
( od czasów API 3 / 1.5 wydaje się - nie może oczywiście sprawdzić Honeycomb), nie wywoła wyjątku. Biorąc pod uwagę, że Lollipop naprawił problem, nie zamierzam się tym zajmować: po prostu użyj domyślnej implementacji (uwzględnionej w podanej przeze mnie klasie).
Na początku bałam się, że nie zadzwonię clearFocus()
, ale testowałem tutaj i linie dziennika były czyste. Więc ta linia, którą zaproponowałem, może mimo wszystko nie być konieczna, ale nie wiem.
Zgodność z poprzednimi poziomami API (edytowana)
Jak wskazałem w komentarzu poniżej, była to koncepcja i możesz pobrać klasę, której używam z mojego konta Dysku Google . Sposób, w jaki użyłem, domyślna implementacja systemu jest używana w wersjach, na które nie ma wpływu błąd.
Przyjąłem kilka założeń (nazwy przycisków itp.), Które są odpowiednie dla moich potrzeb, ponieważ chciałem zredukować standardowy kod w klasach klienta do minimum. Przykład pełnego wykorzystania:
class YourActivity extends SherlockFragmentActivity implements OnDateSetListener
// ...
Bundle b = new Bundle();
b.putInt(DatePickerDialogFragment.YEAR, 2012);
b.putInt(DatePickerDialogFragment.MONTH, 6);
b.putInt(DatePickerDialogFragment.DATE, 17);
DialogFragment picker = new DatePickerDialogFragment();
picker.setArguments(b);
picker.show(getActivity().getSupportFragmentManager(), "fragment_date_picker");