Richiamata su un frammento da un DialogFragment


159

Domanda: come si crea un callback da un DialogFragment a un altro frammento. Nel mio caso, l'attività in questione dovrebbe essere completamente inconsapevole di DialogFragment.

Considera che ho

public class MyFragment extends Fragment implements OnClickListener

Poi ad un certo punto ho potuto fare

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

Dove si presenta MyDialogFragment

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

Ma non vi è alcuna garanzia che l'ascoltatore sarà presente se DialogFragment fa una pausa e riprende durante il suo ciclo di vita. Le uniche garanzie in un frammento sono quelle passate attraverso un pacchetto tramite setArguments e getArguments.

C'è un modo per fare riferimento all'attività se dovrebbe essere l'ascoltatore:

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

Ma non voglio che l'attività ascolti gli eventi, ho bisogno di un frammento. In realtà, potrebbe essere qualsiasi oggetto Java che implementa OnClickListener.

Considera l'esempio concreto di un frammento che presenta un AlertDialog tramite DialogFragment. Ha pulsanti Sì / No. Come posso inviare queste pressioni del pulsante al frammento che lo ha creato?


Hai citato "Ma non vi è alcuna garanzia che l'ascoltatore sarà presente se DialogFragment fa una pausa e riprende durante il suo ciclo di vita." Pensavo che lo stato del frammento venisse distrutto durante onDestroy ()? Devi avere ragione, ma sono solo un po 'confuso su come usare lo stato del frammento ora. Come riproduco il problema che hai citato, l'ascoltatore non è presente?
Sean,

Non vedo perché non puoi semplicemente usare OnClickListener listener = (OnClickListener) getParentFragment();DialogFragment e il tuo frammento principale implementa l'interfaccia come facevi in ​​origine.
Kiruwka,

Ecco una risposta a una domanda non correlata, ma ti mostra come farlo in modo pulito stackoverflow.com/questions/28620026/...
user2288580

Risposte:


190

L'attività coinvolta è completamente inconsapevole di DialogFragment.

Classe di frammento:

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

100
Penso che la chiave qui sia setTargetFragmente getTargetFragment. L'uso di onActivityResultè un po 'poco chiaro. Probabilmente sarebbe meglio dichiarare il proprio metodo specifico nel chiamante Frammento e usarlo, invece di riutilizzare OnActivityResult. Ma è tutta semantica a quel punto.
eternalmatt,

2
la variabile a livello di stack non viene utilizzata?
Visualizza nome

6
sopravviverà a un cambio di configurazione - rotazione?
Maxrunner,

3
Usato questo. Note: il livello dello stack non era necessario per sopravvivere alla rotazione o al sonno. Invece di onActivityResult, il mio frammento implementa DialogResultHandler # handleDialogResult (un'interfaccia che ho creato). @myCode, sarebbe di grande aiuto per mostrare una finestra di dialogo scelta valore che viene aggiunto all'Intent, e quindi leggere all'interno di onActivityResult. Gli intenti non sono chiari per i principianti.
Chris Betti,

7
@eternalmatt, la tua obiezione è del tutto ragionevole, ma penso che il valore di onActivityResult () sia che è garantito che esiste su qualsiasi frammento, quindi qualsiasi frammento può essere usato come genitore. Se si crea la propria interfaccia e la si implementa con il frammento padre, il figlio può essere utilizzato solo con i genitori che implementano tale interfaccia. Associare il bambino a quell'interfaccia potrebbe tornare a perseguitarti se inizi a usarlo più ampiamente in seguito. L'uso dell'interfaccia onActivityResult () "integrata" non richiede alcun accoppiamento aggiuntivo, quindi ti offre un po 'più di flessibilità.
Dalbergia,

78

La soluzione TargetFragment non sembra l'opzione migliore per i frammenti delle finestre di dialogo perché può essere creata IllegalStateExceptiondopo che l'applicazione è stata distrutta e ricreata. In questo caso FragmentManagernon è stato possibile trovare il frammento di destinazione e verrà visualizzato IllegalStateExceptionun messaggio come questo:

"Il frammento non esiste più per la chiave android: target_state: indice 1"

Sembra che Fragment#setTargetFragment() non sia pensato per la comunicazione tra un frammento figlio e genitore, ma piuttosto per la comunicazione tra frammenti fratello / sorella.

Quindi un modo alternativo è quello di creare frammenti di dialogo come questo usando il ChildFragmentManagerframmento genitore, piuttosto che usando le attività FragmentManager:

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

E usando un'interfaccia, nel onCreatemetodo del DialogFragmentpuoi ottenere il frammento genitore:

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

L'unica cosa rimasta è chiamare il metodo di callback dopo questi passaggi.

Per ulteriori informazioni sul problema, puoi consultare il link: https://code.google.com/p/android/issues/detail?id=54520


2
Questo funziona anche con onAttach (contesto di contesto) introdotto aggiunto in api 23.
Santa Teclado,

1
QUESTA dovrebbe essere la risposta accettata. La risposta attualmente accettata è errata e i frammenti non devono essere usati in questo modo.
NecipAllef,

3
@AhmadFadli Il problema qui è quello di ottenere il giusto contesto (genitore) per la comunicazione tra i frammenti. Se utilizzerai il frammento del dialogo come figlio dell'attività, allora non ci dovrebbe essere alcuna confusione. FragmentManager di Activity e getActivity () per recuperare il callback è sufficiente.
Oguz Ozcan,

1
Questa dovrebbe essere la risposta accettata. È chiaramente spiegato e dettagliato, non è solo un codice lanciato alle persone.
Vince l'

1
@lukecross ParentFragment è il frammento che crea il DialogFragment (quello che chiama show ()) Ma sembra che childFragmentManager non sopravviva alla riconfigurazione / alle rotazioni dello schermo ...
iwat0qs

34

Ho seguito questi semplici passaggi per fare queste cose.

  1. Crea un'interfaccia simile DialogFragmentCallbackInterfacecon un metodo simile callBackMethod(Object data). Che chiameresti per passare i dati.
  2. Ora puoi implementare l' DialogFragmentCallbackInterfaceinterfaccia nel tuo frammento comeMyFragment implements DialogFragmentCallbackInterface
  3. Al momento della DialogFragmentcreazione impostare il frammento invocante MyFragmentcome frammento di destinazione che ha creato DialogFragmentutilizzare myDialogFragment.setTargetFragment(this, 0)check setTargetFragment (Fragment fragment, int requestCode)

    MyDialogFragment dialogFrag = new MyDialogFragment();
    dialogFrag.setTargetFragment(this, 1); 
  4. Ottieni il tuo oggetto frammento di destinazione nel tuo DialogFragmentchiamandolo getTargetFragment()e DialogFragmentCallbackInterfacelanciandolo su . Ora puoi utilizzare questa interfaccia per inviare dati al tuo frammento.

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

    È tutto fatto! assicurati di aver implementato questa interfaccia nel tuo frammento.


4
Questa dovrebbe essere la migliore risposta. Bella risposta.
Md. Sajedul Karim,

Assicurati anche di utilizzare lo stesso gestore di frammenti sia per i frammenti di origine che di destinazione, altrimenti getTargetFragment non funzionerà. Pertanto, se si utilizza childFragmentManager, non funzionerà poiché il frammento di origine non viene eseguito dal gestore di frammenti secondari. È meglio pensare a questi 2 frammenti come frammenti di fratelli piuttosto che come frammenti genitore / figlio.
Giovedì

Francamente, è meglio usare solo il modello di frammento di destinazione quando si comunica tra 2 frammento di pari livello. Non avendo un ascoltatore, eviti di perdere accidentalmente il frammento1 nel frammento2. Quando si utilizza il frammento di destinazione, non utilizzare listener / callback. Utilizzare solo onActivityResult(request code, resultcode, intent)per restituire il risultato al frammento1. Da fragment1 setTargetFragment()e da fragment2, utilizzare getTargetFragment(). Quando si utilizza il frammento genitore / figlio per frammentare o l'attività per frammentare, è possibile utilizzare il listener o il callback poiché non vi è alcun rischio di perdita del genitore nel frammento figlio.
Giovedì

@Thupten quando dici "perdita" intendi perdita di memoria o "dettagli di implementazione della perdita"? Non credo che la risposta di Vijay perderà memoria non più dell'utilizzo di onActivityResulty come valore di ritorno. Entrambi i modelli manterranno il riferimento al frammento di destinazione. Se vuoi dire che perdono i dettagli dell'implementazione, penso che il suo modello sia persino migliore di onActivityResult. Il metodo di callback è esplicito (se nominato correttamente). Se tutto ciò che ritorni è OK e ANNULLATO, il primo frammento deve interpretare cosa significano.
tir38

34

Forse un po 'in ritardo, ma può aiutare altre persone con la stessa domanda come ho fatto io.

È possibile utilizzare setTargetFragmentil Dialogprima di mostrare, e nel dialogo è possibile chiamare getTargetFragmentper ottenere il riferimento.


Ecco una risposta a un'altra domanda, ma si applica anche alla tua domanda, ed è una soluzione pulita: stackoverflow.com/questions/28620026/...
user2288580

IllegalStateException per me
luke cross

19

La guida Comunicare con altri frammenti afferma che i frammenti devono comunicare attraverso l'attività associata .

Spesso si desidera che un frammento comunichi con un altro, ad esempio per modificare il contenuto in base a un evento utente. Tutte le comunicazioni da frammento a frammento vengono effettuate tramite l'attività associata. Due frammenti non dovrebbero mai comunicare direttamente.


1
che dire dei frammenti interni, cioè come dovrebbe un frammento all'interno di un altro frammento comunicare al frammento ospite
Ravi,

@Ravi: ogni frammento deve comunicare con l'attività comune a tutti i frammenti chiamando getActivity () .
Edward Brey,

1
@Chris: se i frammenti necessitano di comunicazione continua, definire un'interfaccia per ciascun frammento appropriato da implementare. Il lavoro dell'attività si limita quindi a fornire frammenti con puntatori di interfaccia ai loro frammenti di controparte. Successivamente, i frammenti possono comunicare in modo sicuro "direttamente" tramite le interfacce.
Edward Brey,

3
Penso che man mano che gli usi dei frammenti sono stati ampliati, l'idea originale di non utilizzare la comunicazione diretta dei frammenti si interrompe. Ad esempio, in un riquadro di navigazione ogni frammento figlio immediato dell'attività agisce all'incirca come un'attività. Quindi avere un frammento come un dialogo dialogare attraverso l'attività danneggia la leggibilità / flessibilità dell'IMO. In effetti non sembra esserci alcun modo carino per incapsulare il dialogo per consentirgli di lavorare con attività e frammenti in modo riutilizzabile.
Sam

16
So che questo è vecchio, ma nel caso in cui venga qualcun altro qui, sento che il caso di cui al documento in questione non si applica quando un frammento "possiede" la logica utilizzata per determinare la creazione e la gestione di DialogFragment. È un po 'strano creare un sacco di connessioni dal frammento all'attività quando l'attività non è nemmeno sicura del motivo per cui una finestra di dialogo viene creata o in quali condizioni dovrebbe essere respinta. Inoltre DialogFragment è super semplice ed esiste solo per avvisare l'utente e potenzialmente ottenere una risposta.
Chris,

12

Dovresti definire un interfacenella tua classe di frammento e implementare tale interfaccia nella sua attività principale. I dettagli sono descritti qui http://developer.android.com/guide/components/fragments.html#EventCallbacks . Il codice sarebbe simile a:

Frammento:

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

Attività:

public class MyActivity extends Activity implements OnArticleSelectedListener{

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

    }
    ...
}

1
Penso che hai sfogliato i documenti troppo in fretta. Entrambi questi segmenti di codice sono FragmentAe sta supponendo che un'attività sia un OnArticleSelectedListener, non il frammento che l'ha iniziato.
eternalmatt,

2
Vorrei considerare cosa stai cercando di fare cattive pratiche. Le linee guida di Android raccomandano che tutte le comunicazioni da frammento a frammento avvengano attraverso l'attività (per developer.android.com/training/basics/fragments/… ). Se vuoi davvero che tutto sia gestito al MyFragmenttuo interno, potresti voler passare a un normale utilizzoAlertDialog
James McCracken,

1
Penso che la preoccupazione di far parlare i frammenti direttamente l'uno con l'altro sia che in alcuni layout non tutti i frammenti possano essere caricati e, come mostrano nell'esempio, potrebbe essere necessario cambiare il frammento. Non penso che la preoccupazione sia valida quando si parla di lanciare un frammento di dialogo da un frammento.

Ho implementato questo per le mie attività. Domanda: questa soluzione può essere estesa in modo tale che un frammento possa istanziare questa finestra di dialogo?
Bill Mote,

1
Questa è una buona pratica dal punto di vista architettonico e come tale dovrebbe essere la risposta accettata. L'uso di onActivityResult porta all'architettura degli spaghetti
Bruno Carrier,

4

Il modo corretto di impostare un ascoltatore su un frammento è impostandolo quando è collegato . Il problema che ho avuto è stato che onAttachFragment () non è mai stato chiamato. Dopo alcune indagini mi sono reso conto che stavo usando getFragmentManager invece di getChildFragmentManager

Ecco come lo faccio:

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

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

            }
        });
    }
}

3

Secondo la documentazione ufficiale:

Fragment # setTargetFragment

Target opzionale per questo frammento. Questo può essere usato, ad esempio, se questo frammento viene avviato da un altro e quando fatto vuole restituire un risultato al primo. Il target impostato qui viene mantenuto tra le istanze tramite FragmentManager # putFragment.

Fragment # getTargetFragment

Restituisce il frammento di destinazione impostato da setTargetFragment (Fragment, int).

Quindi puoi farlo:

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

puoi spiegare il tuo?
Yilmaz,

In questo caso, dobbiamo passare il riferimento "MyFragment" a "MyialogFragment" e "Frammento" fornisce il metodo per farlo. Ho aggiunto la descrizione del documento ufficiale, dovrebbe essere più chiaro di me.
SUPERYAO,

2

Stavo affrontando un problema simile. La soluzione che ho scoperto è stata:

  1. Dichiara un'interfaccia nel tuo DialogFragment proprio come James McCracken ha spiegato sopra.

  2. Implementa l'interfaccia nella tua attività (non frammento! Non è una buona pratica).

  3. Dal metodo di callback nella tua attività, chiama una funzione pubblica richiesta nel tuo frammento che fa il lavoro che vuoi fare.

Pertanto, diventa un processo in due fasi: DialogFragment -> Attività e quindi Attività -> Frammento


1

Sto ottenendo risultati per Fragment DashboardLiveWall (chiamando frammento) da Fragment LiveWallFilterFragment (ricevendo frammento) In questo modo ...

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

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

dove

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

setRisultare a chiamare il frammento come

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

1

aggiornato:

Ho creato una libreria basata sul mio codice gist che genera quei casting per te usando @CallbackFragmente @Callback.

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

E l'esempio ti dà l'esempio che invia un callback da un frammento a un altro frammento.

Vecchia risposta:

Ho fatto una BaseCallbackFragmente annotazione @FragmentCallback. Attualmente si estende Fragment, puoi cambiarlo inDialogFragment e funzionerà. Controlla le implementazioni con il seguente ordine: getTargetFragment ()> getParentFragment ()> context (activity).

Quindi devi solo estenderlo e dichiarare le tue interfacce nel tuo frammento e dargli l'annotazione, e il frammento di base farà il resto. L'annotazione ha anche un parametro mandatoryper determinare se si desidera forzare il frammento per implementare il callback.

public class EchoFragment extends BaseCallbackFragment {

    private FragmentInteractionListener mListener;

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

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


1

Ragazzi, eccoci qui!

Quindi il problema che abbiamo è che abbiamo creato un'attività, MainActivitysu quell'attività abbiamo creato un frammento, FragmentAe ora vogliamo creare un frammento di dialogo in cima a FragmentAchiamarlo FragmentB. Come possiamo ottenere i risultati da FragmentBindietro a FragmentAsenza passare MainActivity?

Nota:

  1. FragmentAè un frammento di bambino di MainActivity. Per gestire i frammenti creati in FragmentAuseremo ciò childFragmentManagerche lo fa!
  2. FragmentAè un frammento genitore di FragmentB, per accedere FragmentAdall'interno FragmentBuseremo parenFragment.

Detto questo, 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")
    }
}

Ecco il frammento del dialogo 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")
    }
}

Ecco l'interfaccia che collega i due.

interface UpdateNameListener {
    fun onSave(name: String)
}

Questo è tutto.


1
Ho seguito questo documento: developer.android.com/guide/topics/ui/dialogs e non ha funzionato. Grazie mille. Spero che questa cosa di parentfragment funzioni come previsto ogni volta :)
UmutTekin il

1
non dimenticare di impostare il listener su null all'interno di onDetach :)
BekaBot

@BekaBot Grazie per il commento. Ho fatto alcune ricerche e sembra che non sia necessario chiudere gli ascoltatori. stackoverflow.com/a/37031951/10030693
Ssenyonjo

0

L'ho risolto in modo elegante con RxAndroid. Ricevi un osservatore nel costruttore di DialogFragment e suscita in osservabile e spinge il valore quando viene chiamato il callback. Quindi, nel tuo frammento, crea una classe interna di Observer, crea un'istanza e passala nel costruttore di DialogFragment. Ho usato WeakReference nell'osservatore per evitare perdite di memoria. Ecco il codice:

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

    }
}

Puoi vedere il codice sorgente qui: https://github.com/andresuarezz26/carpoolingapp


1
La cosa divertente di Android è che c'è qualcosa chiamato il ciclo di vita. Il frammento di base o il frammento della finestra di dialogo devono essere in grado di preservare lo stato (e la loro connessione) durante gli eventi del ciclo di vita. I callback o gli osservatori non possono essere serializzati e quindi hanno lo stesso problema qui.
GDanger
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.