DialogFragment Android vs Dialog

Google recommande d’utiliser DialogFragment au lieu d’un simple Dialog à l’aide de l’ Fragments API , mais il est absurde d’utiliser un DialogFragment isolé pour un simple message de confirmation Yes-No. Quelle est la meilleure pratique dans ce cas?

Oui, utilisez DialogFragment et dans onCreateDialog vous pouvez simplement utiliser un générateur AlertDialog pour créer un AlertDialog simple avec des boutons de confirmation Oui / Non. Pas beaucoup de code du tout.

En ce qui concerne la gestion des événements dans votre fragment, il y a plusieurs façons de le faire, mais je définis simplement un Handler message dans mon Fragment , le transmets au DialogFragment via son constructeur puis renvoie les messages au gestionnaire de mon fragment. . Encore une fois différentes façons de le faire, mais ce qui suit fonctionne pour moi.

Dans la boîte de dialog, maintenez un message et instanciez-le dans le constructeur:

 private Message okMessage; ... okMessage = handler.obtainMessage(MY_MSG_WHAT, MY_MSG_OK); 

Implémentez le onClickListener dans votre boîte de dialog, puis appelez le gestionnaire comme il convient:

 public void onClick(..... if (which == DialogInterface.BUTTON_POSITIVE) { final Message toSend = Message.obtain(okMessage); toSend.sendToTarget(); } } 

modifier

Et comme Message est parcellable, vous pouvez l’enregistrer dans onSaveInstanceState et le restaurer

 outState.putParcelable("okMessage", okMessage); 

Puis dans onCreate

 if (savedInstanceState != null) { okMessage = savedInstanceState.getParcelable("okMessage"); } 

Vous pouvez créer des sous-classes DialogFragment génériques comme YesNoDialog et OkDialog, et transmettre le titre et le message si vous utilisez beaucoup les dialogs dans votre application.

 public class YesNoDialog extends DialogFragment { public static final Ssortingng ARG_TITLE = "YesNoDialog.Title"; public static final Ssortingng ARG_MESSAGE = "YesNoDialog.Message"; public YesNoDialog() { } @Override public Dialog onCreateDialog(Bundle savedInstanceState) { Bundle args = getArguments(); Ssortingng title = args.getSsortingng(ARG_TITLE); Ssortingng message = args.getSsortingng(ARG_MESSAGE); return new AlertDialog.Builder(getActivity()) .setTitle(title) .setMessage(message) .setPositiveButton(android.R.ssortingng.yes, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { getTargetFragment().onActivityResult(getTargetRequestCode(), Activity.RESULT_OK, null); } }) .setNegativeButton(android.R.ssortingng.no, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { getTargetFragment().onActivityResult(getTargetRequestCode(), Activity.RESULT_CANCELED, null); } }) .create(); } } 

Puis appelez-le en utilisant ce qui suit:

  DialogFragment dialog = new YesNoDialog(); Bundle args = new Bundle(); args.putSsortingng(YesNoDialog.ARG_TITLE, title); args.putSsortingng(YesNoDialog.ARG_MESSAGE, message); dialog.setArguments(args); dialog.setTargetFragment(this, YES_NO_CALL); dialog.show(getFragmentManager(), "tag"); 

Et gérez le résultat dans onActivityResult .

Utilisez DialogFragment sur AlertDialog:


  • Depuis l’introduction du niveau 13 de l’API :

    la méthode showDialog from Activity est obsolète . Invoquer une boîte de dialog ailleurs dans le code n’est pas conseillé car vous devrez gérer vous-même la boîte de dialog (par exemple, changement d’orientation).

  • DialogFragment des différences – AlertDialog

    Sont-ils tellement différents? De la référence Android concernant DialogFragment :

    Un DialogFragment est un fragment qui affiche une fenêtre de dialog, flottant au-dessus de la fenêtre de son activité. Ce fragment contient un object Dialog, qu’il affiche comme approprié en fonction de l’état du fragment. Le contrôle de la boîte de dialog (décider quand afficher, masquer, rejeter) doit être effectué via l’API ici , pas avec les appels directs dans la boîte de dialog.

  • Autres notes

    • Les fragments sont une évolution naturelle dans le cadre d’Android en raison de la diversité des appareils avec des tailles d’écran différentes.
    • DialogFragments et Fragments sont disponibles dans la bibliothèque de support, ce qui rend la classe utilisable dans toutes les versions utilisées d’Android.

Je recommande d’utiliser DialogFragment .

Bien sûr, créer une boîte de dialog “Oui / Non” avec elle est assez complexe étant donné que cela devrait être une tâche plutôt simple, mais créer une boîte de dialog similaire avec Dialog est étonnamment compliqué.

(Le cycle de vie d’activité complique les choses – vous devez laisser Activity gérer le cycle de vie de la boîte de dialog – et il n’y a aucun moyen de transmettre des parameters personnalisés, par exemple le message personnalisé à Activity.showDialog si vous utilisez des niveaux API inférieurs à 8)

La bonne chose est que vous pouvez généralement construire votre propre abstraction sur DialogFragment assez facilement.

AlertDialogFragment générique avec modèle de générateur

Dans mon projet, j’ai déjà beaucoup utilisé AlertDialog.Builder avant de découvrir que cela posait problème. Cependant, je ne voulais pas changer autant de code dans mon application. En outre, je suis fan de passer OnClickListeners tant que classes anonymes où elles sont nécessaires (c’est-à-dire avec setPositiveButton() , setNegativeButton() etc.) au lieu d’implémenter des milliers de méthodes de rappel pour titulaire fragment, qui peut, à mon avis, conduire à un code très confus et complexe. Surtout si vous avez plusieurs dialogs différents dans un fragment et que vous devez ensuite les distinguer dans les implémentations de rappel entre lesquelles le dialog est actuellement affiché.

Par conséquent, j’ai combiné différentes approches pour créer une classe d’assistance AlertDialogFragment générique qui peut être utilisée exactement comme AlertDialog :


SOLUTION

( VEUILLEZ NOTER que j’utilise des expressions lambda Java 8 dans mon code, vous devrez peut-être modifier certaines parties du code si vous n’utilisez pas encore des expressions lambda .)

 /** * Helper class for dialog fragments to show a {@link AlertDialog}. It can be used almost exactly * like a {@link AlertDialog.Builder} * 

* Creation Date: 22.03.16 * * @author felix, http://flx-apps.com/ */ public class AlertDialogFragment extends DialogFragment { protected FragmentActivity activity; protected Bundle args; protected Ssortingng tag = AlertDialogFragment.class.getSimpleName(); @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); activity = getActivity(); args = getArguments(); } @NonNull @Override public Dialog onCreateDialog(Bundle savedInstanceState) { Dialog dialog = setDialogDefaults(new AlertDialog.Builder(getActivity())).create(); if (args.containsKey("gravity")) { dialog.getWindow().getAtsortingbutes().gravity = args.getInt("gravity"); } dialog.setOnShowListener(d -> { if (dialog != null && dialog.findViewById((android.R.id.message)) != null) { ((TextView) dialog.findViewById(android.R.id.message)).setMovementMethod(LinkMovementMethod.getInstance()); } }); return dialog; } @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { return super.onCreateView(inflater, container, savedInstanceState); } @Override public void onDismiss(DialogInterface dialog) { super.onDismiss(dialog); if (args.containsKey("onDismissListener")) { Parcelable onDismissListener = args.getParcelable("onDismissListener"); if (onDismissListener != null && onDismissListener instanceof ParcelableOnDismissListener) { ((ParcelableOnDismissListener) onDismissListener).onDismiss(this); } } } /** * Sets default dialog properties by arguments which were set using {@link #builder(FragmentActivity)} */ protected AlertDialog.Builder setDialogDefaults(AlertDialog.Builder builder) { args = getArguments(); activity = getActivity(); if (args.containsKey("title")) { builder.setTitle(args.getCharSequence("title")); } if (args.containsKey("message")) { CharSequence message = args.getCharSequence("message"); builder.setMessage(message); } if (args.containsKey("viewId")) { builder.setView(getActivity().getLayoutInflater().inflate(args.getInt("viewId"), null)); } if (args.containsKey("positiveButtonText")) { builder.setPositiveButton(args.getCharSequence("positiveButtonText"), (dialog, which) -> { onButtonClicked("positiveButtonListener", which); }); } if (args.containsKey("negativeButtonText")) { builder.setNegativeButton(args.getCharSequence("negativeButtonText"), (dialog, which) -> { onButtonClicked("negativeButtonListener", which); }); } if (args.containsKey("neutralButtonText")) { builder.setNeutralButton(args.getCharSequence("neutralButtonText"), (dialog, which) -> { onButtonClicked("neutralButtonListener", which); }); } if (args.containsKey("items")) { builder.setItems(args.getSsortingngArray("items"), (dialog, which) -> { onButtonClicked("itemClickListener", which); }); } // @formatter:off // FIXME this a pretty hacky workaround: we don't want to show the dialog if onClickListener of one of the dialog's button click listener were lost // the problem is, that there is no (known) solution for parceling a OnClickListener in the long term (only for state changes like orientation change, // but not if the Activity was completely lost) if ( (args.getParcelable("positiveButtonListener") != null && !(args.getParcelable("positiveButtonListener") instanceof ParcelableOnClickListener)) || (args.getParcelable("negativeButtonListener") != null && !(args.getParcelable("negativeButtonListener") instanceof ParcelableOnClickListener)) || (args.getParcelable("neutralButtonListener") != null && !(args.getParcelable("neutralButtonListener") instanceof ParcelableOnClickListener)) || (args.getParcelable("itemClickListener") != null && !(args.getParcelable("itemClickListener") instanceof ParcelableOnClickListener)) ) { new DebugMessage("Forgot onClickListener. Needs to be dismissed.") .logLevel(DebugMessage.LogLevel.VERBOSE) .show(); try { dismissAllowingStateLoss(); } catch (NullPointerException | IllegalStateException ignored) {} } // @formatter:on return builder; } public interface OnDismissListener { void onDismiss(AlertDialogFragment dialogFragment); } public interface OnClickListener { void onClick(AlertDialogFragment dialogFragment, int which); } protected void onButtonClicked(Ssortingng buttonKey, int which) { ParcelableOnClickListener parcelableOnClickListener = getArguments().getParcelable(buttonKey); if (parcelableOnClickListener != null) { parcelableOnClickListener.onClick(this, which); } } // region Convenience Builder Pattern class almost similar to AlertDialog.Builder // ============================================================================================= public AlertDialogFragment builder(FragmentActivity activity) { this.activity = activity; this.args = new Bundle(); return this; } public AlertDialogFragment addArguments(Bundle bundle) { args.putAll(bundle); return this; } public AlertDialogFragment setTitle(int titleSsortingngId) { return setTitle(activity.getSsortingng(titleSsortingngId)); } public AlertDialogFragment setTitle(CharSequence title) { args.putCharSequence("title", title); return this; } public AlertDialogFragment setMessage(int messageSsortingngId) { return setMessage(activity.getSsortingng(messageSsortingngId)); } public AlertDialogFragment setMessage(CharSequence message) { args.putCharSequence("message", message); return this; } public AlertDialogFragment setPositiveButton(int textSsortingngId, OnClickListener onClickListener) { return setPositiveButton(activity.getSsortingng(textSsortingngId), onClickListener); } public AlertDialogFragment setPositiveButton(CharSequence text, AlertDialogFragment.OnClickListener onClickListener) { args.putCharSequence("positiveButtonText", text); args.putParcelable("positiveButtonListener", createParcelableOnClickListener(onClickListener)); return this; } public AlertDialogFragment setNegativeButton(int textSsortingngId, AlertDialogFragment.OnClickListener onClickListener) { return setNegativeButton(activity.getSsortingng(textSsortingngId), onClickListener); } public AlertDialogFragment setNegativeButton(CharSequence text, AlertDialogFragment.OnClickListener onClickListener) { args.putCharSequence("negativeButtonText", text); args.putParcelable("negativeButtonListener", createParcelableOnClickListener(onClickListener)); return this; } public AlertDialogFragment setNeutralButton(int textSsortingngId, AlertDialogFragment.OnClickListener onClickListener) { return setNeutralButton(activity.getSsortingng(textSsortingngId), onClickListener); } public AlertDialogFragment setNeutralButton(CharSequence text, AlertDialogFragment.OnClickListener onClickListener) { args.putCharSequence("neutralButtonText", text); args.putParcelable("neutralButtonListener", createParcelableOnClickListener(onClickListener)); return this; } public AlertDialogFragment setOnDismissListener(OnDismissListener onDismissListener) { if (onDismissListener == null) { return this; } Parcelable p = new ParcelableOnDismissListener() { @Override public void onDismiss(AlertDialogFragment dialogFragment) { onDismissListener.onDismiss(dialogFragment); } }; args.putParcelable("onDismissListener", p); return this; } public AlertDialogFragment setItems(Ssortingng[] items, AlertDialogFragment.OnClickListener onClickListener) { args.putSsortingngArray("items", items); args.putParcelable("itemClickListener", createParcelableOnClickListener(onClickListener)); return this; } public AlertDialogFragment setView(int viewId) { args.putInt("viewId", viewId); return this; } public AlertDialogFragment setGravity(int gravity) { args.putInt("gravity", gravity); return this; } public AlertDialogFragment setTag(Ssortingng tag) { this.tag = tag; return this; } public AlertDialogFragment create() { setArguments(args); return AlertDialogFragment.this; } public AlertDialogFragment show() { create(); try { super.show(activity.getSupportFragmentManager(), tag); } catch (IllegalStateException e1) { /** * this whole part is used in order to attempt to show the dialog if an * {@link IllegalStateException} was thrown (it's kinda comparable to * {@link FragmentTransaction#commitAllowingStateLoss()} * So you can remove all those dirty hacks if you are sure that you are always * properly showing dialogs in the right moments */ new DebugMessage("got IllegalStateException attempting to show dialog. trying to hack around.") .logLevel(DebugMessage.LogLevel.WARN) .exception(e1) .show(); try { Field mShownByMe = DialogFragment.class.getDeclaredField("mShownByMe"); mShownByMe.setAccessible(true); mShownByMe.set(this, true); Field mDismissed = DialogFragment.class.getDeclaredField("mDismissed"); mDismissed.setAccessible(true); mDismissed.set(this, false); } catch (Exception e2) { new DebugMessage("error while showing dialog") .exception(e2) .logLevel(DebugMessage.LogLevel.ERROR) .show(); } FragmentTransaction transaction = activity.getSupportFragmentManager().beginTransaction(); transaction.add(this, tag); transaction.commitAllowingStateLoss(); // FIXME hacky and unpredictable workaround } return AlertDialogFragment.this; } @Override public int show(FragmentTransaction transaction, Ssortingng tag) { throw new NoSuchMethodError("Please use AlertDialogFragment.show()!"); } @Override public void show(FragmentManager manager, Ssortingng tag) { throw new NoSuchMethodError("Please use AlertDialogFragment.show()!"); } protected ParcelableOnClickListener createParcelableOnClickListener(AlertDialogFragment.OnClickListener onClickListener) { if (onClickListener == null) { return null; } return new ParcelableOnClickListener() { @Override public void onClick(AlertDialogFragment dialogFragment, int which) { onClickListener.onClick(dialogFragment, which); } }; } /** * Parcelable OnClickListener (can be remembered on screen rotation) */ public abstract static class ParcelableOnClickListener extends ResultReceiver implements AlertDialogFragment.OnClickListener { public static final Creator CREATOR = ResultReceiver.CREATOR; ParcelableOnClickListener() { super(null); } @Override public abstract void onClick(AlertDialogFragment dialogFragment, int which); } /** * Parcelable OnDismissListener (can be remembered on screen rotation) */ public abstract static class ParcelableOnDismissListener extends ResultReceiver implements AlertDialogFragment.OnDismissListener { public static final Creator CREATOR = ResultReceiver.CREATOR; ParcelableOnDismissListener() { super(null); } @Override public abstract void onDismiss(AlertDialogFragment dialogFragment); } // ============================================================================================= // endregion }

USAGE

 // showing a normal alert dialog with state loss on configuration changes (like device rotation) new AlertDialog.Builder(getActivity()) .setTitle("Are you sure? (1)") .setMessage("Do you really want to do this?") .setPositiveButton("Yes", (dialog, which) -> Toast.makeText(getContext(), "Yes clicked", Toast.LENGTH_SHORT).show()) .setNegativeButton("Cancel", null) .show(); // showing a dialog fragment using the helper class with no state loss on configuration changes new AlertDialogFragment.builder(getActivity()) .setTitle("Are you sure? (2)") .setMessage("Do you really want to do this?") .setPositiveButton("Yes", (dialog, which) -> Toast.makeText(getContext(), "Yes clicked", Toast.LENGTH_SHORT).show()) .setNegativeButton("Cancel", null) .show(); 

Je poste ceci ici non seulement pour partager ma solution, mais aussi parce que je voulais vous demander votre avis: cette approche est-elle légitime ou problématique dans une certaine mesure?

Puis-je suggérer une petite simplification de la réponse de @ ashishduh:

 public class AlertDialogFragment extends DialogFragment { public static final Ssortingng ARG_TITLE = "AlertDialog.Title"; public static final Ssortingng ARG_MESSAGE = "AlertDialog.Message"; public static void showAlert(Ssortingng title, Ssortingng message, Fragment targetFragment) { DialogFragment dialog = new AlertDialogFragment(); Bundle args = new Bundle(); args.putSsortingng(ARG_TITLE, title); args.putSsortingng(ARG_MESSAGE, message); dialog.setArguments(args); dialog.setTargetFragment(targetFragment, 0); dialog.show(targetFragment.getFragmentManager(), "tag"); } public AlertDialogFragment() {} @NonNull @Override public AlertDialog onCreateDialog(Bundle savedInstanceState) { Bundle args = getArguments(); Ssortingng title = args.getSsortingng(ARG_TITLE, ""); Ssortingng message = args.getSsortingng(ARG_MESSAGE, ""); return new AlertDialog.Builder(getActivity()) .setTitle(title) .setMessage(message) .setPositiveButton(android.R.ssortingng.ok, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { getTargetFragment().onActivityResult(getTargetRequestCode(), Activity.RESULT_OK, null); } }) .create(); } 

Cela évite à l’utilisateur (de la classe) de se familiariser avec les composants internes du composant et rend l’utilisation très simple:

 AlertDialogFragment.showAlert(title, message, this); 

PS Dans mon cas, j’avais besoin d’un simple dialog d’alerte, c’est ce que j’ai créé. Vous pouvez appliquer l’approche à un Oui / Non ou à tout autre type dont vous avez besoin.