185 votes

Appel à un fragment à partir d'un DialogFragment

Question : Comment créer un callback d'un DialogFragment vers un autre Fragment. Dans mon cas, l'activité concernée doit ignorer complètement le DialogFragment.

Considérez que j'ai

public class MyFragment extends Fragment implements OnClickListener

Puis, à un moment donné, j'ai pourrait faire

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

Où MyDialogFragment se présente comme suit

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

Mais il n'y a aucune garantie que l'écouteur sera présent si le DialogFragment s'interrompt et reprend au cours de son cycle de vie. Les seules garanties dans un fragment sont celles transmises par un Bundle via setArguments et getArguments.

Il existe un moyen de référencer l'activité si elle doit être l'écouteur :

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

Mais je ne veux pas que l'Activité écoute les événements, j'ai besoin d'un Fragment. En fait, il peut s'agir de n'importe quel objet Java qui implémente OnClickListener.

Prenons l'exemple concret d'un Fragment qui présente un AlertDialog via un DialogFragment. Il comporte des boutons Oui/Non. Comment puis-je renvoyer ces pressions sur les boutons au Fragment qui l'a créé ?

0 votes

Vous avez mentionné "Mais il n'y a aucune garantie que l'écouteur sera présent si le DialogFragment s'interrompt et reprend au cours de son cycle de vie". Je pensais que l'état des fragments était détruit lors de la fonction onDestroy() ? Vous devez avoir raison, mais je suis un peu confus quant à l'utilisation de l'état des fragments maintenant. Comment puis-je reproduire le problème que vous avez mentionné, l'écouteur n'est pas là ?

0 votes

Je ne vois pas pourquoi vous ne pouvez pas simplement utiliser OnClickListener listener = (OnClickListener) getParentFragment(); dans DialogFragment à la place, et votre Fragment principal implémente l'interface comme vous l'avez fait à l'origine.

0 votes

Voici une réponse à une question qui n'a rien à voir, mais qui vous montre comment procéder de manière propre. stackoverflow.com/questions/28620026/

1voto

Nepster Points 893

Je reçois le résultat du Fragment DashboardLiveWall(fragment appelant) du Fragment LiveWallFilterFragment(fragment recevant) Comme ceci...

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

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

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

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

setResult retourne au fragment appelant comme

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

1voto

Arst Points 91

Mis à jour :

J'ai créé une bibliothèque basée sur mon code gist qui génère ces castings pour vous en utilisant @CallbackFragment y @Callback .

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

Et l'exemple vous donne l'exemple qui envoie un callback d'un fragment à un autre fragment.

Vieille réponse :

J'ai fait un BaseCallbackFragment et annotation @FragmentCallback . Il s'étend actuellement Fragment vous pouvez le changer en DialogFragment et fonctionnera. Il vérifie les implémentations dans l'ordre suivant : getTargetFragment() > getParentFragment() > contexte (activité).

Il vous suffit alors de l'étendre, de déclarer vos interfaces dans votre fragment et de lui donner l'annotation, et le fragment de base fera le reste. L'annotation a également un paramètre mandatory pour vous permettre de déterminer si vous voulez forcer le fragment à implémenter le 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

1voto

    this is work for me
    i think you can set callback in display method in your fragment,

    **in my fragment**

    val myDialogFragment=MyDialogFragment()
    myDialogFragment.display(fragmentManager!!,this)

//mon fragment implémente le CallbackDialogFragment, il faut donc le définir pour la méthode d'affichage.

    **in dialog fragment**

    lateinit var callBackResult: CallbackDialogFragment

    fun display(fragmentManager: FragmentManager, callback: CallbackDialogFragment) {
        callBackResult = callback
        show(fragmentManager,"dialogTag")
    }

0voto

Gerardo Suarez Points 120

J'ai résolu ce problème d'une manière élégante avec RxAndroid. Recevez un observateur dans le constructeur du DialogFragment, inscrivez-le à l'observable et poussez la valeur lorsque le callback est appelé. Ensuite, dans votre Fragment, créez une classe interne de l'observateur, créez une instance et passez-la dans le constructeur du DialogFragment. J'ai utilisé WeakReference dans l'observateur pour éviter les fuites de mémoire. Voici le code :

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

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

    }
}

Vous pouvez voir le code source ici : https://github.com/andresuarezz26/carpoolingapp

0voto

olajide Points 707

Une façon plus améliorée est d'utiliser simplement newInstance et interface .

Voici un fragment qui a besoin DailogFragment

public class Fragment extends Fragment implements returnPinInterface {
....
....
public View onCreateView(@NotNull LayoutInflater inflater, ViewGroup 
container,Bundle savedInstanceState) {

// A simple call to show DialogFragment

   btnProceed.setOnClickListener(v -> {
        fragment = DailogFragment.newInstance(this);
        fragment.show(getChildFragmentManager(),null );
        fragment.setCancelable(false);
    });

   //Grab whatever user clicked/selected/ or typed
   @Override
   public void onPinReturn(String s) {
      Log.d("ReturnedPin", s);
   }
}

Voici votre DialogFragment

public class PinDialogFragment extends DialogFragment {

 //Create a static variable to help you receive instance of fragment you 
 //passed
public static Fragment fragm;

  // Create new Instance and grab the object passed in Fragment up 
  //there
  public static PinDialogFragment newInstance(Fragment frag) {
    PinDialogFragment fragment = new PinDialogFragment();
    fragm = frag;
    return fragment;
  }

public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
   View v = inflater.inflate(R.layout.fragment_pin, container, false);

  //
   btn.setOnClickListener(btn ->                         
   listener.onReturnPin("1234"));

   return v;
}

//Use the Fragm to instantiate your Interface

@Override
public void onAttach(Context context) {
    super.onAttach(context);

    if (fragm instanceof ReturnPinInterface) {
        listener = (ReturnPinInterface) fragm;
    } else {
        throw new RuntimeException("you must implement ReturnPinInterface");
    }
}
}

Profitez-en !

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X