DialogFragment - сохранение слушателя после поворота экрана

У меня есть DialogFragment, который создает DatePickerDialog. Я использую статический метод с именем newInstance для установки начальных значений, чтобы использовать пустой конструктор по умолчанию. Однако, как я должен установить слушателя? После поворота экрана при нажатии на кнопку "Готово" слушатель ничего не делает, так как его не существует.

public class DatePickerFragment extends DialogFragment {
    public static final String ARG_YEAR = "year";
    public static final String ARG_MONTH = "month";
    public static final String ARG_DAY = "day";

    private OnDateSetListener listener_;

    public static DatePickerFragment newInstance(OnDateSetListener listener, int year, int month, int day) {
        final DatePickerFragment date_picker = new DatePickerFragment();
        date_picker.setListener(listener);

        final Bundle arguments = new Bundle();
        arguments.putInt(ARG_YEAR, year);
        arguments.putInt(ARG_MONTH, month);
        arguments.putInt(ARG_DAY, day);
        date_picker.setArguments(arguments);

        return date_picker;
    }

    private void setListener(OnDateSetListener listener) {
        listener_ = listener;
    }

    @Override
    public Dialog onCreateDialog(Bundle saved_instance_state) {
        final Bundle arguments = getArguments();
        final int year = arguments.getInt(ARG_YEAR);
        final int month = arguments.getInt(ARG_MONTH);
        final int day = arguments.getInt(ARG_DAY);

        return new DatePickerDialog(getActivity(), listener_, year, month, day);
    }
}

person Renato Rodrigues    schedule 26.12.2012    source источник


Ответы (3)


Однако, как я должен установить слушателя?

Вы обновляете ссылку на прослушиватель в методе onCreate метода Activity:

private OnDateSetListener mOds = new OnDateSetListener() {

    @Override
    public void onDateSet(DatePicker view, int year, int monthOfYear,
            int dayOfMonth) {
             // do important stuff
    }
};

и в методе onCreate:

if (savedInstanceState != null) {
    DatePickerFragment dpf = (DatePickerFragment) getSupportFragmentManager()
            .findFragmentByTag("theTag?");
    if (dpf != null) {
        dpf.setListener(mOds);
    }
}
person user    schedule 26.12.2012
comment
+1, также для уточнения, важно использовать тег фрагмента диалога для повторного применения слушателя. Этот тег указывается во втором аргументе метода фрагментов диалога show. - person rekaszeru; 02.08.2017
comment
DialogFragment не является фрагментом библиотеки поддержки, поэтому вместо DatePickerFragment dpf = (DatePickerFragment) getSupportFragmentManager() .findFragmentByTag("theTag?"); следует использовать DatePickerFragment dpf = (DatePickerFragment) getFragmentManager() .findFragmentByTag("theTag?"); - person Velmurugan V; 15.10.2017
comment
@VelmuruganV DialogFragment доступен в библиотеке поддержки developer.android.com /ссылка/android/support/v4/приложение/ - person user; 15.10.2017
comment
ой извините я этого не знал. Спасибо, что поправили меня @Luksprog - person Velmurugan V; 16.10.2017
comment
Ужасно, что нам приходится иметь дело с такими вещами. Мы все должны пойти и сообщить о проблеме в Google, чтобы пойти и сделать хороший API, который не заставит нас обходить все вокруг. - person milosmns; 01.08.2019

На мой взгляд, есть более эффективный способ сделать это, используя жизненный цикл фрагмента. Вы можете использовать обратные вызовы жизненного цикла фрагмента onAttach() и onDetach() для автоматического преобразования активности в качестве прослушивателя следующим образом:

@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);
    try {
        _listener = (OnDateSetListener)activity;
    } catch(ClassCastException e) {
        // check if _listener is null before using,
        // or throw new ClassCastException("hosting activity must implement OnDateSetListener");
    }
}

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

Этот метод официально задокументирован здесь

person d370urn3ur    schedule 27.01.2015
comment
Да, но таким образом вы не можете использовать один и тот же класс (скажем, DialogFragment) несколько раз в Activity с разными слушателями... - person ARLabs; 18.12.2015
comment
Почему бы нет? Просто реализуйте любой интерфейс прослушивателя, который вы хотите, в Activity и приведите его к разным экземплярам прослушивателя в DialogFragment. Если вы хотите сделать некоторые необязательными, вы можете просто иметь более сложную обработку ошибок ClassCast. - person d370urn3ur; 18.12.2015
comment
Я имею в виду, что если одна и та же активность должна использовать один и тот же DialogFragment два раза и (как вы предложили) активность наследуется от слушателя, когда вызывается метод прослушивателя в действии, вы не знаете, в чем дело... Я думаю лучше использовать два разных экземпляра слушателя (они не могут быть активностью). То, что я сказал, основано на гипотезе о том, что для хорошего дизайна мы избегаем циклических зависимостей, а DialogFragment определяет свой собственный тип слушателя и не знает специализации Activity. - person ARLabs; 25.01.2016
comment
Я не понимаю, что вы имеете в виду: когда вызывается метод слушателя, вы не знаете, в чем дело. Вы имеете в виду, какой экземпляр DialogFragment вызвал метод слушателя? Почему вы не можете просто передать идентифицирующую информацию о DialogFragment в методе слушателя? Мне нужно увидеть код, чтобы понять ваш вариант использования. Дело в том, что DialogFragment уже тесно связан с Activity, и это самый простой способ справиться с наиболее распространенным использованием DialogFragments. Он также официально задокументирован, поэтому неудивительно, что он встречается в рабочих приложениях. - person d370urn3ur; 26.01.2016
comment
@ d370urn3ur, вероятно, ARLabs означает, что Activity может раздувать множество фрагментов, расширяющих один и тот же DialogFragment. При этом слушатель может быть разным. - person CoolMind; 05.10.2016
comment
@ARLabs, согласен с тобой. Я полагаю, в этом случае мы должны добавить Intent или setArguments() для каждого экземпляра DialogFragment. Туда мы передадим идентификатор экземпляра (случайное число, которое не изменится после ротации). Мы должны передать этот идентификатор слушателю в качестве параметра. В Activity мы получим этот id и проверим, что DialogFragment вызвал слушатель. - person CoolMind; 03.04.2019

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

Кстати, тег является вторым параметром при вызове DialogFragment.show().

person cn123h    schedule 01.01.2015
comment
Я обнаружил, что если вы вызываете DialogFragment из Fragment и после поворота экрана вызываете fragmentManager.findFragmentByTag() из onCreateView(), он может вернуть null. В этом случае попробуйте позвонить childFragmentManager.findFragmentByTag(). Или посмотрите ответ @d370urn3ur. В этом случае вы должны установить обратный вызов внутри файла Activity. - person CoolMind; 03.04.2019