Android DialogFragment vs Dialog

Google empfiehlt, DialogFragment statt eines einfachen DialogFragment mit der Fragments API , aber es ist absurd, ein isoliertes DialogFragment für ein einfaches Ja-Nein-Bestätigungsfenster zu verwenden. Was ist in diesem Fall die beste Vorgehensweise?

Ja, benutze DialogFragment und in onCreateDialog kannst du einfach einen AlertDialog Builder verwenden, um einen einfachen AlertDialog mit Ja / Nein-Bestätigungsschaltflächen zu erstellen. Nicht sehr viel Code.

In Bezug auf die Behandlung von Ereignissen in Ihrem Fragment gibt es verschiedene Möglichkeiten, dies zu tun, aber ich definiere einfach einen Nachrichtenhandler in meinem Fragment , übergebe ihn über seinen Konstruktor an das DialogFragment und übergebe dann Nachrichten an den Handler meines Fragments entsprechend den verschiedenen Klickereignissen . Wieder verschiedene Möglichkeiten, das zu tun, aber das Folgende funktioniert für mich.

Halten Sie im Dialog eine Nachricht und instanziieren Sie sie im Konstruktor:

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

Implementieren Sie den onClickListener in Ihrem Dialog und rufen Sie dann den Handler entsprechend auf:

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

Bearbeiten

Und da die Message parzellierbar ist, können Sie sie in onSaveInstanceState und wiederherstellen

 outState.putParcelable("okMessage", okMessage); 

Dann in onCreate

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

Sie können generische DialogFragment-Unterklassen wie YesNoDialog und OkDialog erstellen und Titel und Nachrichten übergeben, wenn Sie in Ihrer App häufig Dialoge verwenden.

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

Dann rufe es wie folgt auf:

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

Und behandeln Sie das Ergebnis in onActivityResult .

Verwenden Sie DialogFragment über AlertDialog:


  • Seit der Einführung von API Level 13 :

    Die showDialog- Methode von Activity ist veraltet . Es ist nicht ratsam, einen Dialog an einer anderen Stelle im Code aufzurufen, da Sie den Dialog selbst verwalten müssen (zB Orientierungsänderung).

  • Unterschied DialogFragment – AlertDialog

    Sind sie so verschieden? Von Android-Referenz zu DialogFragment :

    Ein DialogFragment ist ein Fragment, das ein Dialogfenster anzeigt, das über dem Fenster seiner Aktivität schwebt. Dieses Fragment enthält ein Dialog-Objekt, das basierend auf dem Status des Fragments entsprechend angezeigt wird. Die Steuerung des Dialogs (entscheiden, wann er angezeigt, ausgeblendet oder ausgeblendet werden soll) sollte über die API erfolgen, nicht über direkte Aufrufe des Dialogs.

  • Weitere Hinweise

    • Fragmente sind eine natürliche Entwicklung im Android-Framework aufgrund der Vielfalt der Geräte mit unterschiedlichen Bildschirmgrößen.
    • DialogFragments und Fragmente werden in der Support-Bibliothek zur Verfügung gestellt, wodurch die class in allen aktuell verwendeten Android-Versionen nutzbar ist.

Ich würde empfehlen, DialogFragment .

Sicher, das Erstellen eines “Ja / Nein” -Dialogs ist ziemlich komplex, wenn man bedenkt, dass es eine ziemlich einfache Aufgabe sein sollte, aber das Erstellen eines ähnlichen Dialogfeldes mit Dialog ist ebenfalls überraschend kompliziert.

(Aktivitätslebenszyklus macht es kompliziert – Sie müssen die Activity den Lebenszyklus des Dialogfelds verwalten lassen – und es gibt keine Möglichkeit, benutzerdefinierte Parameter zu übergeben, zB die benutzerdefinierte Nachricht an Activity.showDialog wenn API-Levels unter 8 verwendet werden)

Das DialogFragment ist, dass Sie in der Regel Ihre eigene Abstraktion ganz einfach über DialogFragment können.

Generisches AlertDialogFragment mit Builder-Muster

In meinem Projekt habe ich AlertDialog.Builder bereits sehr AlertDialog.Builder benutzt, bevor ich herausgefunden habe, dass es problematisch ist. Ich wollte jedoch nicht so viele Codes in meiner App ändern. Außerdem bin ich ein Fan von OnClickListeners als anonyme classn, wo sie benötigt werden (das heißt, wenn setPositiveButton() , setNegativeButton() usw.) statt Tausende von Callback-Methoden zu implementieren, um zwischen einem Dialogfragment und der Halterfragment, das meiner Meinung nach zu sehr verwirrendem und komplexem Code führen kann. Insbesondere, wenn Sie mehrere verschiedene Dialoge in einem Fragment haben und dann in den Callback-Implementierungen unterscheiden müssen, zwischen welchem ​​Dialog gerade angezeigt wird.

Daher habe ich verschiedene Ansätze kombiniert, um eine generische AlertDialogFragment Helper-class zu erstellen, die genau wie AlertDialog :


LÖSUNG

( BITTE BEACHTEN SIE, dass ich Java 8 Lambda-Ausdrücke in meinem Code verwende, so dass Sie möglicherweise Teile des Codes ändern müssen, wenn Sie noch keine Lambda-Ausdrücke verwenden .)

 /** * 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 String 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().getAttributes().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.getStringArray("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(String 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 titleStringId) { return setTitle(activity.getString(titleStringId)); } public AlertDialogFragment setTitle(CharSequence title) { args.putCharSequence("title", title); return this; } public AlertDialogFragment setMessage(int messageStringId) { return setMessage(activity.getString(messageStringId)); } public AlertDialogFragment setMessage(CharSequence message) { args.putCharSequence("message", message); return this; } public AlertDialogFragment setPositiveButton(int textStringId, OnClickListener onClickListener) { return setPositiveButton(activity.getString(textStringId), onClickListener); } public AlertDialogFragment setPositiveButton(CharSequence text, AlertDialogFragment.OnClickListener onClickListener) { args.putCharSequence("positiveButtonText", text); args.putParcelable("positiveButtonListener", createParcelableOnClickListener(onClickListener)); return this; } public AlertDialogFragment setNegativeButton(int textStringId, AlertDialogFragment.OnClickListener onClickListener) { return setNegativeButton(activity.getString(textStringId), onClickListener); } public AlertDialogFragment setNegativeButton(CharSequence text, AlertDialogFragment.OnClickListener onClickListener) { args.putCharSequence("negativeButtonText", text); args.putParcelable("negativeButtonListener", createParcelableOnClickListener(onClickListener)); return this; } public AlertDialogFragment setNeutralButton(int textStringId, AlertDialogFragment.OnClickListener onClickListener) { return setNeutralButton(activity.getString(textStringId), 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(String[] items, AlertDialogFragment.OnClickListener onClickListener) { args.putStringArray("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(String 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, String tag) { throw new NoSuchMethodError("Please use AlertDialogFragment.show()!"); } @Override public void show(FragmentManager manager, String 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 }

VERWENDUNG

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

Ich poste dies hier nicht nur, um meine Lösung zu teilen, sondern auch, weil ich Sie um Ihre Meinung fragen wollte: Ist dieser Ansatz in gewissem Maße legitim oder problematisch?

Darf ich eine kleine Vereinfachung von @ ashishduhs Antwort vorschlagen:

 public class AlertDialogFragment extends DialogFragment { public static final String ARG_TITLE = "AlertDialog.Title"; public static final String ARG_MESSAGE = "AlertDialog.Message"; public static void showAlert(String title, String message, Fragment targetFragment) { DialogFragment dialog = new AlertDialogFragment(); Bundle args = new Bundle(); args.putString(ARG_TITLE, title); args.putString(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(); String title = args.getString(ARG_TITLE, ""); String message = args.getString(ARG_MESSAGE, ""); return new AlertDialog.Builder(getActivity()) .setTitle(title) .setMessage(message) .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { getTargetFragment().onActivityResult(getTargetRequestCode(), Activity.RESULT_OK, null); } }) .create(); } 

Es beseitigt die Notwendigkeit, dass der Benutzer (der class) mit den Interna der Komponente vertraut ist und die Verwendung wirklich einfach macht:

 AlertDialogFragment.showAlert(title, message, this); 

PS In meinem Fall brauchte ich einen einfachen Alarmdialog, also habe ich das erstellt. Sie können den Ansatz auf ein Ja / Nein oder einen anderen Typ anwenden, den Sie benötigen.