--- Примечание для модераторов: Сегодня (15 июля), я заметил , что кто - то уже сталкивался с этой проблемой здесь . Но я не уверен, уместно ли закрывать это как дубликат, так как я думаю, что предоставил намного лучшее объяснение проблемы. Я не уверен, стоит ли мне редактировать другой вопрос и вставлять туда этот контент, но мне неудобно слишком сильно менять чужой вопрос. ---
У меня здесь что-то странное .
Я не думаю, что проблема зависит от того, против какого SDK вы строите. Версия ОС устройства имеет значение.
Проблема № 1: несоответствие по умолчанию
DatePickerDialog
был изменен (?) в Jelly Bean и теперь предоставляет только кнопку Готово . Предыдущие версии включали кнопку Отмена , и это может повлиять на пользовательский опыт (несоответствие, мышечная память из предыдущих версий Android).
Репликация: создание базового проекта. Поместите это вonCreate
:
DatePickerDialog picker = new DatePickerDialog(
this,
new OnDateSetListener() {
@Override
public void onDateSet(DatePicker v, int y, int m, int d) {
Log.d("Picker", "Set!");
}
},
2012, 6, 15);
picker.show();
Ожидаемый: Отменить кнопку появится в диалоговом окне.
Ток: Отмена кнопки не отображается.
Скриншоты: 4.0.3 (ОК) и 4.1.1 (возможно, неправильно?).
Проблема № 2: неправильное поведение
Диалог вызывает того слушателя, которому он действительно должен позвонить, а затем всегда вызывает OnDateSetListener
слушателя. Отмена по-прежнему вызывает метод set, а установка его вызывает метод дважды.
Репликация: используйте код № 1, но добавьте код ниже (вы увидите, что это решает № 1, но только визуально / пользовательский интерфейс):
picker.setButton(DialogInterface.BUTTON_NEGATIVE, "Cancel",
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Log.d("Picker", "Cancel!");
}
});
Ожидаемое:
- Нажатие клавиши BACK или нажатие за пределами диалогового окна ничего не должно делать .
- Нажатие «Отмена» должно напечатать Picker Cancel! ,
- Нажатие «Set» должно напечатать Picker Set! ,
Текущий:
- Нажатие клавиши НАЗАД или нажатие за пределами диалогового окна выводит на печать комплект выбора! ,
- Нажатие «Отмена» печатает Пикер Отмена! а затем комплект поставки! ,
- Нажатие «Set» печатает Picker Set! а затем комплект поставки! ,
Строки журнала, показывающие поведение:
07-15 12:00:13.415: D/Picker(21000): Set!
07-15 12:00:24.860: D/Picker(21000): Cancel!
07-15 12:00:24.876: D/Picker(21000): Set!
07-15 12:00:33.696: D/Picker(21000): Set!
07-15 12:00:33.719: D/Picker(21000): Set!
Другие заметки и комментарии
- Оборачивать это вокруг
DatePickerFragment
не имеет значения. Я упростил проблему для вас, но я проверил это.
Ответы:
Примечание: исправлено с Lollipop , источник здесь . Обновлен автоматизированный класс для использования в клиентах (совместим со всеми версиями Android).
TL; DR: 1-2-3 простых шага для глобального решения:
OnDateSetListener
в своей деятельности (или измените класс в соответствии с вашими потребностями).Запустите диалог с этим кодом (в этом примере я использую его внутри a
Fragment
):И это все, что нужно! Причина, по которой я все еще сохраняю свой ответ как «принятый», заключается в том, что я все еще предпочитаю свое решение, поскольку оно занимает очень мало места в клиентском коде, оно решает фундаментальную проблему (слушатель вызывается в классе фреймворка), отлично работает при изменениях конфигурации и он направляет логику кода в реализацию по умолчанию в предыдущих версиях Android, не подверженную этой ошибке (см. источник класса).
Оригинальный ответ (сохраняется по историческим и дидактическим причинам):
Источник ошибок
Хорошо, похоже, что это действительно ошибка, и кто-то еще ее уже заполнил. Выпуск 34833 .
Я обнаружил, что проблема, возможно, в
DatePickerDialog.java
. Где написано:Я предполагаю, что это могло быть:
Теперь, если кто-нибудь подскажет, как я могу предложить отчет об исправлении / ошибке для Android, я был бы рад. Между тем я предложил возможное исправление (простое) в виде прикрепленной версии
DatePickerDialog.java
в выпуске там.Концепция, чтобы избежать ошибки
Установите слушателя
null
в конструкторе и создайте свою собственнуюBUTTON_POSITIVE
кнопку позже . Вот и все, подробности ниже.Проблема возникает потому
DatePickerDialog.java
, что , как вы можете видеть в источнике, вызывает глобальную переменную (mCallBack
), которая хранит слушателя, который был передан в конструктор:Итак, хитрость заключается в том, чтобы обеспечить
null
прослушивание слушателя в качестве слушателя, а затем прокрутить собственный набор кнопок (ниже приведен исходный код из # 1, обновлено):Теперь это будет работать из-за возможной коррекции, которую я выложил выше.
И так как
DatePickerDialog.java
проверяетnull
всякий раз, когда он читаетmCallback
( со времен API 3 / 1.5 кажется - не может проверить Honeycomb, конечно), он не будет вызывать исключение. Учитывая, что Lollipop исправил проблему, я не буду ее рассматривать: просто используйте реализацию по умолчанию (описанную в классе, который я предоставил).Сначала я боялся не звонить
clearFocus()
, но я проверил здесь, и строки журнала были чистыми. Так что предложенная мною линия может даже не понадобиться, но я не знаю.Совместимость с предыдущими уровнями API (отредактировано)
Как я указал в комментарии ниже, это была концепция, и вы можете загрузить используемый мной класс из моей учетной записи Google Drive . В способе, который я использовал, системная реализация по умолчанию используется в версиях, не затронутых этой ошибкой.
Я сделал несколько предположений (названий кнопок и т. Д.), Которые подходят для моих нужд, потому что я хотел свести к минимуму шаблонный код в клиентских классах. Пример полного использования:
источник
Я добавлю свой собственный рифф к решению, опубликованному Дэвидом Чезарино, если вы не используете Фрагменты и хотите простой способ исправить это во всех версиях (с 2.1 по 4.1):
источник
android.R.string.ok
иandroid.R.string.cancel
поля, а не собственные пользовательские. И спасибо за ответ.dateToShow
? Другой нуль там на самом деле «исправить», так что должно быть там. На какой версии вы находитесь?import
у вас былоField
? У меня есть 6 вариантов, и ни один из них не работал.Пока ошибка не будет исправлена, я предлагаю не использовать DatePickerDialog или TimePickerDialog. Используйте пользовательский AlertDialog с виджетом TimePicker / DatePicker;
Изменить TimePickerDialog с помощью;
Изменить DatePickerDialog с помощью;
источник
Один для TimePicker, основанный на решении Дэвида Чезарино, «TL; DR: 1-2-3 простых шага для глобального решения»
TimePickerDialog не предоставляет такую функциональность, как DatePickerDialog.getDatePicker. Итак, слушатель OnTimeSetListener должен быть предоставлен. Просто для того, чтобы сохранить сходство с обходным решением DatePicker, я сохранил старую концепцию mListener. Вы можете изменить его, если вам нужно.
Calling и Listener - это то же самое, что и оригинальное решение. Просто включите
расширить родительский класс,
Воплощать в жизнь
пример вызова
(Обновлено для обработки отмены)
источник
В случае, если кто-то хочет быстро обойти, вот код, который я использовал:
}
Где layout.date_picker_view - это простой ресурс макета с DatePicker, поскольку он является единственным элементом:
Вот полный учебник, если вы заинтересованы.
источник
Моё простое решение. Если вы хотите снова запустить его, просто запустите «resetFired» (скажем, при повторном открытии диалога).
источник
onDateSet
он будет вызван один раз, когда диалог закрывается любым способом, и если это означает, что он должен был установить время, то он будет запущен снова, поэтому нам нужно перехватить второй вызов, а не первый как вы сделали,isJellyBeanOrAbove()
версий ниже, чем Jellybean, нет ошибки, о которой весь этот вопрос, и, учитывая, что мы хотим перехватить второй вызов, код не запустится, если мы не сделаем эту проверку, поверьте мне, я попробовал код на эмуляторах и реальных устройствах (с различными версиями) несколько раз, и это работает как очарованиеСогласно блестящему ответу Анкура Чаудхари на похожую
TimePickerDialog
проблему, если мы проверим внутри, являетсяonDateSet
ли данное представлениеisShown()
или нет, это решит всю проблему с минимальными усилиями, без необходимости расширять средство выбора или проверять какие-то отвратительные флаги, идущие вокруг кода. или даже проверяя версию ОС, просто сделайте следующее:и, конечно, то же самое можно сделать для
onTimeSet
ответа Анкураисточник
Я справился с этой ситуацией, используя флаг и переопределяя методы onCancel и onDismiss.
OnCancel вызывается только тогда, когда пользователь касается вне диалогового окна или кнопки возврата. onDismiss всегда вызывается
Установка флага в методе onCancel может помочь отфильтровать в методе onDismiss намерение пользователя: отменить действие или выполненное действие. Ниже приведен код, который показывает идею.
источник
Существует очень простой обходной путь, если ваше приложение не использует панель действий. Кстати, обратите внимание, что некоторые приложения используют эту функцию для работы, потому что отмена выбора даты имеет особое значение (например, оно очищает поле даты до пустой строки, что для некоторых приложений является допустимым и значимым типом ввода ) и использование логических флагов для предотвращения повторной установки даты на ОК не поможет вам в этом случае.
Число рейнольдса фактическое исправление, вам не нужно создавать новые кнопки или свой собственный диалог. Дело в том, чтобы быть совместимым как с более старыми версиями Android, так и с ошибками (4. ) и любыми будущими, хотя в последнем, конечно, нельзя быть уверенным. Обратите внимание, что в Android 2. onStop () для android.app.Dialog вообще ничего не делает, а в 4. * он выполняет mActionBar.setShowHideAnimationEnabled (false), что важно, только если в вашем приложении есть панель действий. Функция onStop () в DatePickerDialog, которая наследуется от Dialog, вносит только mDatePicker.clearFocus () (по состоянию на последнее исправление для исходных кодов Android 4.3), что не кажется существенным.
Поэтому замена onStop () методом, который ничего не делает, во многих случаях должна исправить ваше приложение и гарантировать, что оно останется таковым в обозримом будущем. Таким образом, просто расширьте класс DatePickerDialog своим собственным и переопределите onStop () с помощью фиктивного метода. Вы также должны будете предоставить один или два конструктора в соответствии с вашими требованиями. Также обратите внимание, что не следует пытаться переусердствовать с этим исправлением, например, пытаясь что-то сделать с панелью активности напрямую, так как это ограничит вашу совместимость только с последними версиями Android. Также обратите внимание, что было бы неплохо иметь возможность вызывать super для onStop () DatePicker, потому что ошибка есть только в onStop () в самом DatePickerDialog, но не в суперклассе DatePickerDialog. Однако это потребует от вас вызова super.super.onStop () из вашего пользовательского класса, какая Java не позволит вам сделать, поскольку это идет вразрез с философией инкапсуляции :) Ниже приведен мой маленький класс, который я использовал для проверки DatePickerDialog. Я надеюсь, что этот комментарий будет полезен для кого-то. Войтек Ярош
}
источник
Попробуйте следующие концепции.
метод onDateSet () вызывает дважды (если вы проверяете в emulator.it вызовы дважды. Если вы используете реальное устройство, то оно будет вызывать правильно один раз. Если вы используете эмулятор, тогда используйте счетчик. Если вы работаете в реальном устройстве, тогда игнорировать переменную счетчика. Для реального устройства это работает для меня.)
когда пользователь нажимает кнопку в DatePickerDialog.
для этого вы должны сохранить значение счетчика и ничего не делать, когда метод вызывает первый раз, и выполнять операцию, когда метод вызывает второй раз.
Смотрите приведенные ниже фрагменты кода
Для отмены выбора даты дилалог его работает для меня. Для эмулятора его не работает
Это работает для меня для реального устройства. Но для эмулятора это не работает правильно. Я думаю, что это ошибка эмулятора Android.
источник
Простое решение будет использовать логическое значение, чтобы пропустить второй запуск
источник
onStop
вызов метода , когда он не должен ... он стреляет дважды является следствием ошибки.onDateSet
. Таким образом, сломан.onDateSet
будет вызываться один раз, но при выборе «сделано» или «установить» он будет вызван дважды. Поэтому нам нужно пропустить только первый, поэтому, если он вызывается дважды, то и только тогда у нас будет правильная датаВы можете переопределить onCancel () и использовать setOnDismissListener () для обнаружения негативных действий пользователя. А с DatePickerDialog.BUTTON_POSITIVE вы знаете, что пользователь хочет установить новую дату.
затем проверьте для setDate:
источник
Вот мой класс обхода для DatePickerDialog на кнопке отмены так же как отмене его кнопкой возврата. Копирование и использование в стиле DatePickerDialog (так как слушатель с состоянием, мы должны создать новый экземпляр при использовании, в противном случае требуется больше кода, чтобы он работал)
Использование:
Класс:
}
источник
Я использую даты, время и число. Сборщики чисел вызывают onValueChanged всякий раз, когда пользователь выбирает число, перед тем как сборщик отклоняется, поэтому у меня уже была такая структура, чтобы делать что-то со значением только тогда, когда сборщик отклонен:
Я расширил это, чтобы установить пользовательские onClickListeners для моих кнопок с аргументом, чтобы увидеть, какая кнопка была нажата. Теперь я могу проверить, какая кнопка была нажата, прежде чем установить окончательное значение:
А затем я расширил это для работы с типами даты и времени для средств выбора даты и времени, а также с типом int для средств выбора чисел.
Я опубликовал это, потому что я думал, что это было проще, чем некоторые из решений выше, но теперь, когда я включил весь код, я думаю, что это не намного проще! Но это хорошо вписывается в структуру, которую я уже имел.
Обновление для Lollipop: По-видимому, эта ошибка возникает не на всех устройствах Android 4.1-4.4, потому что я получил несколько отчетов от пользователей, чьи средства выбора даты и времени не вызывали обратные вызовы onDateSet и onTimeSet. И ошибка была официально исправлена в Android 5.0. Мой подход работал только на устройствах, где присутствует ошибка, потому что мои пользовательские кнопки не вызывали обработчик onClick диалога, который является единственным местом, где onDateSet и onTimeSet вызываются, когда ошибки нет. Я обновил мой код выше, чтобы вызывать диалог onClick, так что теперь он работает независимо от того, присутствует ли ошибка.
источник
Мне понравился ответ Дэвида Чезарино выше, но я хотел что-то, что было бы заменой неработающего диалога и работало бы над любым диалогом, который мог бы отсутствовать, отмена / неправильное поведение отмены. Вот производные классы для DatePickerDialog / TimePickerDialog, которые должны работать как капли замен. Это не пользовательские представления. Он использует системный диалог, но просто изменяет поведение кнопки отмены / возврата, чтобы работать как положено.
Это должно работать на уровне API 3 и выше. Итак, в основном любая версия Android (я тестировал ее специально на желе и леденце).
DatePickerDialog:
TimePickerDialog:
источник
Моя рабочая версия с ClearButton с использованием лямбда-выражений:
источник
Для TimePickerDialog обходной путь может быть следующим:
Я делегирую все события оболочке KitKatSetTimeListener и запускаю только исходный OnTimeSetListener только в случае нажатия BUTTON_POSITIVE.
источник
После тестирования некоторых предложений, размещенных здесь, я лично считаю, что это решение является наиболее простым. Я передаю «null» как мой слушатель в конструкторе DatePickerDialog, а затем, когда я нажимаю кнопку «ОК», я вызываю мой onDateSearchSetListener:
источник
Я знаю, что этот пост был здесь почти год, но я решил опубликовать свои выводы. Вы могли бы все еще держать слушателя (вместо того, чтобы установить его на отключение звука) и все еще иметь эту работу как ожидалось. Ключ заключается в том, чтобы неявно установить «ОК» или (и) кнопки «отмена». Я проверил это, и это работает с благодарностью для меня. Слушателя не увольняют дважды.
Посмотрите на этот пример,
источник
timePickerListener
по-прежнему вызывается независимо от того, что вы делаете в своем диалоге. Мне даже не нужно проверять, чтобы знать это, вам просто нужно взглянуть на источники : если вы не установите егоnull
,tryNotifyTimeSet()
вызовет слушателяonTimeSet()
как в его, такonClick()
и вonStop()
.