Afficher la boîte de dialogue du fragment?

119

J'ai quelques fragments qui doivent montrer un dialogue régulier. Dans ces boîtes de dialogue, l'utilisateur peut choisir une réponse oui / non, puis le fragment doit se comporter en conséquence.

Maintenant, la Fragmentclasse n'a pas de onCreateDialog()méthode à remplacer, donc je suppose que je dois implémenter les dialogues à l'extérieur, dans le contenant Activity. Ce n'est pas grave, mais il est alors Activitynécessaire de rapporter la réponse choisie d'une manière ou d'une autre au fragment. Je pourrais bien sûr utiliser un modèle de rappel ici, de sorte que le fragment s'enregistre lui-même auprès Activityd'une classe d'auditeur, et l'activité rapporterait la réponse à travers cela, ou quelque chose comme ça.

Mais cela semble être un gros gâchis pour une tâche simple comme l'affichage d'un "simple" dialogue oui-non dans un fragment. De plus, de cette façon, je Fragmentserais moins autonome.

Y a-t-il un moyen plus propre de le faire?

Éditer:

La réponse à cette question n'explique pas vraiment en détail comment utiliser DialogFragments pour afficher des boîtes de dialogue à partir de Fragments. Alors AFAIK, la voie à suivre est:

  1. Affichez un fragment.
  2. Si nécessaire, instanciez un DialogFragment.
  3. Définissez le fragment d'origine comme cible de ce DialogFragment, avec .setTargetFragment().
  4. Afficher le DialogFragment avec .show () du fragment d'origine.
  5. Lorsque l'utilisateur choisit une option sur ce DialogFragment, informez le Fragment d'origine de cette sélection (par exemple, l'utilisateur a cliqué sur «oui»), vous pouvez obtenir la référence du Fragment d'origine avec .getTarget ().
  6. Ignorez le DialogFragment.
Zsombor Erdődy-Nagy
la source
1
Votre technique fonctionne, sauf lorsqu'une rotation d'écran se produit. J'approche alors une force. Des idées?
Weston
@Weston découvrez la première réponse de Zsombor: stackoverflow.com/questions/8235080/…
mightimaus

Réponses:

37

Vous devez utiliser un DialogFragment à la place.

mgv
la source
9
Malheureusement, cette approche est un peu plus détaillée que l'approche classique des dialogues gérés des précédentes révisions Android, mais c'est maintenant la méthode préférée. Vous pouvez éviter de référencer Activityentièrement le en utilisant les méthodes putFragmentet getFragmentde FragmentManager, ce qui permet au de DialogFragmentfaire rapport directement au fragment appelant (même après des changements d'orientation).
Dave
Que faire si vous avez un ListFragment qui doit afficher des boîtes de dialogue, vous ne pouvez pas les étendre tous les deux
marchinram
16
La sous-classe ListFragment utiliserait DialogFragments en instanciant de nouveaux, et non en sous-classant DialogFragment. (Un DialogFragment est une boîte de dialogue implémentée en tant que fragment, pas en tant que fragment qui peut afficher des boîtes de dialogue.)
nmr
4
Veuillez ajouter un extrait pour que nous puissions comprendre rapidement
Arpit Patel
35

Je dois douter avec prudence de la réponse précédemment acceptée selon laquelle l'utilisation d'un DialogFragment est la meilleure option. Le but (principal) prévu de DialogFragment semble être d'afficher des fragments qui sont eux-mêmes des boîtes de dialogue, et non d'afficher des fragments qui ont des boîtes de dialogue à afficher.

Je pense que l'utilisation de l'activité du fragment pour servir d'intermédiaire entre le dialogue et le fragment est l'option préférable.

Mark D
la source
10
Étant donné que l' onCreateDialogapproche des dialogues gérés ( ) est bientôt obsolète, je ne suis pas d'accord et je dirais que DialogFragmentc'est effectivement la voie à suivre.
Dave
4
Une réponse acceptée signifie utiliser un DialogFragment au lieu d'un Dialog, pas au lieu d'un ListFragment, AFAICS.
nmr
En fait, un fragment de boîte de dialogue peut être utilisé à la fois comme une boîte de dialogue et un fragment normal - voir intégration developer.android.com/reference/android/app/DialogFragment.html
Clive Jefferies
@CliveJefferies Il peut, mais n'est toujours pas censé afficher d'autres dialogues de l'intérieur de lui-même. Je pense que les gars ici se trompent.
Marcel Bro
@anoniim cela dépend de la manière dont le fragment de dialogue est utilisé. S'il est utilisé comme un fragment régulier, c'est bien. S'il est utilisé comme boîte de dialogue, alors oui, vous ne devriez pas afficher une autre boîte de dialogue.
Clive Jefferies
24

Voici un exemple complet de DialogFragment oui / non:

La classe:

public class SomeDialog extends DialogFragment {

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        return new AlertDialog.Builder(getActivity())
            .setTitle("Title")
            .setMessage("Sure you wanna do this!")
            .setNegativeButton(android.R.string.no, new OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    // do nothing (will close dialog)
                }
            })
            .setPositiveButton(android.R.string.yes,  new OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    // do something
                }
            })
            .create();
    }
}

Pour démarrer le dialogue:

        FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
        // Create and show the dialog.
        SomeDialog newFragment = new SomeDialog ();
        newFragment.show(ft, "dialog");

Vous pouvez également laisser la classe implémenter onClickListener et l'utiliser à la place des écouteurs intégrés.

Rappel à l'activité

Si vous souhaitez implémenter le rappel, voici comment cela se fait dans votre activité:

YourActivity extends Activity implements OnFragmentClickListener

et

@Override
public void onFragmentClick(int action, Object object) {
    switch(action) {
        case SOME_ACTION:
        //Do your action here
        break;
    }
}

La classe de rappel:

public interface OnFragmentClickListener {
    public void onFragmentClick(int action, Object object);
}

Ensuite, pour effectuer un rappel à partir d'un fragment, vous devez vous assurer que l'auditeur est attaché comme ceci:

@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);
    try {
        mListener = (OnFragmentClickListener) activity;
    } catch (ClassCastException e) {
        throw new ClassCastException(activity.toString() + " must implement listeners!");
    }
}

Et un rappel est effectué comme ceci:

mListener.onFragmentClick(SOME_ACTION, null); // null or some important object as second parameter.
Warpzit
la source
4
Cela n'explique pas comment le démarrer à partir d'un fragment
akohout
@raveN vous feriez simplement un rappel à votre activité qui commencerait alors le fragment.
Warpzit
@Warpzit Merci pour la réponse complète. J'ai peur de toucher le FragmentManager, avec lequel je fais déjà des choses impies ... Comment puis-je transmettre les événements onClick au fragment (non-Dialog) qui nécessitait une entrée de l'utilisateur? BTW, la transaction ne devrait-elle pas être ajoutée à backstack?
kaay
@kaay de l'activité, vous pouvez appeler n'importe quelle méthode publique dans le fragment donné qui a besoin de la nouvelle entrée. À partir de là, la mise à jour devrait être assez facile avec le nouveau contenu.
Warpzit le
1
@RichardLeMesurier En effet, les fragments sont des hauts et des bas.
Warpzit
13

Pour moi, c'était le suivant-

MyFragment:

public class MyFragment extends Fragment implements MyDialog.Callback
{
    ShowDialog activity_showDialog;

    @Override
    public void onAttach(Activity activity)
    {
        super.onAttach(activity);
        try
        {
            activity_showDialog = (ShowDialog)activity;
        }
        catch(ClassCastException e)
        {
            Log.e(this.getClass().getSimpleName(), "ShowDialog interface needs to be     implemented by Activity.", e);
            throw e;
        }
    }

    @Override
    public void onClick(View view) 
    {
        ...
        MyDialog dialog = new MyDialog();
        dialog.setTargetFragment(this, 1); //request code
        activity_showDialog.showDialog(dialog);
        ...
    }

    @Override
    public void accept()
    {
        //accept
    }

    @Override
    public void decline()
    {
        //decline
    }

    @Override
    public void cancel()
    {
        //cancel
    }

}

MyDialog:

public class MyDialog extends DialogFragment implements View.OnClickListener
{
    private EditText mEditText;
    private Button acceptButton;
    private Button rejectButton;
    private Button cancelButton;

    public static interface Callback
    {
        public void accept();
        public void decline();
        public void cancel();
    }

    public MyDialog()
    {
        // Empty constructor required for DialogFragment
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
    {
        View view = inflater.inflate(R.layout.dialogfragment, container);
        acceptButton = (Button) view.findViewById(R.id.dialogfragment_acceptbtn);
        rejectButton = (Button) view.findViewById(R.id.dialogfragment_rejectbtn);
        cancelButton = (Button) view.findViewById(R.id.dialogfragment_cancelbtn);
        acceptButton.setOnClickListener(this);
        rejectButton.setOnClickListener(this);
        cancelButton.setOnClickListener(this);
        getDialog().setTitle(R.string.dialog_title);
        return view;
    }

    @Override
    public void onClick(View v)
    {
        Callback callback = null;
        try
        {
            callback = (Callback) getTargetFragment();
        }
        catch (ClassCastException e)
        {
            Log.e(this.getClass().getSimpleName(), "Callback of this class must be implemented by target fragment!", e);
            throw e;
        }

        if (callback != null)
        {
            if (v == acceptButton)
            {   
                callback.accept();
                this.dismiss();
            }
            else if (v == rejectButton)
            {
                callback.decline();
                this.dismiss();
            }
            else if (v == cancelButton)
            {
                callback.cancel();
                this.dismiss();
            }
        }
    }
}

Activité:

public class MyActivity extends ActionBarActivity implements ShowDialog
{
    ..

    @Override
    public void showDialog(DialogFragment dialogFragment)
    {
        FragmentManager fragmentManager = getSupportFragmentManager();
        dialogFragment.show(fragmentManager, "dialog");
    }
}

Disposition DialogFragment:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    android:orientation="vertical" >

    <TextView
        android:id="@+id/dialogfragment_textview"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:layout_centerHorizontal="true"
        android:layout_marginBottom="10dp"
        android:text="@string/example"/>

    <Button
        android:id="@+id/dialogfragment_acceptbtn"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:layout_centerHorizontal="true"
        android:layout_below="@+id/dialogfragment_textview"
        android:text="@string/accept"
        />

    <Button
        android:id="@+id/dialogfragment_rejectbtn"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:layout_alignLeft="@+id/dialogfragment_acceptbtn"
        android:layout_below="@+id/dialogfragment_acceptbtn"
        android:text="@string/decline" />

     <Button
        android:id="@+id/dialogfragment_cancelbtn"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:layout_marginBottom="20dp"
        android:layout_alignLeft="@+id/dialogfragment_rejectbtn"
        android:layout_below="@+id/dialogfragment_rejectbtn"
        android:text="@string/cancel" />

     <Button
        android:id="@+id/dialogfragment_heightfixhiddenbtn"
        android:layout_width="200dp"
        android:layout_height="20dp"
        android:layout_marginTop="10dp"
        android:layout_marginBottom="20dp"
        android:layout_alignLeft="@+id/dialogfragment_cancelbtn"
        android:layout_below="@+id/dialogfragment_cancelbtn"
        android:background="@android:color/transparent"
        android:enabled="false"
        android:text=" " />
</RelativeLayout>

Comme son nom l' dialogfragment_heightfixhiddenbtnindique, je ne pouvais tout simplement pas trouver un moyen de corriger le fait que la hauteur du bouton du bas était réduite de moitié malgré le fait wrap_contentque je l'ai dit , alors j'ai ajouté un bouton caché pour être "coupé" en deux à la place. Désolé pour le hack.

EpicPandaForce
la source
1
+1 le jeu de référence avec setTargetFragment()est recréé correctement par le système lorsqu'il redémarre le jeu d'activité / fragment après la rotation. Ainsi, la référence pointera automatiquement vers la nouvelle cible.
Richard Le Mesurier
Je me frapperais le nez maintenant pour ne pas avoir utilisé ButterKnife à l'époque, cependant. Utilisez ButterKnife, cela rend votre vue beaucoup plus jolie.
EpicPandaForce
Et vous pouvez utiliser Otto pour envoyer un événement de Fragment à Activity, de sorte que vous n'ayez pas besoin de l'interface pour attacher la magie. Exemple Otto ici: stackoverflow.com/a/28480952/2413303
EpicPandaForce
@EpicPandaForce Pouvez-vous ajouter l'interface / la classe "ShowDialog"? C'est la seule chose qui manque dans votre exemple.
ntrch le
@ntrch it waspublic interface ShowDialog { void showDialog(DialogFragment dialogFragment); }
EpicPandaForce
3
 public void showAlert(){


     AlertDialog.Builder alertDialog = new AlertDialog.Builder(getActivity());
     LayoutInflater inflater = getActivity().getLayoutInflater();
     View alertDialogView = inflater.inflate(R.layout.test_dialog, null);
     alertDialog.setView(alertDialogView);

     TextView textDialog = (TextView) alertDialogView.findViewById(R.id.text_testDialogMsg);
     textDialog.setText(questionMissing);

     alertDialog.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
         public void onClick(DialogInterface dialog, int which) {
             dialog.cancel();
         }
     });
     alertDialog.show();

}

où .test_dialog est de xml personnalisé

Alex Zaraos
la source
2

Je suis moi-même un débutant et je n'ai honnêtement pas trouvé de réponse satisfaisante que je pourrais comprendre ou mettre en œuvre.

Voici donc un lien externe qui m'a vraiment aidé à réaliser ce que je voulais. C'est très simple et facile à suivre également.

http://www.helloandroid.com/tutorials/how-display-custom-dialog-your-android-application

CE QUE J'AI ESSAYÉ D'OBTENIR AVEC LE CODE:

J'ai une MainActivity qui héberge un Fragment. Je voulais qu'une boîte de dialogue apparaisse en haut de la mise en page pour demander une entrée utilisateur, puis traiter l'entrée en conséquence. Voir une capture d'écran

Voici à quoi ressemble onCreateView de mon fragment

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {

    View rootView = inflater.inflate(R.layout.fragment_home_activity, container, false);

    Button addTransactionBtn = rootView.findViewById(R.id.addTransactionBtn);

    addTransactionBtn.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            Dialog dialog = new Dialog(getActivity());
            dialog.setContentView(R.layout.dialog_trans);
            dialog.setTitle("Add an Expense");
            dialog.setCancelable(true);

            dialog.show();

        }
    });

J'espère que ça t'aidera

Faites-moi savoir s'il y a une confusion. :)

Junaid Aziz
la source
0
    public static void OpenDialog (Activity activity, DialogFragment fragment){

    final FragmentManager fm = ((FragmentActivity)activity).getSupportFragmentManager();

    fragment.show(fm, "tag");
}
Elmar Fazlagic
la source
3
s'il vous plaît ajouter quelques explications à votre réponse
RealCheeseLord