Обратный вызов фрагмента из фрагмента диалога

159

Вопрос: Как создать обратный вызов из DialogFragment в другой Fragment. В моем случае, Активность должна полностью не знать о DialogFragment.

Считай, у меня есть

public class MyFragment extends Fragment implements OnClickListener

Тогда в какой-то момент я мог сделать

DialogFragment dialogFrag = MyDialogFragment.newInstance(this);
dialogFrag.show(getFragmentManager, null);

Где выглядит MyDialogFragment

protected OnClickListener listener;
public static DialogFragment newInstance(OnClickListener listener) {
    DialogFragment fragment = new DialogFragment();
    fragment.listener = listener;
    return fragment;
}

Но нет никакой гарантии, что слушатель будет рядом, если DialogFragment приостановится и возобновит свой жизненный цикл. Единственными гарантиями во фрагменте являются те, которые передаются через Bundle через setArguments и getArguments.

Существует способ ссылаться на активность, если это должен быть слушатель:

public Dialog onCreateDialog(Bundle bundle) {
    OnClickListener listener = (OnClickListener) getActivity();
    ....
    return new AlertDialog.Builder(getActivity())
        ........
        .setAdapter(adapter, listener)
        .create();
}

Но я не хочу, чтобы Activity прослушивала события, мне нужен фрагмент. Действительно, это может быть любой объект Java, который реализует OnClickListener.

Рассмотрим конкретный пример фрагмента, который представляет AlertDialog через DialogFragment. Он имеет кнопки Да / Нет. Как я могу отправить эти нажатия кнопки назад на фрагмент, который его создал?

eternalmatt
источник
Вы упомянули: «Но нет никакой гарантии, что слушатель будет рядом, если DialogFragment приостановится и возобновит свой жизненный цикл». Я думал, что состояние фрагмента разрушается во время onDestroy ()? Вы должны быть правы, но я сейчас немного запутался, как использовать состояние фрагмента. Как воспроизвести проблему, о которой вы упоминали, слушателя нет рядом?
Шон
Я не понимаю, почему вы не можете просто использовать OnClickListener listener = (OnClickListener) getParentFragment();вместо этого DialogFragment, а ваш основной фрагмент реализует интерфейс, как вы делали изначально.
kiruwka
Вот ответ на несвязанный вопрос, но он покажет вам, как это сделать чистым способом stackoverflow.com/questions/28620026/…
user2288580

Ответы:

190

Активная деятельность совершенно не знает о DialogFragment.

Фрагмент класса:

public class MyFragment extends Fragment {
int mStackLevel = 0;
public static final int DIALOG_FRAGMENT = 1;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    if (savedInstanceState != null) {
        mStackLevel = savedInstanceState.getInt("level");
    }
}

@Override
public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putInt("level", mStackLevel);
}

void showDialog(int type) {

    mStackLevel++;

    FragmentTransaction ft = getActivity().getFragmentManager().beginTransaction();
    Fragment prev = getActivity().getFragmentManager().findFragmentByTag("dialog");
    if (prev != null) {
        ft.remove(prev);
    }
    ft.addToBackStack(null);

    switch (type) {

        case DIALOG_FRAGMENT:

            DialogFragment dialogFrag = MyDialogFragment.newInstance(123);
            dialogFrag.setTargetFragment(this, DIALOG_FRAGMENT);
            dialogFrag.show(getFragmentManager().beginTransaction(), "dialog");

            break;
    }
}

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
        switch(requestCode) {
            case DIALOG_FRAGMENT:

                if (resultCode == Activity.RESULT_OK) {
                    // After Ok code.
                } else if (resultCode == Activity.RESULT_CANCELED){
                    // After Cancel code.
                }

                break;
        }
    }
}

}

Класс DialogFragment:

public class MyDialogFragment extends DialogFragment {

public static MyDialogFragment newInstance(int num){

    MyDialogFragment dialogFragment = new MyDialogFragment();
    Bundle bundle = new Bundle();
    bundle.putInt("num", num);
    dialogFragment.setArguments(bundle);

    return dialogFragment;

}

@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {

    return new AlertDialog.Builder(getActivity())
            .setTitle(R.string.ERROR)
            .setIcon(android.R.drawable.ic_dialog_alert)
            .setPositiveButton(R.string.ok_button,
                    new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int whichButton) {
                            getTargetFragment().onActivityResult(getTargetRequestCode(), Activity.RESULT_OK, getActivity().getIntent());
                        }
                    }
            )
            .setNegativeButton(R.string.cancel_button, new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int whichButton) {
                    getTargetFragment().onActivityResult(getTargetRequestCode(), Activity.RESULT_CANCELED, getActivity().getIntent());
                }
            })
            .create();
}
}
Петр Ильесарев
источник
100
Я думаю, что ключ здесь setTargetFragmentи getTargetFragment. Использование onActivityResultнемного неясно. Вероятно, было бы лучше объявить свой собственный конкретный метод в вызывающей стороне фрагмента и использовать его вместо повторного использования onActivityResult. Но в этом все семантика.
Eternalmatt
2
переменная уровня стека не используется?
Отображаемое имя
6
это переживет изменение конфигурации - вращение?
Maxrunner
3
Использовал это. Примечания: уровень стека не был необходим, чтобы пережить вращение или сон. Вместо onActivityResult мой фрагмент реализует DialogResultHandler # handleDialogResult (интерфейс, который я создал). @myCode, было бы очень полезно показать диалоговое значение, добавленное в Intent, а затем прочитать его в onActivityResult. Намерения непонятны новичкам.
Крис Бетти
7
@eternalmatt, ваше возражение вполне обоснованно, но я думаю, что значение onActivityResult () заключается в том, что он гарантированно существует в любом фрагменте, поэтому любой фрагмент может быть использован в качестве родителя. Если вы создаете свой собственный интерфейс и его родительский фрагмент реализует его, то дочерний элемент может использоваться только с родителями, которые реализуют этот интерфейс. Если вы подключите ребенка к этому интерфейсу, это может вас преследовать, если вы позже начнете использовать ребенка более широко. Использование «встроенного» интерфейса onActivityResult () не требует дополнительного связывания, поэтому дает вам немного больше гибкости.
Дальбергия,
78

Решение TargetFragment не кажется лучшим вариантом для фрагментов диалогов, поскольку оно может быть создано IllegalStateExceptionпосле уничтожения и повторного создания приложения. В этом случае FragmentManagerне удалось найти целевой фрагмент, и вы получите сообщение IllegalStateExceptionс таким:

«Фрагмент больше не существует для ключа android: target_state: index 1»

Похоже, что Fragment#setTargetFragment()он предназначен не для связи между дочерним и родительским фрагментами, а скорее для связи между родственными фрагментами.

Таким образом, альтернативный способ - создать подобные фрагменты диалога, используя ChildFragmentManagerродительский фрагмент, а не действия FragmentManager:

dialogFragment.show(ParentFragment.this.getChildFragmentManager(), "dialog_fragment");

И используя интерфейс, в onCreateметоде DialogFragmentвы можете получить родительский фрагмент:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    try {
        callback = (Callback) getParentFragment();
    } catch (ClassCastException e) {
        throw new ClassCastException("Calling fragment must implement Callback interface");
    }
}

Осталось только вызвать метод обратного вызова после этих шагов.

Дополнительную информацию о проблеме можно получить по ссылке: https://code.google.com/p/android/issues/detail?id=54520.

Огуз Озджан
источник
2
Это также работает с onAttach (контекстным контекстом), представленным в api 23.
Santa Teclado
1
ЭТО должен быть принятый ответ. Текущий принятый ответ содержит ошибки, и фрагменты не предназначены для такого использования.
NecipAllef
3
@AhmadFadli Проблема здесь в том, чтобы получить правильный контекст (родительский) для связи между фрагментами. Если вы собираетесь использовать фрагмент диалога как дитя активности, тогда не должно быть никакой путаницы. FragmentManager of Activity и getActivity () для получения обратного вызова достаточно.
Огуз Озджан
1
Это должен быть принятый ответ. Это ясно объяснено и подробно, это не просто код, брошенный людям.
Винс
1
@lukecross ParentFragment - это фрагмент, который создает DialogFragment (тот, который вызывает show ()), но похоже, что childFragmentManager не выдерживает реконфигурацию / поворот экрана ...
iwat0qs
34

Я следовал этим простым шагам, чтобы сделать это.

  1. Создайте интерфейс как DialogFragmentCallbackInterfaceс некоторым методом, как callBackMethod(Object data). Который вы бы звонили для передачи данных.
  2. Теперь вы можете реализовать DialogFragmentCallbackInterfaceинтерфейс в вашем фрагменте, какMyFragment implements DialogFragmentCallbackInterface
  3. Во время DialogFragmentсоздания установите ваш вызывающий фрагмент в MyFragmentкачестве целевого фрагмента, который создал, DialogFragmentиспользуйте myDialogFragment.setTargetFragment(this, 0)проверку setTargetFragment (фрагмент фрагмента, int requestCode)

    MyDialogFragment dialogFrag = new MyDialogFragment();
    dialogFrag.setTargetFragment(this, 1); 
  4. Получите целевой объект фрагмента в свой DialogFragment, вызвав getTargetFragment()и приведя его к. DialogFragmentCallbackInterfaceТеперь вы можете использовать этот интерфейс для отправки данных к вашему фрагменту.

    DialogFragmentCallbackInterface callback = 
               (DialogFragmentCallbackInterface) getTargetFragment();
    callback.callBackMethod(Object data);

    Это все сделано! просто убедитесь, что вы реализовали этот интерфейс в своем фрагменте.

Виджай Ванхеде
источник
4
Это должен быть лучший ответ. Отличный ответ.
Г-н Саджедул Карим
Также убедитесь, что вы используете один и тот же менеджер фрагментов для исходного и конечного фрагментов, иначе getTargetFragment не будет работать. Поэтому, если вы используете childFragmentManager, он не будет работать, так как исходный фрагмент не фиксируется дочерним менеджером фрагментов. Лучше всего рассматривать эти 2 фрагмента как фрагменты сиблингов, а не как фрагменты родительский / дочерний.
Thupten
Честно говоря, лучше использовать только шаблон целевого фрагмента при общении между двумя фрагментами. Не имея слушателя, вы избегаете случайной утечки фрагмента 1 во фрагменте 2. При использовании целевого фрагмента не используйте слушатель / обратный вызов. Используйте только onActivityResult(request code, resultcode, intent)для возврата результата во фрагмент1. Из фрагмента 1 setTargetFragment()и из фрагмента 2 используйте getTargetFragment(). При использовании родительского / дочернего фрагмента для фрагментации или действия для фрагментирования вы можете использовать прослушиватель или обратный вызов, поскольку нет опасности утечки родительского фрагмента в дочернем фрагменте.
Thupten
@ Когда вы говорите «утечка», вы имеете в виду утечку памяти или «детали реализации утечки»? Я не думаю, что ответ Виджая приведет к утечке памяти больше, чем использование onActivityResulty для возвращаемого значения. Оба шаблона будут сохранять ссылку на целевой фрагмент. Если вы имеете в виду утечку деталей реализации, то я думаю, что его шаблон даже лучше, чем onActivityResult. Метод обратного вызова является явным (если указан правильно). Если все, что вы получите, в порядке и ОТМЕНЕНО, первый фрагмент должен интерпретировать, что это значит.
tir38
34

Может быть, немного поздно, но может помочь другим людям с тем же вопросом, что и я.

Вы можете использовать setTargetFragmentна Dialogперед показом, и в диалоге вы можете позвонить , getTargetFragmentчтобы получить ссылку.

Руи
источник
Вот ответ на другой вопрос, но он также относится к вашему вопросу и является чистым решением: stackoverflow.com/questions/28620026/…
user2288580
IllegalStateException для меня
Люк Кросс
19

В руководстве « Общение с другими фрагментами» говорится, что фрагменты должны взаимодействовать через связанные действия .

Часто вам нужно, чтобы один фрагмент связывался с другим, например, для изменения содержимого в зависимости от события пользователя. Вся связь фрагмента с фрагментом осуществляется через связанную активность. Два фрагмента никогда не должны общаться напрямую.

Эдвард Брей
источник
1
как насчет внутренних фрагментов, т. е. как фрагмент внутри другого фрагмента должен сообщаться с фрагментом хоста
Рави
@Ravi: каждый фрагмент должен связываться с действием, которое является общим для всех фрагментов, вызывая getActivity () .
Эдвард Брей
1
@Chris: если фрагментам требуется постоянное взаимодействие, определите интерфейс для каждого соответствующего фрагмента, который нужно реализовать. Работа задания ограничивается предоставлением фрагментов с указателями интерфейса на их фрагменты. После этого фрагменты могут безопасно общаться «напрямую» через интерфейсы.
Эдвард Брей
3
Я думаю, что, поскольку использование фрагментов было расширено, первоначальная идея не использовать прямую связь фрагментов разрушается. Например, в панели навигации каждый непосредственный дочерний фрагмент действия грубо действует как действие. Таким образом, наличие фрагмента, такого как диалоговый фрагмент, связанного через деятельность, вредит читабельности / гибкости IMO. На самом деле, похоже, не существует какого-либо приятного способа инкапсуляции фрагмента диалога, чтобы он мог работать с действиями и фрагментами повторно.
Сэм
16
Я знаю, что это старо, но в случае, если кто-то еще придет сюда, я чувствую, что случай, о котором говорится в этом документе, не применяется, когда один фрагмент «владеет» логикой, которая используется для определения создания и управления DialogFragment. Это своего рода странно - создавать связки между фрагментом и действием, когда действие даже не уверено, почему создается диалог или при каких условиях он должен быть отклонен. Кроме того, DialogFragment очень прост и существует только для того, чтобы уведомить пользователя и потенциально получить ответ.
Крис
12

Вы должны определить interfaceв своем фрагменте класс и реализовать этот интерфейс в родительской активности. Подробности изложены здесь http://developer.android.com/guide/components/fragments.html#EventCallbacks . Код будет выглядеть примерно так:

Фрагмент:

public static class FragmentA extends DialogFragment {

    OnArticleSelectedListener mListener;

    // Container Activity must implement this interface
    public interface OnArticleSelectedListener {
        public void onArticleSelected(Uri articleUri);
    }

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        try {
            mListener = (OnArticleSelectedListener) activity;
        } catch (ClassCastException e) {
            throw new ClassCastException(activity.toString() + " must implement OnArticleSelectedListener");
        }
    }
}

Деятельность:

public class MyActivity extends Activity implements OnArticleSelectedListener{

    ...
    @Override
    public void onArticleSelected(Uri articleUri){

    }
    ...
}
Джеймс МакКракен
источник
1
Я думаю, что вы просмотрели документы слишком быстро. Оба эти сегмента кода есть, FragmentAи он предполагает, что активность - это OnArticleSelectedListenerне Фрагмент, который его начал.
Eternalmatt
2
Я бы подумал, что ты пытаешься сделать плохой практикой. Руководства по Android рекомендуют, чтобы весь обмен фрагментами происходил через действие (согласно developer.android.com/training/basics/fragments/… ). Если вы действительно хотите, чтобы все это обрабатывалось внутри, MyFragmentвы можете перейти к использованию обычногоAlertDialog
Джеймс МакКракен
1
Я думаю, что проблема с тем, чтобы фрагменты общались непосредственно друг с другом, заключается в том, что в некоторых макетах не все фрагменты могут быть загружены, и, как они показывают в примере, может потребоваться переключить фрагмент. Я не думаю, что это беспокойство справедливо, когда речь идет о запуске фрагмента диалога из фрагмента.
Я реализовал это для моей деятельности. Вопрос: может ли это решение быть расширено так, чтобы фрагмент мог создать экземпляр этого диалога?
Билл Мот
1
Это хорошая практика с архитектурной точки зрения, и как таковой должен быть принятый ответ. Использование onActivityResult приводит к архитектуре спагетти
Bruno Carrier
4

Правильный способ установить слушателя на фрагмент, установив его, когда он присоединен . У меня была проблема, что onAttachFragment () никогда не вызывался. После некоторого расследования я понял, что я использовал getFragmentManager вместо getChildFragmentManager

Вот как я это делаю:

MyDialogFragment dialogFragment = MyDialogFragment.newInstance("title", "body");
dialogFragment.show(getChildFragmentManager(), "SOME_DIALOG");

Прикрепите его в onAttachFragment:

@Override
public void onAttachFragment(Fragment childFragment) {
    super.onAttachFragment(childFragment);

    if (childFragment instanceof MyDialogFragment) {
        MyDialogFragment dialog = (MyDialogFragment) childFragment;
        dialog.setListener(new MyDialogFragment.Listener() {
            @Override
            public void buttonClicked() {

            }
        });
    }
}
user1354603
источник
3

Согласно официальной документации:

Фрагмент # setTargetFragment

Необязательная цель для этого фрагмента. Это может быть использовано, например, если этот фрагмент запускается другим, и когда это будет сделано, он хочет вернуть результат первому. Установленная здесь цель сохраняется во всех экземплярах через FragmentManager # putFragment.

Фрагмент # getTargetFragment

Возвращает целевой фрагмент, установленный setTargetFragment (Fragment, int).

Так что вы можете сделать это:

// In your fragment

public class MyFragment extends Fragment implements OnClickListener {
    private void showDialog() {
        DialogFragment dialogFrag = MyDialogFragment.newInstance(this);
        // Add this
        dialogFrag.setTargetFragment(this, 0);
        dialogFrag.show(getFragmentManager, null);
    }
    ...
}

// then

public class MyialogFragment extends DialogFragment {
    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        // Then get it
        Fragment fragment = getTargetFragment();
        if (fragment instanceof OnClickListener) {
            listener = (OnClickListener) fragment;
        } else {
            throw new RuntimeException("you must implement OnClickListener");
        }
    }
    ...
}
SUPERYAO
источник
ты можешь объяснить свое?
Yilmaz
В этом случае нам нужно передать ссылку «MyFragment» на «MyialogFragment», а «Fragment» предоставляет метод для этого. Я добавил описание официального документа, он должен сказать более четко, чем я.
SUPERYAO
2

Я столкнулся с аналогичной проблемой. Решение, которое я обнаружил, было:

  1. Объявите интерфейс в вашем DialogFragment так же, как это объяснил Джеймс МакКракен выше.

  2. Реализуйте интерфейс в своей деятельности (не фрагмент! Это не очень хорошая практика).

  3. Из метода обратного вызова в вашей деятельности вызовите требуемую публичную функцию в вашем фрагменте, которая выполняет ту работу, которую вы хотите сделать.

Таким образом, это становится двухступенчатым процессом: DialogFragment -> Activity и затем Activity -> Fragment

Шайлеш Мани Пандей
источник
1

Я получаю результат Fragment DashboardLiveWall (вызывающий фрагмент) из Fragment LiveWallFilterFragment (получающий фрагмент) Как это ...

 LiveWallFilterFragment filterFragment = LiveWallFilterFragment.newInstance(DashboardLiveWall.this ,"");

 getActivity().getSupportFragmentManager().beginTransaction(). 
 add(R.id.frame_container, filterFragment).addToBackStack("").commit();

где

public static LiveWallFilterFragment newInstance(Fragment targetFragment,String anyDummyData) {
        LiveWallFilterFragment fragment = new LiveWallFilterFragment();
        Bundle args = new Bundle();
        args.putString("dummyKey",anyDummyData);
        fragment.setArguments(args);

        if(targetFragment != null)
            fragment.setTargetFragment(targetFragment, KeyConst.LIVE_WALL_FILTER_RESULT);
        return fragment;
    }

setResult вернуться к вызывающему фрагменту, как

private void setResult(boolean flag) {
        if (getTargetFragment() != null) {
            Bundle bundle = new Bundle();
            bundle.putBoolean("isWorkDone", flag);
            Intent mIntent = new Intent();
            mIntent.putExtras(bundle);
            getTargetFragment().onActivityResult(getTargetRequestCode(),
                    Activity.RESULT_OK, mIntent);
        }
    }

onActivityResult

@Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if (resultCode == Activity.RESULT_OK) {
            if (requestCode == KeyConst.LIVE_WALL_FILTER_RESULT) {

                Bundle bundle = data.getExtras();
                if (bundle != null) {

                    boolean isReset = bundle.getBoolean("isWorkDone");
                    if (isReset) {

                    } else {
                    }
                }
            }
        }
    }
Zar E Ahmer
источник
1

Обновлено:

Я создал библиотеку, основанную на моем гист-коде, который генерирует их для вас с помощью @CallbackFragmentи @Callback.

https://github.com/zeroarst/callbackfragment .

И пример дает вам пример, который отправляет обратный вызов от фрагмента к другому фрагменту.

Старый ответ:

Я сделал BaseCallbackFragmentи аннотацию @FragmentCallback. В настоящее время он расширяется Fragment, вы можете изменить его DialogFragmentи работать. Он проверяет реализации в следующем порядке: getTargetFragment ()> getParentFragment ()> context (activity).

Затем вам просто нужно расширить его и объявить ваши интерфейсы в вашем фрагменте и дать ему аннотацию, а базовый фрагмент сделает все остальное. В аннотации также есть параметр, mandatoryпозволяющий вам определить, хотите ли вы заставить фрагмент реализовать обратный вызов.

public class EchoFragment extends BaseCallbackFragment {

    private FragmentInteractionListener mListener;

    @FragmentCallback
    public interface FragmentInteractionListener {
        void onEcho(EchoFragment fragment, String echo);
    }
}

https://gist.github.com/zeroarst/3b3f32092d58698a4568cdb0919c9a93

Arst
источник
1

Парни Kotlin здесь мы идем!

Итак, проблема у нас в том, что мы создали действие, MainActivityна этом действии мы создали фрагмент, FragmentAи теперь мы хотим создать фрагмент диалога поверх FragmentAвызова его FragmentB. Как мы можем получить результаты FragmentBобратно FragmentAбез прохождения MainActivity?

Примечание:

  1. FragmentAэто дочерний фрагмент MainActivity. Для управления созданными фрагментами FragmentAмы будем использовать childFragmentManagerэто!
  2. FragmentAявляется родительским фрагментом FragmentB, для доступа FragmentAизнутри FragmentBмы будем использовать parenFragment.

Сказав это, внутри FragmentA,

class FragmentA : Fragment(), UpdateNameListener {
    override fun onSave(name: String) {
        toast("Running save with $name")
    }

    // call this function somewhere in a clickListener perhaps
    private fun startUpdateNameDialog() {
        FragmentB().show(childFragmentManager, "started name dialog")
    }
}

Вот фрагмент диалога FragmentB.

class FragmentB : DialogFragment() {

    private lateinit var listener: UpdateNameListener

    override fun onAttach(context: Context) {
        super.onAttach(context)
        try {
            listener = parentFragment as UpdateNameListener
        } catch (e: ClassCastException) {
            throw ClassCastException("$context must implement UpdateNameListener")
        }
    }

    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
        return activity?.let {
            val builder = AlertDialog.Builder(it)
            val binding = UpdateNameDialogFragmentBinding.inflate(LayoutInflater.from(context))
            binding.btnSave.setOnClickListener {
                val name = binding.name.text.toString()
                listener.onSave(name)
                dismiss()
            }
            builder.setView(binding.root)
            return builder.create()
        } ?: throw IllegalStateException("Activity can not be null")
    }
}

Вот интерфейс, связывающий их.

interface UpdateNameListener {
    fun onSave(name: String)
}

Вот и все.

Ssenyonjo
источник
1
Я следовал этому документу: developer.android.com/guide/topics/ui/dialogs, и он не работал. Огромное спасибо. Я надеюсь, что этот родительский фрагмент работает так, как и ожидалось каждый раз :)
UmutTekin
1
не забудьте установить слушателя на null внутри onDetach :)
BekaBot
@BekaBot Спасибо за комментарий. Я провел некоторое исследование, и кажется, что нет необходимости закрывать слушателей. stackoverflow.com/a/37031951/10030693
Ссенёнхо
0

Я решил это элегантно с помощью RxAndroid. Получите наблюдателя в конструкторе DialogFragment и запишите значение наблюдаемого и нажмите значение при вызове обратного вызова. Затем в своем фрагменте создайте внутренний класс Observer, создайте экземпляр и передайте его в конструктор DialogFragment. Я использовал WeakReference в обозревателе, чтобы избежать утечек памяти. Вот код:

BaseDialogFragment.java

import java.lang.ref.WeakReference;

import io.reactivex.Observer;

public class BaseDialogFragment<O> extends DialogFragment {

    protected WeakReference<Observer<O>> observerRef;

    protected BaseDialogFragment(Observer<O> observer) {
        this.observerRef = new WeakReference<>(observer);
   }

    protected Observer<O> getObserver() {
    return observerRef.get();
    }
}

DatePickerFragment.java

public class DatePickerFragment extends BaseDialogFragment<Integer>
    implements DatePickerDialog.OnDateSetListener {


public DatePickerFragment(Observer<Integer> observer) {
    super(observer);
}

@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
    return new DatePickerDialog(getActivity(), this, year, month, day);
}

@Override
public void onDateSet(DatePicker view, int year, int month, int dayOfMonth) {
        if (getObserver() != null) {
            Observable.just(month).subscribe(getObserver());
        }
    }
}

MyFragment.java

//Show the dialog fragment when the button is clicked
@OnClick(R.id.btn_date)
void onDateClick() {
    DialogFragment newFragment = new DatePickerFragment(new OnDateSelectedObserver());
    newFragment.show(getFragmentManager(), "datePicker");
}
 //Observer inner class
 private class OnDateSelectedObserver implements Observer<Integer> {

    @Override
    public void onSubscribe(Disposable d) {

    }

    @Override
    public void onNext(Integer integer) {
       //Here you invoke the logic

    }

    @Override
    public void onError(Throwable e) {

    }

    @Override
    public void onComplete() {

    }
}

Вы можете увидеть исходный код здесь: https://github.com/andresuarezz26/carpoolingapp

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