Retorno de chamada para um fragmento de um DialogFragment

159

Pergunta: Como se cria um retorno de chamada de um DialogFragment para outro Fragmento. No meu caso, a Atividade envolvida deve desconhecer completamente o DialogFragment.

Considere que eu tenho

public class MyFragment extends Fragment implements OnClickListener

Então, em algum momento eu poderia fazer

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

Onde se parece o MyDialogFragment

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

Mas não há garantia de que o ouvinte estará presente se o DialogFragment for pausado e retomado durante o seu ciclo de vida. As únicas garantias em um fragmento são aquelas transmitidas por meio de um pacote via setArguments e getArguments.

Existe uma maneira de referenciar a atividade se ela deve ser o ouvinte:

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

Mas não quero que a Atividade ouça eventos, preciso de um Fragmento. Realmente, poderia ser qualquer objeto Java que implemente o OnClickListener.

Considere o exemplo concreto de um fragmento que apresenta um AlertDialog via DialogFragment. Possui botões Sim / Não. Como posso enviar esses pressionamentos de botão de volta ao fragmento que o criou?

eternalmatt
fonte
Você mencionou "Mas não há garantia de que o ouvinte estará presente se o DialogFragment for pausado e retomado durante o ciclo de vida". Eu pensei que o estado do fragmento fosse destruído durante onDestroy ()? Você deve estar certo, mas estou um pouco confuso sobre como usar o estado Fragment agora. Como reproduzo o problema que você mencionou, o ouvinte não está por perto?
30514 Sean
Não vejo por que você não pode simplesmente usar OnClickListener listener = (OnClickListener) getParentFragment();no DialogFragment e seu Fragmento principal implementa a interface como você fez originalmente.
Kiruwka
Aqui é uma resposta a uma pergunta não relacionada mas mostra-lhe como isso é feito de forma limpa stackoverflow.com/questions/28620026/...
user2288580

Respostas:

190

A atividade envolvida desconhece completamente o DialogFragment.

Classe de fragmento:

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;
        }
    }
}

}

Classe 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();
}
}
Piotr Ślesarew
fonte
100
Eu acho que a chave aqui é setTargetFragmente getTargetFragment. O uso de onActivityResulté um pouco incerto. Provavelmente, seria melhor declarar seu próprio método específico no chamador do Fragment e usá-lo, em vez de redefinir o propósito em ActivityResult. Mas é tudo semântica nesse ponto.
eternalmatt
2
variável de nível de pilha não é usada?
Nome de exibição
6
isso sobreviverá a uma rotação de mudança de configuração?
Maxrunner
3
Usou isso. Notas: o nível da pilha não era necessário para sobreviver à rotação ou ao sono. Em vez de onActivityResult, meu fragmento implementa DialogResultHandler # handleDialogResult (uma interface que eu criei). @myCode, seria super útil mostrar um valor selecionado da caixa de diálogo sendo adicionado ao Intent e depois ler dentro do seu onActivityResult. As intenções não são claras para iniciantes.
21914 Chris Betti
7
@ eternalmatt, sua objeção é totalmente razoável, mas acho que o valor de onActivityResult () é que ele existe em qualquer fragmento, portanto qualquer fragmento pode ser usado como pai. Se você criar sua própria interface e o Fragmento pai a implementar, o filho poderá ser usado apenas com os pais que implementam essa interface. Acoplar o filho a essa interface pode voltar a assombrá-lo se você começar a usá-lo mais amplamente mais tarde. O uso da interface onActivityResult () "interna" não requer acoplamento adicional, permitindo uma flexibilidade um pouco maior.
Dalbergia
78

A solução TargetFragment não parece a melhor opção para fragmentos de diálogo, pois pode ser criada IllegalStateExceptionapós a destruição e a recriação do aplicativo. Nesse caso, FragmentManagernão foi possível encontrar o fragmento de destino e você receberá uma IllegalStateExceptionmensagem como esta:

"O fragmento não existe mais para o android principal: target_state: index 1"

Parece que Fragment#setTargetFragment()não se destina à comunicação entre um fragmento filho e pai, mas para a comunicação entre fragmentos de irmãos.

Portanto, uma maneira alternativa é criar fragmentos de diálogo como este usando o ChildFragmentManagerfragmento pai, em vez de usar as atividades FragmentManager:

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

E, usando uma Interface, no onCreatemétodo da, DialogFragmentvocê pode obter o fragmento pai:

@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");
    }
}

Só resta chamar o método de retorno de chamada após essas etapas.

Para obter mais informações sobre o problema, acesse o link: https://code.google.com/p/android/issues/detail?id=54520

Oguz Ozcan
fonte
2
Isso também funciona com o onAttach (contexto Contexto) introduziu adicionado na api 23.
Papai Teclado
1
ESTA deve ser a resposta aceita. A resposta atual aceita é de buggy e os fragmentos não devem ser usados ​​assim.
NecipAllef
3
@AhmadFadli O problema aqui é obter o contexto certo (pai) para a comunicação entre os fragmentos. Se você usar o fragmento de diálogo como filho da atividade, não haverá confusão. FragmentManager of Activity e getActivity () para recuperar o retorno de chamada é suficiente.
Oguz Ozcan
1
Essa deve ser a resposta aceita. É claramente explicado e detalhado, não é apenas um código lançado para as pessoas.
Vince
1
@lukecross ParentFragment é o fragmento que cria o DialogFragment (aquele que chama show ()) Mas parece que childFragmentManager não sobrevive à reconfiguração / rotações de tela ...
iwat0qs
34

Eu segui estes passos simples para fazer essas coisas.

  1. Crie interface como DialogFragmentCallbackInterfacecom algum método como callBackMethod(Object data). Qual você chamaria para passar dados.
  2. Agora você pode implementar DialogFragmentCallbackInterfaceinterface em seu fragmento comoMyFragment implements DialogFragmentCallbackInterface
  3. No momento da DialogFragmentcriação, defina seu fragmento de chamada MyFragmentcomo fragmento de destino que criou, DialogFragmentuse myDialogFragment.setTargetFragment(this, 0)check setTargetFragment (Fragmento de fragmento, int requestCode)

    MyDialogFragment dialogFrag = new MyDialogFragment();
    dialogFrag.setTargetFragment(this, 1); 
  4. Coloque seu objeto de fragmento de destino no seu DialogFragmentchamando getTargetFragment()e faça a DialogFragmentCallbackInterfaceconversão para .Agora você pode usar essa interface para enviar dados para o seu fragmento.

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

    É tudo feito! apenas verifique se você implementou essa interface no seu fragmento.

Vijay Vankhede
fonte
4
Essa deve ser a melhor resposta. Ótima resposta.
Md. Sajedul Karim
Certifique-se também de usar o mesmo gerenciador de fragmentos para os fragmentos de origem e de destino, caso contrário, getTargetFragment não funcionará. Portanto, se você estiver usando childFragmentManager, ele não funcionará, pois o fragmento de origem não foi confirmado pelo gerenciador de fragmentos filho. É melhor pensar nesses dois fragmentos como fragmentos de irmãos, em vez de fragmentos de pai / filho.
Thupten
Francamente, é melhor usar apenas o padrão de fragmento de destino ao se comunicar entre dois fragmentos de irmão. Por não ter um ouvinte, você evita o vazamento acidental do fragmento1 no fragmento2. Ao usar o fragmento de destino, não use ouvinte / retorno de chamada. Use apenas onActivityResult(request code, resultcode, intent)para retornar o resultado ao fragmento1. Do fragmento1 setTargetFragment()e do fragmento2, use getTargetFragment(). Ao usar o fragmento pai / filho em um fragmento ou a atividade em um fragmento, você pode usar o ouvinte ou o retorno de chamada, pois não há perigo de vazar o pai no fragmento filho.
Thupten
@ Quando você diz "vazamento", quer dizer vazamento de memória ou "detalhes de implementação de vazamentos"? Não acho que a resposta de Vijay vaze memória mais do que usar onActivityResulty para obter valor de retorno. Ambos os padrões manterão a referência ao fragmento alvo. Se você quer dizer vazar detalhes da implementação, acho que o padrão dele é ainda melhor do que no onActivityResult. O método de retorno de chamada é explícito (se nomeado corretamente). Se tudo o que você voltar estiver OK e CANCELADO, o primeiro fragmento precisará interpretar o que isso significa.
tir38 31/03
34

Talvez um pouco tarde, mas possa ajudar outras pessoas com a mesma pergunta que eu.

Você pode usar setTargetFragmenton Dialogantes de mostrar, e na caixa de diálogo que você pode chamar getTargetFragmentpara obter a referência.

Rui
fonte
Aqui está uma resposta para outra pergunta, mas ela também se aplica à sua pergunta e é uma solução limpa: stackoverflow.com/questions/28620026/…
user2288580
IllegalStateException para mim
luke cross
19

O guia Comunicação com outros fragmentos diz que os fragmentos devem se comunicar por meio da atividade associada .

Muitas vezes, você deseja que um fragmento se comunique com outro, por exemplo, para alterar o conteúdo com base em um evento do usuário. Toda a comunicação entre fragmentos é feita através da atividade associada. Dois fragmentos nunca devem se comunicar diretamente.

Edward Brey
fonte
1
que sobre fragmentos internos ou seja, como deve um fragmento dentro de outro fragmento de comunicar à fragmento de acolhimento
Ravi
@ Ravi: Cada fragmento deve se comunicar com a atividade que é comum a todos os fragmentos, chamando getActivity () .
Edward Brey 28/11
1
@ Chris: Se os fragmentos precisarem de comunicação contínua, defina uma interface para cada fragmento apropriado a ser implementado. O trabalho da atividade é limitado a fornecer fragmentos com ponteiros de interface para seus fragmentos de contrapartida. Depois disso, os fragmentos podem se comunicar com segurança "diretamente" através das interfaces.
Edward Brey 17/02
3
Penso que, à medida que os usos de fragmentos foram expandidos, a idéia original de não usar a comunicação direta por fragmentos se desintegra. Por exemplo, em uma gaveta de navegação, cada fragmento filho imediato da atividade atua aproximadamente como uma atividade. Portanto, um fragmento como um fragmento de diálogo se comunique por meio da atividade prejudica a legibilidade / flexibilidade da IMO. De fato, não parece haver uma maneira agradável de encapsular o fragmento de diálogo para permitir que ele trabalhe com atividades e fragmentos de maneira reutilizável.
Sam
16
Sei que isso é antigo, mas no caso de mais alguém chegar aqui, sinto que o caso mencionado nesse documento não se aplica quando um fragmento "possui" a lógica usada para determinar a criação e o gerenciamento do DialogFragment. É meio estranho criar um monte de conexões do fragmento para a atividade quando a atividade nem sabe ao certo por que um Diálogo está sendo criado ou sob quais condições ele deve ser descartado. Além disso, o DialogFragment é super simples e existe apenas para notificar o usuário e potencialmente obter uma resposta.
Chris
12

Você deve definir um interfaceem sua classe de fragmento e implementar essa interface em sua atividade pai. Os detalhes estão descritos aqui http://developer.android.com/guide/components/fragments.html#EventCallbacks . O código seria semelhante a:

Fragmento:

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");
        }
    }
}

Atividade:

public class MyActivity extends Activity implements OnArticleSelectedListener{

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

    }
    ...
}
James McCracken
fonte
1
Acho que você examinou os documentos muito rapidamente. Ambos os segmentos de código são FragmentAe ele está assumindo que uma atividade é um OnArticleSelectedListener, não o fragmento que o iniciou.
eternalmatt 6/12/12
2
Eu consideraria o que você está tentando fazer com más práticas. As diretrizes do Android recomendam que toda a comunicação entre fragmentos ocorra através da atividade (por developer.android.com/training/basics/fragments/… ). Se você realmente quer tudo para ser tratada dentro MyFragmentvocê pode querer passar a usar um regularAlertDialog
James McCracken
1
Eu acho que a preocupação de ter os fragmentos conversando diretamente é que, em alguns layouts, nem todos os fragmentos podem ser carregados e, como mostram no exemplo, pode ser necessário alternar o fragmento. Eu não acho que essa preocupação seja válida quando se fala em iniciar um fragmento de diálogo a partir de um fragmento.
Eu tenho isso implementado para minhas atividades. Pergunta: esta solução pode ser estendida para que um fragmento possa instanciar esse diálogo?
Bill Mote
1
Essa é uma boa prática do ponto de vista arquitetural e, como tal, deve ser a resposta aceita. Usando leads onActivityResult à arquitetura spaghetti
de Bruno portador
4

A maneira correta de definir um ouvinte para um fragmento é definindo-o quando está anexado . O problema que tive foi que onAttachFragment () nunca foi chamado. Após alguma investigação, percebi que estava usando getFragmentManager em vez de getChildFragmentManager

Aqui está como eu faço isso:

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

Anexe-o no 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
fonte
3

De acordo com a documentação oficial:

Fragmento # setTargetFragment

Alvo opcional para este fragmento. Isso pode ser usado, por exemplo, se esse fragmento estiver sendo iniciado por outro e, quando terminar, quiser retornar um resultado ao primeiro. O destino definido aqui é retido nas instâncias via FragmentManager # putFragment.

Fragmento # getTargetFragment

Retorne o fragmento de destino definido por setTargetFragment (Fragment, int).

Então você pode fazer isso:

// 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
fonte
você pode explicar o seu?
Yilmaz
Nesse caso, precisamos passar a referência "MyFragment" para "MyialogFragment" e "Fragment" fornece o método para fazê-lo. Eu adicionei a descrição do documento oficial, deveria dizer mais claramente do que eu.
SUPERYAO 17/08/19
2

Eu estava enfrentando um problema semelhante. A solução que eu descobri foi:

  1. Declare uma interface no seu DialogFragment, como James McCracken explicou acima.

  2. Implemente a interface em sua atividade (não fragmento! Essa não é uma boa prática).

  3. No método de retorno de chamada em sua atividade, chame uma função pública necessária em seu fragmento, que executa o trabalho que você deseja executar.

Assim, torna-se um processo de duas etapas: DialogFragment -> Activity e depois Activity -> Fragment

Shailesh Mani Pandey
fonte
1

Estou obtendo resultado para Fragment DashboardLiveWall (fragmento de chamada) do Fragment LiveWallFilterFragment (fragmento de recebimento) Assim ...

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

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

Onde

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 de volta à chamada fragmento como

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
fonte
1

Atualizada:

Eu criei uma biblioteca baseada no meu código de essência que gera as transmissões para você usando @CallbackFragmente @Callback.

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

E o exemplo fornece o exemplo que envia um retorno de chamada de um fragmento para outro fragmento.

Resposta antiga:

Eu fiz uma BaseCallbackFragmentanotação @FragmentCallback. Atualmente Fragment, ele se estende , você pode alterá-lo para DialogFragmente irá funcionar. Ele verifica as implementações na seguinte ordem: getTargetFragment ()> getParentFragment ()> context (activity).

Então você só precisa estendê-lo, declarar suas interfaces no seu fragmento e fornecer a anotação, e o fragmento base fará o resto. A anotação também possui um parâmetro mandatorypara você determinar se deseja forçar o fragmento a implementar o retorno de chamada.

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
fonte
1

Pessoal Kotlin aqui vamos nós!

Portanto, o problema que temos é que criamos uma atividade, MainActivitynessa atividade criamos um fragmento FragmentAe agora queremos criar um fragmento de diálogo em cima de FragmentAchamá-lo FragmentB. Como obtemos os resultados de FragmentBvolta FragmentAsem passar MainActivity?

Nota:

  1. FragmentAé um fragmento filho de MainActivity. Para gerenciar fragmentos criados FragmentA, usaremos o childFragmentManagerque faz isso!
  2. FragmentAé um fragmento pai de FragmentB, para acessar FragmentAde dentro FragmentB, usaremos parenFragment.

Dito isto, por dentro 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")
    }
}

Aqui está o fragmento de diálogo 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")
    }
}

Aqui está a interface que liga os dois.

interface UpdateNameListener {
    fun onSave(name: String)
}

É isso aí.

Ssenyonjo
fonte
1
Eu segui este documento: developer.android.com/guide/topics/ui/dialogs e não funcionou. Muito obrigado. Espero que essa coisa de parentfragment funcione como esperado todas as vezes :)
UmutTekin
1
não se esqueça de definir o ouvinte como nulo dentro do onDetach :)
BekaBot 07/07
@BekaBot Obrigado pelo comentário. Eu fiz algumas pesquisas e parece que não é necessário fechar os ouvintes. stackoverflow.com/a/37031951/10030693
Ssenyonjo
0

Eu resolvi isso de uma maneira elegante com o RxAndroid. Receba um observador no construtor do DialogFragment e assine o observable e empurre o valor quando o retorno de chamada estiver sendo chamado. Em seu Fragmento, crie uma classe interna do Observador, crie uma instância e passe-a no construtor do DialogFragment. Usei WeakReference no observador para evitar vazamentos de memória. Aqui está o código:

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

    }
}

Você pode ver o código fonte aqui: https://github.com/andresuarezz26/carpoolapp

Gerardo Suarez
fonte
1
O engraçado do Android é que existe algo chamado ciclo de vida. O fragmento de base ou de diálogo precisa preservar o estado (e sua conexão) durante os eventos do ciclo de vida. Retornos de chamada ou observadores não podem ser serializados e, portanto, têm o mesmo problema aqui.
GDanger # 29/19