DialogFragment de Android vs Diálogo

Google recomienda que utilicemos DialogFragment lugar de un DialogFragment de Dialog simple mediante el uso de la Fragments API , pero es absurdo utilizar un DialogFragment aislado para un simple cuadro de mensaje de confirmación Sí-No. ¿Cuál es la mejor práctica en este caso?

Sí, use DialogFragment y en onCreateDialog simplemente puede usar un constructor AlertDialog de todos modos para crear un AlertDialog simple con los botones de confirmación Yes / No. No mucho código en absoluto.

Con respecto a la manipulación de eventos en su fragmento habría varias maneras de hacerlo, pero simplemente definir un Handler mensajes en mi Fragment , pasarlo a la DialogFragment través de su constructor y luego pasar los mensajes de nuevo al manejador de mi fragmento como apropiarse en los diversos eventos de clic . De nuevo varias maneras de hacer eso, pero el siguiente funciona para mí.

En el diálogo mantenga un mensaje e instántelo en el constructor:

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

Implementar el onClickListener en su diálogo y luego llamar al manejador según corresponda:

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

Editar

Y como Message es parcelable puedes guardarlo en onSaveInstanceState y restaurarlo

 outState.putParcelable("okMessage", okMessage); 

Entonces en onCreate

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

Puede crear subclases genéricas de DialogFragment como YesNoDialog y OkDialog, y pasar el título y el mensaje si utiliza diálogos mucho en su aplicación.

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

Luego llámalo usando lo siguiente:

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

Y manejar el resultado en onActivityResult .

Yo recomendaría usar DialogFragment .

Claro, la creación de un "Sí / No" diálogo con él es bastante complejo teniendo en cuenta que debe ser una tarea bastante simple, pero la creación de un cuadro de diálogo similar con el Dialog es sorprendentemente complicado también.

(El ciclo de vida de la actividad hace que sea complicado – debe dejar que la Activity administre el ciclo de vida del cuadro de diálogo – y no hay manera de pasar parámetros personalizados, por ejemplo, el mensaje personalizado a Activity.showDialog si se utilizan niveles de API en 8)

Lo bueno es que por lo general puede construir su propia abstracción en la parte superior de DialogFragment muy fácilmente.

Utilice DialogFragment sobre AlertDialog:


  • Desde la introducción del API nivel 13 :

    El método showDialog de Activity está obsoleto . No es aconsejable invocar un diálogo en otro lugar del código, ya que tendrá que administrar el diálogo usted mismo (por ejemplo, cambio de orientación).

  • Diferencia DialogFragment – AlertDialog

    ¿Son tan diferentes? De la referencia de Android con respecto a DialogFragment :

    Un DialogFragment es un fragmento que muestra una ventana de diálogo, flotando en la parte superior de la ventana de su actividad. Este fragmento contiene un objeto Dialog, que se muestra como apropiado sobre la base del estado del fragmento. El control del diálogo (decidir cuándo mostrarlo, ocultarlo, despedirlo) debe hacerse a través de la API aquí , no con llamadas directas en el diálogo.

  • Otras notas

    • Fragmentos son una evolución natural en el marco de Android debido a la diversidad de dispositivos con diferentes tamaños de pantalla.
    • DialogFragments y Fragments están disponibles en la biblioteca de soporte que hace que la clase sea utilizable en todas las versiones actuales usadas de Android.

Generic AlertDialogFragment con el patrón Builder

En mi proyecto, ya usé AlertDialog.Builder ya mucho antes de descubrir que es problemático. Sin embargo, no quería cambiar ese código en ninguna parte de mi aplicación. Además, en realidad soy un fan de pasar OnClickListeners como clases anónimas donde se necesitan (es decir, cuando se utiliza setPositiveButton() , setNegativeButton() etc.) en lugar de tener que implementar miles de métodos de devolución de llamada para comunicarse entre un fragmento de diálogo y el Que puede, a mi juicio, conducir a un código muy confuso y complejo. Especialmente, si tiene varios diálogos diferentes en un fragmento y luego necesita distinguir en las implementaciones de devolución de llamada entre qué diálogo se está mostrando actualmente.

Por lo tanto, combiné diferentes enfoques para crear una clase genérica AlertDialogFragment auxiliar que se puede utilizar exactamente como AlertDialog :


SOLUCIÓN

( OBSERVE POR FAVOR que estoy usando las expresiones lambda de Java 8 en mi código, así que usted puede ser que tenga que cambiar partes del código si usted no está utilizando expresiones lambda todavía.)

 /** * Helper class for dialog fragments to show a {@link AlertDialog}. It can be used almost exactly * like a {@link AlertDialog.Builder} * <p /> * 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<ResultReceiver> 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<ResultReceiver> CREATOR = ResultReceiver.CREATOR; ParcelableOnDismissListener() { super(null); } @Override public abstract void onDismiss(AlertDialogFragment dialogFragment); } // ============================================================================================= // endregion } 

USO

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

Estoy publicando esto aquí no sólo para compartir mi solución, sino también porque quería pedirle a la gente su opinión: ¿Es este enfoque legítimo o problemático hasta cierto punto?

  • Establecer fuente personalizada para fragmentos de Android
  • ¿Los Fragmentos y las Actividades de Fragmentos son inherentemente más rápidos que las Actividades?
  • Actividad y interacción de fragmentos
  • AccountAuthenticatorActivity y fragmentos
  • No se puede mantener fragmentos en vivo a través de cambiar la orientación o Guardar fragmento de interfaz de usuario
  • ¿Cómo cambiar la actividad en el botón de navegación inferior?
  • ¿Cómo puedo conservar el estado de un elemento giratorio / desplegable seleccionado en el cambio de orientación?
  • Tabulaciones de Fragmentos y Actividades de Fragmentos
  • Añadiendo un fragmento en la parte superior de otro fragmento onClickListener
  • Android RecyclerView que se encuentra en un fragmento que se encuentra en un ViewPager no vuelve a cargar / refrescar las vistas si se alcanza el setOffscreenPageLimit
  • Referencia de fragmentos dentro de ViewPager
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.