Devolución de llamada a un fragmento desde un fragmento de diálogo

159

Pregunta: ¿Cómo se crea una devolución de llamada de un DialogFragment a otro Fragment? En mi caso, la actividad involucrada debe ser completamente inconsciente del DialogFragment.

Considera que tengo

public class MyFragment extends Fragment implements OnClickListener

Entonces en algún momento podría hacer

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

Donde se ve MyDialogFragment

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

Pero no hay garantía de que el oyente estará presente si DialogFragment hace una pausa y se reanuda durante su ciclo de vida. Las únicas garantías en un Fragmento son las que se pasan a través de un Paquete a través de setArguments y getArguments.

Hay una manera de hacer referencia a la actividad si debería ser el oyente:

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

Pero no quiero que la Actividad escuche eventos, necesito un Fragmento. Realmente, podría ser cualquier objeto Java que implemente OnClickListener.

Considere el ejemplo concreto de un Fragmento que presenta un AlertDialog a través de DialogFragment. Tiene botones Sí / No. ¿Cómo puedo enviar estas pulsaciones de botón al Fragmento que lo creó?

eternalmatt
fuente
Usted mencionó "Pero no hay garantía de que el oyente estará presente si DialogFragment hace una pausa y se reanuda durante su ciclo de vida". Pensé que el estado Fragmento se destruía durante onDestroy ()? Debe tener razón, pero ahora estoy un poco confundido sobre cómo usar el estado Fragmento. ¿Cómo reproduzco el problema que mencionó, el oyente no está cerca?
Sean
No veo por qué no puedes simplemente usar OnClickListener listener = (OnClickListener) getParentFragment();en DialogFragment, y tu Fragment principal implementa la interfaz como lo hiciste originalmente.
kiruwka
Aquí hay una respuesta a una pregunta no relacionada, pero le muestra cómo se hace de manera limpia stackoverflow.com/questions/28620026/…
user2288580

Respuestas:

190

La actividad involucrada desconoce completamente el DialogFragment.

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

}

Clase 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
fuente
100
Creo que la clave aquí es setTargetFragmenty getTargetFragment. El uso de onActivityResultes un poco confuso. Probablemente sería mejor declarar su propio método específico en el llamador Fragment y usarlo, en lugar de volver a utilizar onActivityResult. Pero todo es semántica en ese punto.
eternalmatt
2
No se utiliza la variable de nivel de pila?
Nombre para mostrar el
66
¿Sobrevivirá esto a un cambio de configuración-rotación?
Maxrunner
3
Usé esto. Notas: el nivel de apilamiento no era necesario para sobrevivir a la rotación o al sueño. En lugar de onActivityResult, mi fragmento implementa DialogResultHandler # handleDialogResult (una interfaz que creé). @myCode, sería muy útil para mostrar un valor de diálogo seleccionado que se agrega a la intención, y luego leer dentro de su onActivityResult. Las intenciones no son claras para los principiantes.
Chris Betti
77
@eternalmatt, su objeción es completamente razonable, pero creo que el valor de onActivityResult () es que está garantizado de existir en cualquier Fragmento, por lo que cualquier Fragmento puede usarse como padre. Si crea su propia interfaz y hace que el Fragmento principal la implemente, entonces el hijo secundario solo se puede usar con los padres que implementan esa interfaz. Acoplando al niño a esa interfaz podría volverse en tu contra si comienzas a usarlo más tarde. El uso de la interfaz "incorporada" onActivityResult () no requiere acoplamiento adicional, por lo que le permite un poco más de flexibilidad.
Dalbergia
78

La solución TargetFragment no parece la mejor opción para fragmentos de diálogo porque puede crearse IllegalStateExceptiondespués de que la aplicación se destruya y se vuelva a crear. En este caso FragmentManagerno pudo encontrar el fragmento de destino y recibirá un IllegalStateExceptionmensaje como este:

"El fragmento ya no existe para Android clave: target_state: índice 1"

Parece que Fragment#setTargetFragment()no está destinado a la comunicación entre un Fragmento de padres e hijos, sino más bien a la comunicación entre Fragmentos de hermanos.

Entonces, una forma alternativa es crear fragmentos de diálogo como este mediante el uso del ChildFragmentManagerfragmento principal, en lugar de utilizar las actividades FragmentManager:

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

Y al usar una interfaz, en el onCreatemétodo de DialogFragmentpuede obtener el fragmento primario:

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

Lo único que queda es llamar a su método de devolución de llamada después de estos pasos.

Para obtener más información sobre el problema, puede consultar el enlace: https://code.google.com/p/android/issues/detail?id=54520

Oguz Ozcan
fuente
2
Esto también funciona con el onAttach (contexto de contexto) introducido agregado en la api 23.
Santa Teclado
1
ESTA debería ser la respuesta aceptada. La respuesta actual aceptada es defectuosa y los fragmentos no están destinados a ser utilizados de esta manera.
NecipAllef
3
@AhmadFadli El problema aquí es obtener el contexto correcto (padre) para la comunicación entre fragmentos. Si va a utilizar el fragmento de diálogo como hijo de una actividad, entonces no debería haber ninguna confusión. FragmentManager of Activity y getActivity () para recuperar la devolución de llamada es suficiente.
Oguz Ozcan
1
Esta debería ser la respuesta aceptada. Está claramente explicado y detallado, no es solo código lanzado a las personas.
Vince
1
@lukecross ParentFragment es el fragmento que crea el DialogFragment (el que llama show ()) Pero parece que childFragmentManager no sobrevive a la reconfiguración / rotaciones de pantalla ...
iwat0qs
34

Seguí estos simples pasos para hacer esto.

  1. Crear interfaz como DialogFragmentCallbackInterfacecon algún método como callBackMethod(Object data). Que llamarías para pasar datos.
  2. Ahora puede implementar la DialogFragmentCallbackInterfaceinterfaz en su fragmento comoMyFragment implements DialogFragmentCallbackInterface
  3. En el momento de la DialogFragmentcreación, configure su fragmento de invocación MyFragmentcomo fragmento de destino que creó DialogFragmentusando myDialogFragment.setTargetFragment(this, 0)check setTargetFragment (Fragment fragment, int requestCode)

    MyDialogFragment dialogFrag = new MyDialogFragment();
    dialogFrag.setTargetFragment(this, 1); 
  4. Obtenga su objeto de fragmento de destino en su DialogFragmentllamada getTargetFragment()y DialogFragmentCallbackInterfaceenvíelo a . Ahora puede usar esta interfaz para enviar datos a su fragmento.

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

    Eso es todo hecho! solo asegúrese de haber implementado esta interfaz en su fragmento.

Vijay Vankhede
fuente
44
Esta debería ser la mejor respuesta. Gran respuesta.
Md. Sajedul Karim
También asegúrese de usar el mismo administrador de fragmentos para los fragmentos de origen y destino, de lo contrario getTargetFragment no funcionará. Entonces, si está utilizando childFragmentManager, no funcionará ya que el fragmento de origen no está confirmado por el administrador de fragmentos secundarios. Es mejor pensar en estos 2 fragmentos como fragmentos hermanos en lugar de fragmentos padre / hijo.
Thupten
Francamente, es mejor usar solo el patrón de fragmento objetivo cuando se comunica entre 2 fragmentos hermanos. Al no tener un oyente, evitas filtrar accidentalmente el fragmento1 en el fragmento2. Cuando use un fragmento de destino, no use escucha / devolución de llamada. Solo se usa onActivityResult(request code, resultcode, intent)para devolver el resultado al fragmento1. Del fragmento1 setTargetFragment()y del fragmento2, use getTargetFragment(). Al usar el fragmento padre / hijo para fragmentar o la actividad para fragmentar, puede usar el oyente o la devolución de llamada, ya que no hay ningún peligro de fuga del padre en el fragmento hijo.
Thupten
@Thupten cuando dices "fuga", ¿te refieres a una fuga de memoria o "detalles de implementación de fuga"? No creo que la respuesta de Vijay pierda memoria más que usar onActivityResulty como valor de retorno. Ambos patrones mantendrán referencia al fragmento objetivo. Si te refieres a filtrar detalles de implementación, entonces creo que su patrón es incluso mejor que en OnActivityResult. El método de devolución de llamada es explícito (si se nombra correctamente). Si todo lo que obtiene está bien y CANCELADO, el primer fragmento debe interpretar lo que significan.
tir38
34

Tal vez un poco tarde, pero puede ayudar a otras personas con la misma pregunta que yo hice.

Puede usar setTargetFragmenton Dialogantes de mostrar, y en el diálogo puede llamar getTargetFragmentpara obtener la referencia.

Rui
fuente
Aquí hay una respuesta a otra pregunta, pero también se aplica a su pregunta y es una solución limpia: stackoverflow.com/questions/28620026/…
user2288580
IllegalStateException to me
luke cross
19

La guía de Comunicación con otros fragmentos dice que los fragmentos deben comunicarse a través de la actividad asociada .

A menudo, querrá que un Fragmento se comunique con otro, por ejemplo, para cambiar el contenido en función de un evento del usuario. Toda la comunicación Fragmento a Fragmento se realiza a través de la Actividad asociada. Dos fragmentos nunca deben comunicarse directamente.

Edward Brey
fuente
1
¿Qué hay de fragmentos internos, es decir, cómo debería un fragmento dentro de otro fragmento de comunicar a fragmento anfitrión
Ravi
@Ravi: cada fragmento debe comunicarse con la actividad que es común a todos los fragmentos llamando a getActivity () .
Edward Brey
1
@Chris: si los fragmentos necesitan una comunicación continua, defina una interfaz para cada fragmento apropiado para implementar. El trabajo de la actividad se limita a proporcionar fragmentos con punteros de interfaz a sus fragmentos equivalentes. Después de eso, los fragmentos pueden comunicarse de forma segura "directamente" a través de las interfaces.
Edward Brey
3
Creo que a medida que se ampliaron los usos de los fragmentos, se rompe la idea original de no utilizar la comunicación directa de fragmentos. Por ejemplo, en un cajón de navegación, cada fragmento secundario inmediato de la actividad actúa más o menos como una actividad. Por lo tanto, tener un fragmento como un fragmento de diálogo comunicarse a través de la actividad perjudica la legibilidad / flexibilidad de la OMI. De hecho, no parece haber una buena manera de encapsular el fragmento de diálogo para permitirle trabajar con actividades y fragmentos de forma reutilizable.
Sam
16
Sé que esto es antiguo, pero en caso de que alguien más venga aquí, siento que el caso mencionado en ese documento no se aplica cuando un fragmento "posee" la lógica que se utiliza para determinar la creación y administración del DialogFragment. Es un poco extraño crear un montón de conexiones desde el fragmento a la actividad cuando la actividad ni siquiera está segura de por qué se está creando un Diálogo o en qué condiciones se debe descartar. Además, DialogFragment es súper simple y existe solo para notificar al usuario y potencialmente obtener una respuesta.
Chris
12

Debe definir un interfaceen su clase de fragmento e implementar esa interfaz en su actividad principal. Los detalles se describen aquí http://developer.android.com/guide/components/fragments.html#EventCallbacks . El código sería similar 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");
        }
    }
}

Actividad:

public class MyActivity extends Activity implements OnArticleSelectedListener{

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

    }
    ...
}
James McCracken
fuente
1
Creo que pasaste los documentos demasiado rápido. Ambos segmentos de código son FragmentAy está asumiendo que una actividad es un OnArticleSelectedListener, no el Fragmento que lo inició.
eternalmatt
2
Consideraría lo que estás tratando de hacer una mala práctica. Las directrices de Android recomiendan que toda la comunicación fragmento a fragmento se realice a través de la actividad (según developer.android.com/training/basics/fragments/… ). Si realmente quieres que todo se maneje dentro de MyFragmentti, puedes cambiar a usar un regularAlertDialog
James McCracken
1
Creo que la preocupación de que los fragmentos hablen directamente entre sí es que en algunos diseños no todos los fragmentos pueden cargarse y, como muestran en el ejemplo, podría ser necesario cambiar el fragmento. No creo que esa preocupación sea válida cuando se habla de lanzar un fragmento de diálogo desde un fragmento.
Tengo esto implementado para mis actividades. Pregunta: ¿se puede extender esta solución de manera que un fragmento pueda crear una instancia de este diálogo?
Bill Mote
1
Esta es una buena práctica desde una perspectiva arquitectónica, y como tal debería ser la respuesta aceptada. El uso de onActivityResult conduce a la arquitectura de espagueti
Bruno Carrier
4

La forma correcta de configurar un oyente para un fragmento es configurándolo cuando está adjunto . El problema que tuve fue que onAttachFragment () nunca fue llamado. Después de investigar un poco, me di cuenta de que había estado usando getFragmentManager en lugar de getChildFragmentManager

Así es como lo hago:

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

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

            }
        });
    }
}
usuario1354603
fuente
3

Según la documentación oficial:

Fragmento # setTargetFragment

Objetivo opcional para este fragmento. Esto se puede usar, por ejemplo, si este fragmento está siendo iniciado por otro, y cuando se hace quiere devolver el resultado al primero. El objetivo establecido aquí se retiene en todas las instancias a través de FragmentManager # putFragment.

Fragmento # getTargetFragment

Devuelve el fragmento de destino establecido por setTargetFragment (Fragment, int).

Entonces puedes hacer esto:

// 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
fuente
puedes explicar tu?
Yilmaz
En este caso, necesitamos pasar la referencia "MyFragment" a "MyialogFragment", y "Fragment" proporciona el método para hacerlo. Agregué la descripción del documento oficial, debería decir más claramente que yo.
SUPERYAO
2

Estaba enfrentando un problema similar. La solución que descubrí fue:

  1. Declare una interfaz en su DialogFragment tal como James McCracken ha explicado anteriormente.

  2. Implemente la interfaz en su actividad (¡no fragmente! Esa no es una buena práctica).

  3. Desde el método de devolución de llamada en su actividad, llame a una función pública requerida en su fragmento que hace el trabajo que desea hacer.

Por lo tanto, se convierte en un proceso de dos pasos: DialogFragment -> Activity y luego Activity -> Fragment

Shailesh Mani Pandey
fuente
1

Estoy obteniendo el resultado de Fragment DashboardLiveWall (fragmento de llamada) de Fragment LiveWallFilterFragment (fragmento de recepción) De esta manera ...

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

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

dónde

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 vuelve a llamar al 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
fuente
1

Actualizado:

Hice una biblioteca basada en mi código esencial que genera esos castings para ti usando @CallbackFragmenty @Callback.

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

Y el ejemplo le da el ejemplo que envía una devolución de llamada de un fragmento a otro fragmento.

Vieja respuesta:

Hice una BaseCallbackFragmentanotación @FragmentCallback. Actualmente se extiende Fragment, puede cambiarlo aDialogFragment y funcionará. Comprueba las implementaciones en el siguiente orden: getTargetFragment ()> getParentFragment ()> context (activity).

Luego solo necesita extenderlo y declarar sus interfaces en su fragmento y darle la anotación, y el fragmento base hará el resto. La anotación también tiene un parámetro mandatorypara determinar si desea forzar el fragmento a implementar la devolución de llamada.

public class EchoFragment extends BaseCallbackFragment {

    private FragmentInteractionListener mListener;

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

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

Primero
fuente
1

Kotlin chicos aquí vamos!

Entonces, el problema que tenemos es que creamos una actividad, MainActivityen esa actividad creamos un fragmento, FragmentAy ahora queremos crear un fragmento de diálogo además de FragmentAllamarlo FragmentB. ¿Cómo obtenemos los resultados desde FragmentBatrás hasta FragmentAsin pasar porMainActivity ?

Nota:

  1. FragmentAes un fragmento infantil de MainActivity. Para gestionar los fragmentos creados FragmentA, usaremos el childFragmentManagerque hace eso.
  2. FragmentAes un fragmento padre de FragmentB, para acceder FragmentAdesde dentro FragmentBlo utilizaremos parenFragment.

Habiendo dicho eso, adentro 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")
    }
}

Aquí está el 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")
    }
}

Aquí está la interfaz que une los dos.

interface UpdateNameListener {
    fun onSave(name: String)
}

Eso es.

Ssenyonjo
fuente
1
Seguí este documento: developer.android.com/guide/topics/ui/dialogs y no funcionó. Muchas gracias. Espero que este fragmento de padre funcione como se esperaba cada vez :)
UmutTekin
1
no te olvides de configurar el oyente como nulo dentro de onDetach :)
BekaBot
@BekaBot Gracias por el comentario. He investigado un poco y parece que no es necesario cerrar a los oyentes. stackoverflow.com/a/37031951/10030693
Ssenyonjo
0

Resolví esto de una manera elegante con RxAndroid. Reciba un observador en el constructor del DialogFragment y suscríbase a observable y empuje el valor cuando se llame la devolución de llamada. Luego, en su Fragmento, cree una clase interna del Observador, cree una instancia y pásela en el constructor del DialogFragment. Usé WeakReference en el observador para evitar pérdidas de memoria. Aquí está el 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() {

    }
}

Puede ver el código fuente aquí: https://github.com/andresuarezz26/carpoolingapp

Gerardo Suárez
fuente
1
Lo divertido de Android es que hay algo llamado ciclo de vida. El fragmento base o el fragmento de diálogo deben poder preservar el estado (y su conexión) durante los eventos del ciclo de vida. Las devoluciones de llamada u observadores no se pueden serializar y, por lo tanto, tienen el mismo problema aquí.
GDanger