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/

12voto

James McCracken Points 4181

Vous devez définir un interface dans votre classe de fragment et implémentez cette interface dans son activité parente. Les détails sont décrits ici http://developer.Android.com/guide/components/fragments.html#EventCallbacks . Le code ressemblerait à :

Fragment :

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

Activité :

public class MyActivity extends Activity implements OnArticleSelectedListener{

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

    }
    ...
}

1 votes

Je pense que vous avez parcouru la documentation trop rapidement. Ces deux segments de code sont FragmentA et il suppose qu'une activité est une OnArticleSelectedListener et non le fragment qui l'a fait naître.

2 votes

Je considérerais que ce que vous essayez de faire est une mauvaise pratique. Les directives Android recommandent que toute communication entre fragments se fasse par l'intermédiaire de l'activité (per developer.Android.com/training/basics/fragments/ ). Si vous voulez vraiment que tout soit géré dans le cadre de l'initiative MyFragment vous pouvez passer à l'utilisation d'un AlertDialog

1 votes

Je pense que le souci de faire parler les fragments directement entre eux est que dans certaines mises en page, tous les fragments peuvent ne pas être chargés et comme ils le montrent dans l'exemple, il peut être nécessaire de changer de fragment. Je ne pense pas que cette préoccupation soit valable lorsque l'on parle de lancer un fragment de dialogue à partir d'un fragment.

10voto

SuperYao Points 31

Selon la documentation officielle :

Fragment#setTargetFragment

Cible facultative pour ce fragment. Ceci peut être utilisé, par exemple, si ce fragment est lancé par un autre, et qu'une fois terminé, il veut donner un résultat au premier. La cible définie ici est conservée entre les instances via FragmentManager#putFragment.

[Fragment#getTargetFragment](https://developer.android.com/reference/android/app/Fragment.html#getTargetFragment())

Retourne le fragment cible défini par setTargetFragment(Fragment, int).

Donc tu peux faire ça :

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

6voto

Gilboot Points 576

Mise à jour : Veuillez noter qu'il existe des moyens plus faciles de faire cela en utilisant des modèles de vue que je peux partager si quelqu'un est intéressé.

Les gars de Kotlin, c'est parti !

Donc le problème que nous avons est que nous avons créé une activité, MainActivity sur cette activité, nous avons créé un fragment, FragmentA et maintenant nous voulons créer un fragment de dialogue sur le dessus de FragmentA appelez-le FragmentB . Comment obtenir les résultats de FragmentB retour à FragmentA sans passer par MainActivity ?

Nota:

  1. FragmentA est un fragment enfant de MainActivity . Pour gérer les fragments créés dans FragmentA nous utiliserons [childFragmentManager](https://developer.android.com/reference/android/support/v4/app/Fragment#getChildFragmentManager()) qui fait ça !
  2. FragmentA est un fragment parent de FragmentB pour accéder à FragmentA de l'intérieur FragmentB nous utiliserons [parenFragment](https://developer.android.com/reference/android/support/v4/app/Fragment#getParentFragment()) .

Cela dit, à l'intérieur 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")
    }
}

Voici le fragment de dialogue 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")
    }
}

Voici l'interface qui relie les deux.

interface UpdateNameListener {
    fun onSave(name: String)
}

C'est tout.

5voto

user1354603 Points 220

La manière correcte de définir un écouteur pour un fragment est de le définir lorsqu'il est ci-joint . Le problème que j'avais était que onAttachFragment() n'était jamais appelé. Après quelques recherches, j'ai réalisé que j'avais utilisé la méthode getFragmentManager au lieu de getChildFragmentManager

Voici comment je procède :

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

Attachez-le dans 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() {

            }
        });
    }
}

2voto

J'étais confronté à un problème similaire. La solution que j'ai trouvée est la suivante :

  1. Déclarez une interface dans votre DialogFragment comme James McCracken l'a expliqué ci-dessus.

  2. Implémentez l'interface dans votre activité (pas de fragment ! Ce n'est pas une bonne pratique).

  3. À partir de la méthode de rappel de votre activité, appelez une fonction publique requise dans votre fragment qui effectue le travail que vous voulez faire.

Il s'agit donc d'un processus en deux étapes : DialogFragment -> Activity puis Activity -> Fragment.

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