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/

196voto

myCode Points 1002

L'activité concernée n'est absolument pas consciente du DialogFragment.

Classe de fragments :

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

104 votes

Je pense que la clé ici est setTargetFragment y getTargetFragment . L'utilisation de onActivityResult n'est pas très clair. Il serait probablement préférable de déclarer votre propre méthode spécifique dans l'appelant du fragment, et de l'utiliser, au lieu de réaffecter onActivityResult. Mais ce n'est que de la sémantique à ce stade.

2 votes

La variable de niveau pile n'est pas utilisée ?

10 votes

Est-ce que cela survivra à un changement de configuration - rotation ?

90voto

Oguz Ozcan Points 913

La solution TargetFragment ne semble pas être la meilleure option pour les fragments de dialogue car elle peut créer des IllegalStateException après que l'application soit détruite et recréée. Dans ce cas FragmentManager n'a pas pu trouver le fragment cible et vous obtiendrez un IllegalStateException avec un message comme celui-ci :

"Le fragment n'existe plus pour la clé Android:target_state : index 1"

Il semble que Fragment#setTargetFragment() n'est pas destiné à la communication entre un fragment enfant et un fragment parent, mais plutôt à la communication entre des fragments frères et sœurs.

Une autre solution consiste donc à créer des fragments de dialogue comme celui-ci en utilisant la fonction ChildFragmentManager du fragment parent, plutôt que d'utiliser les activités FragmentManager :

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

Et en utilisant une interface, dans onCreate de la méthode DialogFragment vous pouvez obtenir le fragment parent :

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

La seule chose qui reste à faire est d'appeler votre méthode de rappel après ces étapes.

Pour plus d'informations sur cette question, vous pouvez consulter le lien : https://code.google.com/p/Android/issues/detail?id=54520

40voto

vsvankhede Points 8

J'ai suivi ces étapes simples pour faire ce truc.

  1. Créer une interface comme DialogFragmentCallbackInterface avec une méthode comme callBackMethod(Object data) . Que vous appelleriez pour passer des données.

  2. Vous pouvez maintenant mettre en œuvre DialogFragmentCallbackInterface dans votre fragment comme MyFragment implements DialogFragmentCallbackInterface

  3. Au moment de DialogFragment création de votre fragment d'invocation MyFragment comme fragment de cible qui a créé DialogFragment utiliser myDialogFragment.setTargetFragment(this, 0) vérifier setTargetFragment (Fragment fragment, int requestCode)

    MyDialogFragment dialogFrag = new MyDialogFragment();
    dialogFrag.setTargetFragment(this, 1); 
  4. Obtenez votre objet fragment cible dans votre DialogFragment en appelant getTargetFragment() et le lancer à DialogFragmentCallbackInterface Vous pouvez maintenant utiliser cette interface pour envoyer des données à votre fragment.

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

    C'est tout fait ! Assurez-vous simplement que vous avez implémenté cette interface dans votre fragment.

36voto

Rui Points 1166

C'est peut-être un peu tard, mais cela peut aider d'autres personnes qui se posent la même question que moi.

Vous pouvez utiliser setTargetFragment en Dialog avant de l'afficher, et dans le dialogue, vous pouvez appeler getTargetFragment pour obtenir la référence.

0 votes

Voici une réponse à une autre question, mais elle s'applique également à votre question et constitue une solution propre : stackoverflow.com/questions/28620026/

0 votes

IllegalStateException pour moi

19voto

Edward Brey Points 8771

El Communiquer avec d'autres fragments Le guide indique que les fragments doivent communiquer par le biais de l'activité associée .

Souvent, vous voudrez qu'un fragment communique avec un autre, par exemple pour modifier le contenu en fonction d'un événement utilisateur. par exemple pour modifier le contenu en fonction d'un événement utilisateur. Tout communication de Fragment à Fragment se fait par le biais de l'activité associée. activité associée. Deux fragments ne doivent jamais communiquer directement.

1 votes

Qu'en est-il des fragments internes, c'est-à-dire comment un fragment à l'intérieur d'un autre fragment doit-il communiquer avec le fragment hôte ?

0 votes

@Ravi : Chaque fragment devrait communiquer avec l'activité qui est commune à tous les fragments en appelant [getActivity()](http://developer.android.com/reference/android/app/Fragment.html#getActivity()) .

0 votes

@EdwardBrey : comment organisez-vous vos clés d'état d'instance dans ce cas ? c'est-à-dire si plusieurs fragments "de base" utilisent la même implémentation de DialogFragment ?

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