Jelly Bean DatePickerDialog - есть ли способ отменить?


148

--- Примечание для модераторов: Сегодня (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не имеет значения. Я упростил проблему для вас, но я проверил это.

Поздравляю, вы, кажется, нашли ошибку в Android. Вы можете сообщить об этом здесь .
Майкл Хэмптон

1
Очень хорошо написано сообщение об ошибке. Я могу понять это полностью без необходимости запускать тестовый код.
Чеок Ян Ченг

6
Я призываю всех проголосовать за этот вопрос! Выпуск 34833
Брэдли

Не могли бы вы просто переопределить функцию кнопки, чтобы она действовала так, как будто она была отклонена из-за прикосновения за пределами диалога?
Картик Балакришнан,

2
Ошибка все еще открыта через 2 года ... невероятно.
erdomester

Ответы:


115

Примечание: исправлено с Lollipop , источник здесь . Обновлен автоматизированный класс для использования в клиентах (совместим со всеми версиями Android).

TL; DR: 1-2-3 простых шага для глобального решения:

  1. Загрузите этот класс.
  2. Реализуйте OnDateSetListenerв своей деятельности (или измените класс в соответствии с вашими потребностями).
  3. Запустите диалог с этим кодом (в этом примере я использую его внутри 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");

11
Отличная работа по исследованию! Грустно, что это было необходимо, но, тем не менее, здорово.
CommonsWare

1
Очень хорошо! Я бился головой о стену по этому вопросу, который обновлял мои текстовые поля и вызывал / не вызывал правильные обратные вызовы. Спасибо! Интересно, является ли предложенный обходной путь «будущим» или, как вы думаете, это вызовет проблемы, когда ошибка будет исправлена?
продолжительность

1
@RomainGuidoux смотрите обновленный ответ в конце. Класс в ссылке имеет умение вызывать этот метод только в Jelly Bean. Для всего, что ниже, оно будет обходить это и использовать системную реализацию по умолчанию, перенаправляя системный вызов ondateset на вашу деятельность. Просто в jelly bean перед маршрутизацией обратного вызова требуются дополнительные меры (чтобы избежать ошибки), и это требует вызова этого метода honeycomb +. Но опять же только в JB.
davidcsb

1
Потрясающая работа здесь. У меня нет слов о том, как нелепа эта проблема.
Билл Филлипс

5
Как насчет TimePickerDialog? Похоже, что TimePickerDialog не имеет getTimePicker ()
lokoko

16

Я добавлю свой собственный рифф к решению, опубликованному Дэвидом Чезарино, если вы не используете Фрагменты и хотите простой способ исправить это во всех версиях (с 2.1 по 4.1):

public class FixedDatePickerDialog extends DatePickerDialog {
  //I use a Calendar object to initialize it, but you can revert to Y,M,D easily
  public FixedDatePickerDialog(Calendar dateToShow, Context context, OnDateSetListener callBack) {
    super(context, null, dateToShow.get(YEAR), dateToShow.get(MONTH), dateToShow.get(DAY_OF_MONTH));
    initializePicker(callBack);
  }

  public FixedDatePickerDialog(Calendar dateToShow, Context context, int theme,
    OnDateSetListener callBack) {
    super(context, theme, null, dateToShow.get(YEAR), dateToShow.get(MONTH), dateToShow.get(DAY_OF_MONTH));
    initializePicker(callBack);
  }

  private void initializePicker(final OnDateSetListener callback) {
    try {
      //If you're only using Honeycomb+ then you can just call getDatePicker() instead of using reflection
      Field pickerField = DatePickerDialog.class.getDeclaredField("mDatePicker");
      pickerField.setAccessible(true);
      final DatePicker picker = (DatePicker) pickerField.get(this);
      this.setCancelable(true);
      this.setButton(DialogInterface.BUTTON_NEGATIVE, getContext().getText(android.R.string.cancel), (OnClickListener) null);
      this.setButton(DialogInterface.BUTTON_POSITIVE, getContext().getText(android.R.string.ok),
          new DialogInterface.OnClickListener() {
              @Override
              public void onClick(DialogInterface dialog, int which) {
                picker.clearFocus(); //Focus must be cleared so the value change listener is called
                callback.onDateSet(picker, picker.getYear(), picker.getMonth(), picker.getDayOfMonth());
              }
          });
    } catch (Exception e) { /* Reflection probably failed*/ }
  }
}

Просто запомните: если вы жестко соединяете названия кнопок, чтобы ок и отменить, вероятно, лучше использовать стандарт android.R.string.okи android.R.string.cancelполя, а не собственные пользовательские. И спасибо за ответ.
12:30

Ах, хорошо, я не заметил, что они предоставили текст ОК и Отмена. Спасибо!
Демон

получение исключения nullpointer в super (context, theme, null, dateToShow.get (YEAR), dateToShow.get (MONTH), dateToShow.get (DAY_OF_MONTH));
Кишор

Errr ... вы передаете ноль dateToShow? Другой нуль там на самом деле «исправить», так что должно быть там. На какой версии вы находитесь?
Дмон

пожалуйста, дайте мне знать, что importу вас было Field? У меня есть 6 вариантов, и ни один из них не работал.
Адиль Малик

8

Пока ошибка не будет исправлена, я предлагаю не использовать DatePickerDialog или TimePickerDialog. Используйте пользовательский AlertDialog с виджетом TimePicker / DatePicker;

Изменить TimePickerDialog с помощью;

    final TimePicker timePicker = new TimePicker(this);
    timePicker.setIs24HourView(true);
    timePicker.setCurrentHour(20);
    timePicker.setCurrentMinute(15);

    new AlertDialog.Builder(this)
            .setTitle("Test")
            .setPositiveButton(android.R.string.ok, new OnClickListener() {

                @Override
                public void onClick(DialogInterface dialog, int which) {
                    Log.d("Picker", timePicker.getCurrentHour() + ":"
                            + timePicker.getCurrentMinute());
                }
            })
            .setNegativeButton(android.R.string.cancel,
                    new OnClickListener() {

                        @Override
                        public void onClick(DialogInterface dialog,
                                int which) {
                            Log.d("Picker", "Cancelled!");
                        }
                    }).setView(timePicker).show();

Изменить DatePickerDialog с помощью;

    final DatePicker datePicker = new DatePicker(this);
    datePicker.init(2012, 10, 5, null);
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
        datePicker.setCalendarViewShown(false);
    }

    new AlertDialog.Builder(this)
            .setTitle("Test")
            .setPositiveButton(android.R.string.ok, new OnClickListener() {

                @Override
                public void onClick(DialogInterface dialog, int which) {
                    Log.d("Picker", datePicker.getYear() + " "
                            + (datePicker.getMonth() + 1) + " "
                            + datePicker.getDayOfMonth());
                }
            })
            .setNegativeButton(android.R.string.cancel,
                    new OnClickListener() {

                        @Override
                        public void onClick(DialogInterface dialog,
                                int which) {
                            Log.d("Picker", "Cancelled!");
                        }
                    }).setView(datePicker).show();

4

Один для TimePicker, основанный на решении Дэвида Чезарино, «TL; DR: 1-2-3 простых шага для глобального решения»

TimePickerDialog не предоставляет такую ​​функциональность, как DatePickerDialog.getDatePicker. Итак, слушатель OnTimeSetListener должен быть предоставлен. Просто для того, чтобы сохранить сходство с обходным решением DatePicker, я сохранил старую концепцию mListener. Вы можете изменить его, если вам нужно.

Calling и Listener - это то же самое, что и оригинальное решение. Просто включите

import android.app.TimePickerDialog;
import android.app.TimePickerDialog.OnTimeSetListener;

расширить родительский класс,

... implements OnDateSetListener, OnTimeSetListener

Воплощать в жизнь

 @Override
 public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
 ...
 }

пример вызова

    Calendar cal = Calendar.getInstance();
    int hour = cal.get(Calendar.HOUR_OF_DAY);
    int minute = cal.get(Calendar.MINUTE);


    Bundle b = new Bundle();
    b.putInt(TimePickerDialogFragment.HOUR, hour);
    b.putInt(TimePickerDialogFragment.MINUTE, minute);

    DialogFragment picker = new TimePickerDialogFragment();
    picker.setArguments(b);
    picker.show(getSupportFragmentManager(), "frag_time_picker");

(Обновлено для обработки отмены)

public class TimePickerDialogFragment extends DialogFragment {

    public static final String HOUR = "Hour";
    public static final String MINUTE = "Minute";

    private boolean isCancelled = false; //Added to handle cancel
    private TimePickerDialog.OnTimeSetListener mListener;

    //Added to handle parent listener
    private TimePickerDialog.OnTimeSetListener mTimeSetListener = new TimePickerDialog.OnTimeSetListener() {
        public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
            if (!isCancelled)
            {
                mListener.onTimeSet(view,hourOfDay,minute);
            }
        }
    };
    //
    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        this.mListener = (TimePickerDialog.OnTimeSetListener) activity;
    }

    @Override
    public void onDetach() {
        this.mListener = null;
        super.onDetach();
    }

    @TargetApi(11)
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        Bundle b = getArguments();
        int h = b.getInt(HOUR);
        int m = b.getInt(MINUTE);

        final TimePickerDialog picker = new TimePickerDialog(getActivity(), getConstructorListener(), h, m,DateFormat.is24HourFormat(getActivity()));

        //final TimePicker timePicker = new TimePicker(getBaseContext());
        if (hasJellyBeanAndAbove()) {
            picker.setButton(DialogInterface.BUTTON_POSITIVE,
                    getActivity().getString(android.R.string.ok),
                    new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            isCancelled = false; //Cancel flag, used in mTimeSetListener
                        }
                    });
            picker.setButton(DialogInterface.BUTTON_NEGATIVE,
                    getActivity().getString(android.R.string.cancel),
                    new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            isCancelled = true; //Cancel flag, used in mTimeSetListener
                        }
                    });
        }
        return picker;
    }
    private boolean hasJellyBeanAndAbove() {
        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN;
    }

    private TimePickerDialog.OnTimeSetListener getConstructorListener() {
        return hasJellyBeanAndAbove() ? mTimeSetListener : mListener; //instead of null, mTimeSetListener is returned.
    }
}

Это не работает для меня при попытке его на s3 API 18
AbdelHady

3

В случае, если кто-то хочет быстро обойти, вот код, который я использовал:

public void showCustomDatePicker () {

final DatePicker mDatePicker = (DatePicker) getLayoutInflater().
        inflate(R.layout.date_picker_view, null);
//Set an initial date for the picker
final Calendar c = Calendar.getInstance();
int year = c.get(Calendar.YEAR);
int month = c.get(Calendar.MONTH);
int day = c.get(Calendar.DAY_OF_MONTH);
//Set the date now
mDatePicker.updateDate(year, month, day);

//create the dialog
AlertDialog.Builder mBuilder = new Builder(this);
//set the title
mBuilder.setTitle(getString(R.string.date_picker_title))
    //set our date picker
    .setView(mDatePicker)
    //set the buttons 
.setPositiveButton(android.R.string.ok, new OnClickListener() {

    @Override
    public void onClick(DialogInterface dialog, int which) {
        //whatever method you choose to handle the date changes
            //the important thing to know is how to retrieve the data from the picker
        handleOnDateSet(mDatePicker.getYear(), 
                mDatePicker.getMonth(), 
                mDatePicker.getDayOfMonth());
    }
})
.setNegativeButton(android.R.string.cancel, new OnClickListener() {

    @Override
    public void onClick(DialogInterface dialog, int which) {
        dialog.dismiss();
    }
})
//create the dialog and show it.
.create().show();

}

Где layout.date_picker_view - это простой ресурс макета с DatePicker, поскольку он является единственным элементом:

<!xml version="1.0" encoding="utf-8">
<DatePicker xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/date_picker"
android:layout_width="fill_parent"   
android:spinnersShown="true" 
android:calendarViewShown="false"
android:layout_height="fill_parent"/>

Вот полный учебник, если вы заинтересованы.


Это сработало для меня чудесно. Легко понять, легко реализовать! Протестировано на 4.4
erdomester

3

Моё простое решение. Если вы хотите снова запустить его, просто запустите «resetFired» (скажем, при повторном открытии диалога).

private class FixedDatePickerDialogListener implements DatePickerDialog.OnDateSetListener{
    private boolean fired;

    public void resetFired(){
        fired = false;
    }

    public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) {
        if (fired) {
            Log.i("DatePicker", "Double fire occurred.");
            return;//ignore and return.
        } 
        //put your code here to handle onDateSet
        fired = true;//first time fired 
    }
}

@AbdelHady Ваш код редактировался неправильно, но я отредактировал его, чтобы сделать его более понятным. Нет необходимости добавлять метод isJellyBeanOrAbove (), нет никаких преимуществ, просто добавляются ненужные сложности. Вызов resetFired стоит дешево.
Даниэль Райан

код здесь неправильный, потому что onDateSetон будет вызван один раз, когда диалог закрывается любым способом, и если это означает, что он должен был установить время, то он будет запущен снова, поэтому нам нужно перехватить второй вызов, а не первый как вы сделали,
AbdelHady

Что касается isJellyBeanOrAbove()версий ниже, чем Jellybean, нет ошибки, о которой весь этот вопрос, и, учитывая, что мы хотим перехватить второй вызов, код не запустится, если мы не сделаем эту проверку, поверьте мне, я попробовал код на эмуляторах и реальных устройствах (с различными версиями) несколько раз, и это работает как очарование
AbdelHady

Я не уверен, стоит ли это понижать голос. Я опубликовал это 2 года назад, это было в нашем коммерческом приложении с тех пор, работает просто отлично. Об ошибках не сообщали наши команды QA или наши тысячи пользователей. Это должен быть простой ответ, который люди могут расширить. Вызовите «resetFired», когда хотите снова запустить его.
Даниэль Райан

В нашем приложении isJellyBeanOrAbove не требуется. Приложение будет отлично работать во всех версиях, если вы вызовете «resetFired» в нужных областях.
Даниэль Райан

3

Согласно блестящему ответу Анкура Чаудхари на похожую TimePickerDialogпроблему, если мы проверим внутри, является onDateSetли данное представление isShown()или нет, это решит всю проблему с минимальными усилиями, без необходимости расширять средство выбора или проверять какие-то отвратительные флаги, идущие вокруг кода. или даже проверяя версию ОС, просто сделайте следующее:

public void onDateSet(DatePicker view, int year, int month, int day) {
    if (view.isShown()) {
        // read the date here :)
    }
}

и, конечно, то же самое можно сделать для onTimeSetответа Анкура


1
Лучший ответ из всех остальных!
hiew1

2

Я справился с этой ситуацией, используя флаг и переопределяя методы onCancel и onDismiss.

OnCancel вызывается только тогда, когда пользователь касается вне диалогового окна или кнопки возврата. onDismiss всегда вызывается

Установка флага в методе onCancel может помочь отфильтровать в методе onDismiss намерение пользователя: отменить действие или выполненное действие. Ниже приведен код, который показывает идею.

public class DatePickerDialogFragment extends DialogFragment implements DatePickerDialog.OnDateSetListener {

    private boolean cancelDialog = false;
    private int year;
    private int month;
    private int day;

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        DatePickerDialog dpd = new DatePickerDialog(getActivity(), this, year, month, day);
        return dpd;
    }

    public void setDatePickerDate(int year, int month, int day) {
        this.year = year;
        this.month = month;
        this.day = day;
    }

    @Override
    public void onCancel(DialogInterface dialog) {
        super.onCancel(dialog);
        cancelDialog = true;
    }

    @Override
    public void onDismiss(DialogInterface dialog) {
        super.onDismiss(dialog);
        if (!cancelDialog) {
          #put the code you want to execute if the user clicks the done button
        }
    }

    @Override
    public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) {
        setDatePickerDate(year, monthOfYear, dayOfMonth);
    }
}

1

Существует очень простой обходной путь, если ваше приложение не использует панель действий. Кстати, обратите внимание, что некоторые приложения используют эту функцию для работы, потому что отмена выбора даты имеет особое значение (например, оно очищает поле даты до пустой строки, что для некоторых приложений является допустимым и значимым типом ввода ) и использование логических флагов для предотвращения повторной установки даты на ОК не поможет вам в этом случае.

Число рейнольдса фактическое исправление, вам не нужно создавать новые кнопки или свой собственный диалог. Дело в том, чтобы быть совместимым как с более старыми версиями 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. Я надеюсь, что этот комментарий будет полезен для кого-то. Войтек Ярош

public class myDatePickerDialog extends DatePickerDialog {

public myDatePickerDialog(Context context, OnDateSetListener callBack, int year, int monthOfYear, int dayOfMonth) {
    super(context, callBack, year, monthOfYear, dayOfMonth);
}

@Override
protected void onStop() {
    // Replacing tryNotifyDateSet() with nothing - this is a workaround for Android bug https://android-review.googlesource.com/#/c/61270/A

    // Would also like to clear focus, but we cannot get at the private members, so we do nothing.  It seems to do no harm...
    // mDatePicker.clearFocus();

    // Now we would like to call super on onStop(), but actually what we would mean is super.super, because
    // it is super.onStop() that we are trying NOT to run, because it is buggy.  However, doing such a thing
    // in Java is not allowed, as it goes against the philosophy of encapsulation (the Creators never thought
    // that we might have to patch parent classes from the bottom up :)
    // However, we do not lose much by doing nothing at all, because in Android 2.* onStop() in androd.app.Dialog //actually
    // does nothing and in 4.* it does:
    //      if (mActionBar != null) mActionBar.setShowHideAnimationEnabled(false); 
    // which is not essential for us here because we use no action bar... QED
    // So we do nothing and we intend to keep this workaround forever because of users with older devices, who might
    // run Android 4.1 - 4.3 for some time to come, even if the bug is fixed in later versions of Android.
}   

}


Даже если используется ActionBar, это может быть приемлемым решением, если вы никогда не скрываете / не отображаете ActionBar. Чтобы сделать вещи более безопасными, вы можете переопределить onStart и ничего не делать (тогда вызовы для анимации будут безопасно отсоединены). И даже если вы это скрываете / показываете, отключается только анимация.
Даниил

0


Попробуйте следующие концепции.

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


метод onDateSet () вызывает дважды (если вы проверяете в emulator.it вызовы дважды. Если вы используете реальное устройство, то оно будет вызывать правильно один раз. Если вы используете эмулятор, тогда используйте счетчик. Если вы работаете в реальном устройстве, тогда игнорировать переменную счетчика. Для реального устройства это работает для меня.)
когда пользователь нажимает кнопку в DatePickerDialog.
для этого вы должны сохранить значение счетчика и ничего не делать, когда метод вызывает первый раз, и выполнять операцию, когда метод вызывает второй раз.
Смотрите приведенные ниже фрагменты кода

   static int counter=0;       //Counter will be declared globally.

    DatePickerDialog picker = new DatePickerDialog(
            this,
            new OnDateSetListener() {
                @Override
                public void onDateSet(DatePicker v, int y, int m, int d) {

                   counter++;
                   if(counter==1) return;
                   counter=0;
                   //Do the operations here

                }
            },
            2012, 6, 15);
    picker.show();



Для отмены выбора даты дилалог его работает для меня. Для эмулятора его не работает

DialogInterface.OnClickListener dialogOnClickListener=new DialogInterface.OnClickListener()
        {

            @Override
            public void onClick(DialogInterface dialog, int which) {
                // TODO Auto-generated method stub

                if(which==Dialog.BUTTON_NEGATIVE)
                {
                    Log.i(tagName, "dialog negative button clicked");
                    dialog.dismiss();
                }

            }

        };

        mDatePickerDialog.setButton(Dialog.BUTTON_NEGATIVE, "Cancel", dialogOnClickListener);


Это работает для меня для реального устройства. Но для эмулятора это не работает правильно. Я думаю, что это ошибка эмулятора Android.


0

Простое решение будет использовать логическое значение, чтобы пропустить второй запуск

boolean isShow = false; // define global variable


// when showing time picker
TimePickerDialog timeDlg = new TimePickerDialog( this, new OnTimeSetListener()
            {

                @Override
                public void onTimeSet( TimePicker view, int hourOfDay, int minute )
                {
                    if ( isShow )
                    {
                        isShow = false;
                        // your code
                    }

                }
            }, 8, 30, false );

timeDlg.setButton( TimePickerDialog.BUTTON_NEGATIVE, "Cancel", new DialogInterface.OnClickListener()
            {
                @Override
                public void onClick( DialogInterface dialog, int which )
                {
                    isShow = false;
                }
            } );
timeDlg.setButton( TimePickerDialog.BUTTON_POSITIVE, "Set", new DialogInterface.OnClickListener()
            {
                @Override
                public void onClick( DialogInterface dialog, int which )
                {
                    isShow = true;
                }
            } );

timeDlg.show();

Еще раз, как я уже говорил другому парню перед вами, это не решает проблему. Я не хочу показаться грубым, но вы, ребята, должны опробовать ваше собственное решение, прежде чем публиковать здесь ... просто чтобы доказать мою точку зрения, используйте свой код и позвольте пользователю нажать кнопку назад, чтобы отменить диалог и понять, что я имею в виду. Причина бага является onStopвызов метода , когда он не должен ... он стреляет дважды является следствием ошибки.
davidcsb

Если я не был достаточно ясен, позвольте мне быть: нажатие назад, чтобы отменить диалоговые вызовы onDateSet. Таким образом, сломан.
davidcsb

Спасибо, что показали ошибку. Я редактирую ответ, чтобы он работал с кнопкой возврата. Ваш ответ работает, но DatePicker dp = picker.getDatePicker (); не работает с TimePickers, так как метод getTimePicker () не добавлен. Так что это был бы правильный ответ
chamikaw

Как мы пытаемся пропустить второй запуск? !!, я пробовал это несколько раз, когда закрытие диалога любым способом onDateSetбудет вызываться один раз, но при выборе «сделано» или «установить» он будет вызван дважды. Поэтому нам нужно пропустить только первый, поэтому, если он вызывается дважды, то и только тогда у нас будет правильная дата
AbdelHady

0

Вы можете переопределить onCancel () и использовать setOnDismissListener () для обнаружения негативных действий пользователя. А с DatePickerDialog.BUTTON_POSITIVE вы знаете, что пользователь хочет установить новую дату.

 DatePickerDialog mDPD = new DatePickerDialog(
                      getActivity(), mOnDateSetListener, mYear, mMonth, mDay);
 mDPD.setOnCancelListener(new OnCancelListener() {
    @Override
    public void onCancel(DialogInterface dialog) {
        // do something onCancek
        setDate = false;
    }
 });

 mDPD.setOnDismissListener(new OnDismissListener() {
    @Override
    public void onDismiss(DialogInterface arg0) {
        // do something onDismiss
        setDate = false;
    }
});

mDPD.setButton(DatePickerDialog.BUTTON_POSITIVE, "Finish", new DatePickerDialog.OnClickListener() {

    @Override
    public void onClick(DialogInterface dialog, int which) {
        // user set new date
        setDate = true;
    }
});

затем проверьте для setDate:

public void onDateSet(DatePicker view, int year, int month, int day) {
    if(setDate){
        //do something with new date
    }
}

0

Вот мой класс обхода для DatePickerDialog на кнопке отмены так же как отмене его кнопкой возврата. Копирование и использование в стиле DatePickerDialog (так как слушатель с состоянием, мы должны создать новый экземпляр при использовании, в противном случае требуется больше кода, чтобы он работал)

Использование:

new FixedDatePickerDialog(this,
            new FixedOnDateSetListener() {

                @Override
                public void onDateSet(DatePicker view, int year,
                        int monthOfYear, int dayOfMonth) {
                    if (isOkSelected()) {
                        // when DONE button is clicked
                    }
                }

            }, year, month, day).show();

Класс:

public class FixedDatePickerDialog extends DatePickerDialog {
private final FixedOnDateSetListener fixedCallback;
public FixedDatePickerDialog(Context context,
        FixedOnDateSetListener callBack, int year, int monthOfYear,
        int dayOfMonth) {
    super(context, callBack, year, monthOfYear, dayOfMonth);
    fixedCallback = callBack;
    this.setButton(DialogInterface.BUTTON_NEGATIVE,
            context.getString(R.string.cancel), this);
    this.setButton(DialogInterface.BUTTON_POSITIVE,
            context.getString(R.string.done), this);
}

@Override
public void onClick(DialogInterface dialog, int which) {
    if (which == BUTTON_POSITIVE) {
        fixedCallback.setOkSelected(true);
    } else {
        fixedCallback.setOkSelected(false);
    }
    super.onClick(dialog, which);
}

public abstract static class FixedOnDateSetListener implements
        OnDateSetListener {
    private boolean okSelected = false;

    @Override
    abstract public void onDateSet(DatePicker view, int year,
            int monthOfYear, int dayOfMonth);

    public void setOkSelected(boolean okSelected) {
        this.okSelected = okSelected;
    }

    public boolean isOkSelected() {
        return okSelected;
    }
}

}


0

Я использую даты, время и число. Сборщики чисел вызывают onValueChanged всякий раз, когда пользователь выбирает число, перед тем как сборщик отклоняется, поэтому у меня уже была такая структура, чтобы делать что-то со значением только тогда, когда сборщик отклонен:

public int interimValue;
public int finalValue;

public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
    this.interimValue = newVal;
}

public void onDismiss(DialogInterface dialog) {
    super.onDismiss(dialog);
    this.finalValue = this.interimValue;
}

Я расширил это, чтобы установить пользовательские onClickListeners для моих кнопок с аргументом, чтобы увидеть, какая кнопка была нажата. Теперь я могу проверить, какая кнопка была нажата, прежде чем установить окончательное значение:

public int interimValue;
public int finalValue;
public boolean saveButtonClicked;

public void setup() {
    picker.setButton(DialogInterface.BUTTON_POSITIVE, getString(R.string.BUTTON_SAVE), new DialogInterface.OnClickListener() {
        public void onClick(DialogInterface dialog, int which) {
            picker.onClick(dialog, which); // added for Android 5.0
            onButtonClicked(true);
        }
    });
    picker.setButton(DialogInterface.BUTTON_NEGATIVE, getString(R.string.BUTTON_CANCEL), new DialogInterface.OnClickListener() {
        public void onClick(DialogInterface dialog, int which) {
            picker.onClick(dialog, which); // added for Android 5.0
            onButtonClicked(false);
        }
    });
}

public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
    this.interimValue = newVal;
}

public void onButtonClicked(boolean save) {
    this.saveButtonClicked = save;
}

public void onDismiss(DialogInterface dialog) {
    super.onDismiss(dialog);
    if (this.saveButtonClicked) {
        // save
        this.finalValue = this.interimValue;
    } else {
        // cancel
    }
}

А затем я расширил это для работы с типами даты и времени для средств выбора даты и времени, а также с типом int для средств выбора чисел.

Я опубликовал это, потому что я думал, что это было проще, чем некоторые из решений выше, но теперь, когда я включил весь код, я думаю, что это не намного проще! Но это хорошо вписывается в структуру, которую я уже имел.

Обновление для Lollipop: По-видимому, эта ошибка возникает не на всех устройствах Android 4.1-4.4, потому что я получил несколько отчетов от пользователей, чьи средства выбора даты и времени не вызывали обратные вызовы onDateSet и onTimeSet. И ошибка была официально исправлена ​​в Android 5.0. Мой подход работал только на устройствах, где присутствует ошибка, потому что мои пользовательские кнопки не вызывали обработчик onClick диалога, который является единственным местом, где onDateSet и onTimeSet вызываются, когда ошибки нет. Я обновил мой код выше, чтобы вызывать диалог onClick, так что теперь он работает независимо от того, присутствует ли ошибка.


0

Мне понравился ответ Дэвида Чезарино выше, но я хотел что-то, что было бы заменой неработающего диалога и работало бы над любым диалогом, который мог бы отсутствовать, отмена / неправильное поведение отмены. Вот производные классы для DatePickerDialog / TimePickerDialog, которые должны работать как капли замен. Это не пользовательские представления. Он использует системный диалог, но просто изменяет поведение кнопки отмены / возврата, чтобы работать как положено.

Это должно работать на уровне API 3 и выше. Итак, в основном любая версия Android (я тестировал ее специально на желе и леденце).

DatePickerDialog:

package snappy_company_name_here;

import android.content.Context;
import android.content.DialogInterface;
import android.widget.DatePicker;

/**
 * This is a modified version of DatePickerDialog that correctly handles cancellation behavior since it's broken on jellybean and
 * kitkat date pickers.
 *
 * Here is the bug: http://code.google.com/p/android/issues/detail?id=34833
 * Here is an SO post with a bunch of details: http://stackoverflow.com/questions/11444238/jelly-bean-datepickerdialog-is-there-a-way-to-cancel
 *
 * @author stuckj, created on 5/5/15.
 */
public class DatePickerDialog extends android.app.DatePickerDialog implements DialogInterface.OnClickListener
{
    final CallbackHelper callbackHelper;

    // NOTE: Must be static since we're using it in a super constructor call. Which is annoying, but necessary
    private static class CallbackHelper implements OnDateSetListener
    {
        private final OnDateSetListener callBack;
        private boolean dialogButtonPressHandled = false; // To prevent setting the date when the dialog is dismissed...

        // NOTE: Must be static since we're using it in a super constructor call. Which is annoying, but necessary
        public CallbackHelper(final OnDateSetListener callBack)
        {
            this.callBack = callBack;
        }

        @Override
        public void onDateSet(final DatePicker view, final int year, final int monthOfYear, final int dayOfMonth)
        {
            if (!dialogButtonPressHandled && (callBack != null))
            {
                callBack.onDateSet(view, year, monthOfYear, dayOfMonth);
            }
        }
    }

    /**
     * Sets the positive and negative buttons to use the dialog callbacks we define.
     */
    private void setButtons(final Context context)
    {
        setButton(DialogInterface.BUTTON_NEGATIVE, context.getString(android.R.string.cancel), this);
        setButton(DialogInterface.BUTTON_POSITIVE, context.getString(android.R.string.ok), this);
    }

    @Override
    public void onClick(final DialogInterface dialog, final int which)
    {
        // ONLY call the super method in the positive case...
        if (which == DialogInterface.BUTTON_POSITIVE)
        {
            super.onClick(dialog, which);
        }

        callbackHelper.dialogButtonPressHandled = true;
    }

    @Override
    public void onBackPressed()
    {
        getButton(DialogInterface.BUTTON_NEGATIVE).performClick();
    }

    // Need this so we can both pass callbackHelper to the super class and save it off as a variable.
    private DatePickerDialog(final Context context,
                             final OnDateSetListener callBack,
                             final int year,
                             final int monthOfYear,
                             final int dayOfMonth,
                             final CallbackHelper callbackHelper)
    {
        super(context, callbackHelper, year, monthOfYear, dayOfMonth);
        this.callbackHelper = callbackHelper;
        setButtons(context);
    }

    /**
     * @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(final Context context,
                            final OnDateSetListener callBack,
                            final int year,
                            final int monthOfYear,
                            final int dayOfMonth)
    {
        this(context, callBack, year, monthOfYear, dayOfMonth, new CallbackHelper(callBack));
    }

    // Need this so we can both pass callbackHelper to the super class and save it off as a variable.
    private DatePickerDialog(final Context context, final int theme, final OnDateSetListener listener, final int year,
                             final int monthOfYear, final int dayOfMonth, final CallbackHelper callbackHelper)
    {
        super(context, theme, callbackHelper, year, monthOfYear, dayOfMonth);
        this.callbackHelper = callbackHelper;
        setButtons(context);
    }

    /**
     * @param context The context the dialog is to run in.
     * @param theme the theme to apply to this dialog
     * @param listener 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(final Context context, final int theme, final OnDateSetListener listener, final int year,
                            final int monthOfYear, final int dayOfMonth)
    {
        this(context, theme, listener, year, monthOfYear, dayOfMonth, new CallbackHelper(listener));
    }
}

TimePickerDialog:

package snappy_company_name_here;

import android.content.Context;
import android.content.DialogInterface;
import android.widget.TimePicker;

/**
 * This is a modified version of TimePickerDialog that correctly handles cancellation behavior since it's broken on jellybean and
 * kitkat date pickers.
 *
 * Here is the bug: http://code.google.com/p/android/issues/detail?id=34833
 * Here is an SO post with a bunch of details: http://stackoverflow.com/questions/11444238/jelly-bean-datepickerdialog-is-there-a-way-to-cancel
 *
 * @author stuckj, created on 5/5/15.
 */
public class TimePickerDialog extends android.app.TimePickerDialog implements DialogInterface.OnClickListener
{
    final CallbackHelper callbackHelper;

    // NOTE: Must be static since we're using it in a super constructor call. Which is annoying, but necessary
    private static class CallbackHelper implements OnTimeSetListener
    {
        private final OnTimeSetListener callBack;
        private boolean dialogButtonPressHandled = false; // To prevent setting the date when the dialog is dismissed...

        // NOTE: Must be static since we're using it in a super constructor call. Which is annoying, but necessary
        public CallbackHelper(final OnTimeSetListener callBack)
        {
            this.callBack = callBack;
        }

        @Override
        public void onTimeSet(final TimePicker view, final int hourOfDay, final int minute)
        {
            if (!dialogButtonPressHandled && (callBack != null))
            {
                callBack.onTimeSet(view, hourOfDay, minute);
            }
        }
    }

    /**
     * Sets the positive and negative buttons to use the dialog callbacks we define.
     */
    private void setButtons(final Context context)
    {
        setButton(DialogInterface.BUTTON_NEGATIVE, context.getString(android.R.string.cancel), this);
        setButton(DialogInterface.BUTTON_POSITIVE, context.getString(android.R.string.ok), this);
    }

    @Override
    public void onClick(final DialogInterface dialog, final int which)
    {
        // ONLY call the super method in the positive case...
        if (which == DialogInterface.BUTTON_POSITIVE)
        {
            super.onClick(dialog, which);
        }

        callbackHelper.dialogButtonPressHandled = true;
    }

    @Override
    public void onBackPressed()
    {
        getButton(DialogInterface.BUTTON_NEGATIVE).performClick();
    }

    // Need this so we can both pass callbackHelper to the super class and save it off as a variable.
    private  TimePickerDialog(final Context context,
                              final OnTimeSetListener callBack,
                              final int hourOfDay, final int minute, final boolean is24HourView, final CallbackHelper callbackHelper)
    {
        super(context, callbackHelper, hourOfDay, minute, is24HourView);
        this.callbackHelper = callbackHelper;
        setButtons(context);
    }

    /**
     * @param context Parent.
     * @param callBack How parent is notified.
     * @param hourOfDay The initial hour.
     * @param minute The initial minute.
     * @param is24HourView Whether this is a 24 hour view, or AM/PM.
     */
    public TimePickerDialog(final Context context,
                            final OnTimeSetListener callBack,
                            final int hourOfDay, final int minute, final boolean is24HourView)
    {
        this(context, callBack, hourOfDay, minute, is24HourView, new CallbackHelper(callBack));
    }

    // Need this so we can both pass callbackHelper to the super class and save it off as a variable.
    private TimePickerDialog(final Context context, final int theme, final OnTimeSetListener callBack, final int hourOfDay,
                            final int minute, final boolean is24HourView, final CallbackHelper callbackHelper)
    {
        super(context, theme, callbackHelper, hourOfDay, minute, is24HourView);
        this.callbackHelper = callbackHelper;
        setButtons(context);
    }

    /**
     * @param context Parent.
     * @param theme the theme to apply to this dialog
     * @param callBack How parent is notified.
     * @param hourOfDay The initial hour.
     * @param minute The initial minute.
     * @param is24HourView Whether this is a 24 hour view, or AM/PM.
     */
    public TimePickerDialog(final Context context, final int theme, final OnTimeSetListener callBack, final int hourOfDay,
                            final int minute, final boolean is24HourView)
    {
        this(context, theme, callBack, hourOfDay, minute, is24HourView, new CallbackHelper(callBack));
    }
}

Это очень похоже на ответ Моря выше.
stuckj

0

Моя рабочая версия с ClearButton с использованием лямбда-выражений:

public class DatePickerFragment extends DialogFragment {
    private OnDateSelectListener dateSelectListener;
    private OnDateClearListener dateClearListener;

    public void setDateSelectListener(OnDateSelectListener dateSelectListener) {
        this.dateSelectListener = dateSelectListener;
    }

    public void setDateClearListener(OnDateClearListener dateClearListener) {
        this.dateClearListener = dateClearListener;
    }

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        // Use the current date as the default date in the picker
        final Calendar c = Calendar.getInstance();
        int year = c.get(Calendar.YEAR);
        int month = c.get(Calendar.MONTH);
        int day = c.get(Calendar.DAY_OF_MONTH);

        // Create a new instance of DatePickerDialog and return it
        DatePickerDialog dialog = new DatePickerDialog(getActivity(), null, year, month, day);
        dialog.setCancelable(true);
        dialog.setCanceledOnTouchOutside(true);
        dialog.setTitle("Select Date");
        dialog.setButton(BUTTON_POSITIVE, ("Done"), (dialog1, which) -> {
            DatePicker dp = dialog.getDatePicker();
            dialog.dismiss();
            dateSelectListener.onDateSelect(dp.getYear(), dp.getMonth(), dp.getDayOfMonth());
        });
        dialog.setButton(BUTTON_NEUTRAL, ("Clear"), (dialog1, which) -> {
            dialog.dismiss();
            dateClearListener.onDateClear();
        });
        dialog.setButton(BUTTON_NEGATIVE, ("Cancel"), (dialog1, which) -> {
            if (which == DialogInterface.BUTTON_NEGATIVE) {
                dialog.cancel();
            }
        });
        dialog.getDatePicker().setCalendarViewShown(false);
        return dialog;
    }


    public interface OnDateClearListener {
        void onDateClear();
    }

    public interface OnDateSelectListener {
        void onDateSelect(int year, int monthOfYear, int dayOfMonth);
    }
}

0

Для TimePickerDialog обходной путь может быть следующим:

TimePickerDialog createTimePickerDialog(Context context, int themeResId, TimePickerDialog.OnTimeSetListener orignalListener,
                                                         int hourOfDay, int minute, boolean is24HourView) {
        class KitKatTimeSetListener implements TimePickerDialog.OnTimeSetListener {
            private int hour;
            private int minute;

            private KitKatTimeSetListener() {
            }

            @Override
            public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
                this.hour = hourOfDay;
                this.minute = minute;
            }

            private int getHour() { return hour; }
            private int getMinute() {return minute; }
        };

        KitKatTimeSetListener kitkatTimeSetListener = new KitKatTimeSetListener();
        TimePickerDialog timePickerDialog = new TimePickerDialog(context, themeResId, kitkatTimeSetListener, hourOfDay, minute, is24HourView);

        timePickerDialog.setButton(DialogInterface.BUTTON_POSITIVE, context.getString(android.R.string.ok), (dialog, which) -> {
            timePickerDialog.onClick(timePickerDialog, DialogInterface.BUTTON_POSITIVE);
            orignalListener.onTimeSet(new TimePicker(context), kitkatTimeSetListener.getHour(), kitkatTimeSetListener.getMinute());
            dialog.cancel();
        });
        timePickerDialog.setButton(DialogInterface.BUTTON_NEGATIVE, context.getString(android.R.string.cancel), (dialog, which) -> {
            dialog.cancel();
        });

        return timePickerDialog;
    }

Я делегирую все события оболочке KitKatSetTimeListener и запускаю только исходный OnTimeSetListener только в случае нажатия BUTTON_POSITIVE.


0

После тестирования некоторых предложений, размещенных здесь, я лично считаю, что это решение является наиболее простым. Я передаю «null» как мой слушатель в конструкторе DatePickerDialog, а затем, когда я нажимаю кнопку «ОК», я вызываю мой onDateSearchSetListener:

datePickerDialog = new DatePickerDialog(getContext(), null, dateSearch.get(Calendar.YEAR), dateSearch.get(Calendar.MONTH), dateSearch.get(Calendar.DAY_OF_MONTH));
    datePickerDialog.setCancelable(false);
    datePickerDialog.setButton(DialogInterface.BUTTON_POSITIVE, getString(R.string.dialog_ok), new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int which) {
            Log.d("Debug", "Correct");
            onDateSearchSetListener.onDateSet(datePickerDialog.getDatePicker(), datePickerDialog.getDatePicker().getYear(), datePickerDialog.getDatePicker().getMonth(), datePickerDialog.getDatePicker().getDayOfMonth());
        }
    });
    datePickerDialog.setButton(DialogInterface.BUTTON_NEGATIVE, getString(R.string.dialog_cancel), new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int which) {
            Log.d("Debug", "Cancel");
            dialog.dismiss();
        }
    });

-1

Я знаю, что этот пост был здесь почти год, но я решил опубликовать свои выводы. Вы могли бы все еще держать слушателя (вместо того, чтобы установить его на отключение звука) и все еще иметь эту работу как ожидалось. Ключ заключается в том, чтобы неявно установить «ОК» или (и) кнопки «отмена». Я проверил это, и это работает с благодарностью для меня. Слушателя не увольняют дважды.

Посмотрите на этот пример,

private void setTime(){
final Calendar c = Calendar.getInstance();
int hour = c.get(Calendar.HOUR_OF_DAY);
int minute = c.get(Calendar.MINUTE);

final TimePickerDialog timepicker = new TimePickerDialog(this.getActivity(),
        timePickerListener,
        hour, 
        minute, 
        DateFormat.is24HourFormat(getActivity()));

timepicker.setButton(DialogInterface.BUTTON_POSITIVE, "Print", new    
    android.content.DialogInterface.OnClickListener(){
        @Override
        public void onClick(DialogInterface dialog,int which) {
            print = true;
            timepicker.dismiss();           
        }
});

timepicker.setButton(DialogInterface.BUTTON_NEGATIVE, "Cancel", new 
    android.content.DialogInterface.OnClickListener(){
        @Override
        public void onClick(DialogInterface dialog,int which){
            print = false;
            timepicker.dismiss();       
        }
});

timepicker.setCancelable(false);
timepicker.show();
}

Просто и понятно, это не работает так, как вы говорите. timePickerListenerпо-прежнему вызывается независимо от того, что вы делаете в своем диалоге. Мне даже не нужно проверять, чтобы знать это, вам просто нужно взглянуть на источники : если вы не установите его null, tryNotifyTimeSet()вызовет слушателя onTimeSet()как в его, так onClick()и в onStop().
davidcsb

Другими словами, не позволяйте нативному классу содержать ссылку на ваш слушатель (т. Е. Передавать его в конструктор). То, как вы справляетесь с кнопками, не имеет значения.
davidcsb

Привет, Дэвид! Ты не проверял это и предполагал, что это не сработает. Это именно тот кусок кода, который я сейчас использую в своем приложении, и он работает как чемпион.
Крокодил

1) Я никогда не говорил, что это не работает. Я сказал, что это не сработало так, как ты сказал. Пожалуйста, прочитайте еще раз. 2) вы нарушили LSP и SRP, тратить человеко-часов без необходимости изменить всю вашу всю клиентскую логику , которая не требует никаких изменений , чтобы начать с. 3) ваш ответ отвечает на вопрос, да, в противном случае я бы пометил его для удаления как "не ответ", но 4) ваш ответ все еще (извините за это) очень неэффективен и не затрагивает фундаментальную проблему дизайна (слушатель называется ), следовательно, только downvote. 6) Вы отключили обратно и сделали модальное окно для принудительного вызова логического значения. Итак, 6 серьезных вопросов!
davidcsb
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.