Примечание: исправлено с Lollipop , источник здесь . Обновлен автоматизированный класс для использования в клиентах (совместим со всеми версиями Android).
TL; DR: 1-2-3 простых шага для глобального решения:
- Загрузите этот класс.
- Реализуйте
OnDateSetListener
в своей деятельности (или измените класс в соответствии с вашими потребностями).
Запустите диалог с этим кодом (в этом примере я использую его внутри 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");
И это все, что нужно! Причина, по которой я все еще сохраняю свой ответ как «принятый», заключается в том, что я все еще предпочитаю свое решение, поскольку оно занимает очень мало места в клиентском коде, оно решает фундаментальную проблему (слушатель вызывается в классе фреймворка), отлично работает при изменениях конфигурации и он направляет логику кода в реализацию по умолчанию в предыдущих версиях Android, не подверженную этой ошибке (см. источник класса).
Оригинальный ответ (сохраняется по историческим и дидактическим причинам):
Источник ошибок
Хорошо, похоже, что это действительно ошибка, и кто-то еще ее уже заполнил. Выпуск 34833 .
Я обнаружил, что проблема, возможно, в DatePickerDialog.java
. Где написано:
private void tryNotifyDateSet() {
if (mCallBack != null) {
mDatePicker.clearFocus();
mCallBack.onDateSet(mDatePicker, mDatePicker.getYear(),
mDatePicker.getMonth(), mDatePicker.getDayOfMonth());
}
}
@Override
protected void onStop() {
tryNotifyDateSet();
super.onStop();
}
Я предполагаю, что это могло быть:
@Override
protected void onStop() {
// instead of the full tryNotifyDateSet() call:
if (mCallBack != null) mDatePicker.clearFocus();
super.onStop();
}
Теперь, если кто-нибудь подскажет, как я могу предложить отчет об исправлении / ошибке для Android, я был бы рад. Между тем я предложил возможное исправление (простое) в виде прикрепленной версии DatePickerDialog.java
в выпуске там.
Концепция, чтобы избежать ошибки
Установите слушателя null
в конструкторе и создайте свою собственную BUTTON_POSITIVE
кнопку позже . Вот и все, подробности ниже.
Проблема возникает потому DatePickerDialog.java
, что , как вы можете видеть в источнике, вызывает глобальную переменную ( mCallBack
), которая хранит слушателя, который был передан в конструктор:
/**
* @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.
}
Итак, хитрость заключается в том, чтобы обеспечить null
прослушивание слушателя в качестве слушателя, а затем прокрутить собственный набор кнопок (ниже приведен исходный код из # 1, обновлено):
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();
Теперь это будет работать из-за возможной коррекции, которую я выложил выше.
И так как DatePickerDialog.java
проверяет null
всякий раз, когда он читает mCallback
( со времен API 3 / 1.5 кажется - не может проверить Honeycomb, конечно), он не будет вызывать исключение. Учитывая, что Lollipop исправил проблему, я не буду ее рассматривать: просто используйте реализацию по умолчанию (описанную в классе, который я предоставил).
Сначала я боялся не звонить clearFocus()
, но я проверил здесь, и строки журнала были чистыми. Так что предложенная мною линия может даже не понадобиться, но я не знаю.
Совместимость с предыдущими уровнями API (отредактировано)
Как я указал в комментарии ниже, это была концепция, и вы можете загрузить используемый мной класс из моей учетной записи Google Drive . В способе, который я использовал, системная реализация по умолчанию используется в версиях, не затронутых этой ошибкой.
Я сделал несколько предположений (названий кнопок и т. Д.), Которые подходят для моих нужд, потому что я хотел свести к минимуму шаблонный код в клиентских классах. Пример полного использования:
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");