android - from - dialogfragment onattach fragment
DevoluciĆ³n de llamada a un fragmento de un DialogFragment (11)
Debe definir una interface
en su clase de fragmento e implementar esa interfaz en su actividad principal. Los detalles se detallan 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){
}
...
}
Pregunta: ¿Cómo se crea una devolución de llamada desde un DialogFragment a otro Fragment? En mi caso, la actividad involucrada debería ser completamente inconsciente de 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 a lo largo de su ciclo de vida. Las únicas garantías en un Fragmento son aquellas pasadas a través de un Paquete a través de setArguments y getArguments.
Hay una forma de referenciar la actividad si debe 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 estos botones nuevamente al Fragmento que lo creó?
Estaba enfrentando un problema similar. La solución que descubrí fue:
Declare una interfaz en su DialogFragment tal como James McCracken lo explicó anteriormente.
Implemente la interfaz en su actividad (¡no fragmento! Esa no es una buena práctica).
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
La actividad involucrada es completamente inconsciente de 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();
}
}
La forma correcta de configurar un oyente a un fragmento es configurándolo cuando está conectado . El problema que tenía era que enAttachFragment nunca se llamó, después de algunas investigaciones 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");
Adjúntalo enAttachFragment:
@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() {
}
});
}
}
La guía Comunicación con Otros Fragmentos dice que los Fragmentos deberían comunicarse a través de la Actividad asociada .
A menudo, deseará 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 de Fragmento a Fragmento se realiza a través de la Actividad asociada. Dos fragmentos nunca deberían comunicarse directamente.
La solución TargetFragment no parece la mejor opción para los fragmentos de diálogo porque puede crear IllegalStateException
después de que la aplicación se destruye y IllegalStateException
crear. En este caso, FragmentManager
no pudo encontrar el fragmento de destino y obtendrá una IllegalStateException
con un mensaje como este:
"Fragmento ya no existe para la clave android: target_state: índice 1"
Parece que Fragment#setTargetFragment()
no está destinado a la comunicación entre un Fragmento primario y secundario, sino más bien para la comunicación entre Fragmentos hermanos.
Entonces, una forma alternativa es crear fragmentos de diálogo como este utilizando ChildFragmentManager
del fragmento principal, en lugar de usar las actividades FragmentManager
:
dialogFragment.show(ParentFragment.this.getChildFragmentManager(), "dialog_fragment");
Y al usar una interfaz, en el método onCreate
de DialogFragment
puede obtener el fragmento principal:
@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
Obtengo el resultado de Fragment DashboardLiveWall (fragmento de llamada) de Fragment LiveWallFilterFragment (fragmento de recepción) Como este ...
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 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 {
}
}
}
}
}
Resolví esto de una manera elegante con RxAndroid. Reciba un observador en el constructor de DialogFragment y suscríbase a observable y presione el valor cuando se llame a la devolución de llamada. Luego, en su Fragment, cree una clase interna del Observer, cree una instancia y páselo al 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
Seguí estos sencillos pasos para hacer esto.
- Cree una interfaz como
DialogFragmentCallbackInterface
con algún método, comocallBackMethod(Object data)
. Que llamarías para pasar datos. - Ahora puede implementar la interfaz
DialogFragmentCallbackInterface
en su fragmento comoMyFragment implements DialogFragmentCallbackInterface
En el momento de la creación de
DialogFragment
establezca el fragmento de invocaciónMyFragment
como fragmento de destino que creóDialogFragment
usemyDialogFragment.setTargetFragment(this, 0)
check setTargetFragment (Fragment fragment, int requestCode)MyDialogFragment dialogFrag = new MyDialogFragment(); dialogFrag.setTargetFragment(this, 1);
Obtenga su objeto de fragmento de destino en su
DialogFragment
llamando agetTargetFragment()
yDialogFragmentCallbackInterface
aDialogFragmentCallbackInterface
. Ahora puede usar esta interfaz para enviar datos a su fragmento.DialogFragmentCallbackInterface callback = (DialogFragmentCallbackInterface) getTargetFragment(); callback.callBackMethod(Object data);
¡Eso es todo! solo asegúrate de haber implementado esta interfaz en tu fragmento.
Tal vez un poco tarde, pero puede ayudar a otras personas con la misma pregunta que yo.
Puede usar setTargetFragment
en Dialog
antes de mostrar, y en el cuadro de diálogo puede llamar a getTargetFragment
para obtener la referencia.
Actualizado:
@CallbackFragment
una biblioteca basada en mi código de idea que genera esos @CallbackFragment
para ti usando @CallbackFragment
y @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.
Respuesta anterior:
Hice una BaseCallbackFragment
y una anotación @FragmentCallback
. Actualmente se extiende Fragment
, puedes cambiarlo a DialogFragment
y funcionará. Comprueba las implementaciones con el siguiente orden: getTargetFragment ()> getParentFragment ()> context (activity).
Entonces 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 mandatory
para que pueda determinar si desea forzar al 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