Android DialogFragment vs Dialog
Google рекомендует использовать DialogFragment вместо простого Dialog С помощью Fragments API, но абсурдно использовать изолированный DialogFragment для простого окна подтверждения Да-нет. Какова наилучшая практика в этом случае?
6 ответов:
Да, использовать
DialogFragmentиonCreateDialogвы можете просто использовать AlertDialog builder в любом случае, чтобы создать простойAlertDialogс кнопками подтверждения Да/нет. Не очень много кода.что касается обработки событий в вашем фрагменте будут различные способы сделать это, но я просто определяю сообщение
Handlerв своемFragment, передать его вDialogFragmentчерез его конструктор, а затем передавать сообщения обратно в обработчик моего фрагмента, как присваивать на различных событиях щелчка. Снова различные способы сделать это, но следующие работы для меня.в диалоговом окне удерживайте сообщение и создайте его экземпляр в конструкторе:
private Message okMessage; ... okMessage = handler.obtainMessage(MY_MSG_WHAT, MY_MSG_OK);реализовать
onClickListenerв диалоговом окне, а затем вызвать обработчик соответствующим образом:public void onClick(..... if (which == DialogInterface.BUTTON_POSITIVE) { final Message toSend = Message.obtain(okMessage); toSend.sendToTarget(); } }Edit
и
Messageэто parcelable вы можете сохранить его вonSaveInstanceStateи восстановить егоoutState.putParcelable("okMessage", okMessage);затем в
onCreateif (savedInstanceState != null) { okMessage = savedInstanceState.getParcelable("okMessage"); }
вы можете создавать общие подклассы DialogFragment, такие как YesNoDialog и OkDialog, и передавать заголовок и сообщение, если вы часто используете диалоги в своем приложении.
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(); } }затем вызовите его, используя следующее:
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");и обрабатывать результат в
onActivityResult.
использовать DialogFragment над AlertDialog:
С момента введения API уровня 13:
the showDialog метод от деятельности устаревший. Вызов диалога в другом месте кода не рекомендуется, так как вам придется управлять диалогом самостоятельно (например, изменение ориентации).
Разница DialogFragment - AlertDialog
они так сильно отличаются? Из ссылки на Android относительно DialogFragment:
DialogFragment-это фрагмент, который отображает диалоговое окно, плавающее поверх его окно активности. Этот фрагмент содержит объект диалога, который он отображение в зависимости от состояния фрагмента. Контроль над диалог (решая, когда показывать, скрывать, отклонять его) должен быть выполнен через API здесь, не с прямыми вызовами диалогового окна.
другие Примечания
- фрагменты естественной эволюции в рамках Android из-за разнообразия устройств с различными размерами экрана.
- DialogFragments и фрагменты доступны в библиотеке поддержки, которая делает класс пригодным для использования во всех текущих используемых версиях Android.
Я бы рекомендовал использовать
DialogFragment.конечно, создание диалога "Да / нет" с ним довольно сложно, учитывая, что это должна быть довольно простая задача, но создание аналогичного диалогового окна с
Dialog- Это удивительно сложная.(жизненный цикл активности усложняет - вы должны позволить
Activityуправление жизненным циклом диалогового окна - и нет никакого способа, чтобы передать пользовательские параметры, например, специальное сообщениеActivity.showDialogпри использовании уровней API под 8)приятно то, что вы обычно можете построить свою собственную абстракцию поверх
DialogFragmentдовольно легко.
Generic AlertDialogFragment с шаблоном строителя
в моем проекте, я уже использовал
AlertDialog.Builderуже большое прежде чем я узнал, что это проблематично. Тем не менее, я не хотел менять столько кода в любом месте моего приложения. Кроме того, я на самом деле поклонник прохожденияOnClickListenersкак анонимные классы, где они нужны (то есть при использованииsetPositiveButton(),setNegativeButton()etc.) вместо того, чтобы реализовать тысячи методов обратного вызова для связи между диалогом фрагмент и держатель фрагмента, который может, на мой взгляд, привести к очень запутанному и сложному коду. Особенно, если у вас есть несколько разных диалогов в одном фрагменте, а затем нужно различать в реализациях обратного вызова, между которыми в настоящее время отображается диалоговое окно.поэтому я объединил различные подходы для создания общего
AlertDialogFragmentвспомогательный класс, который может быть использован именно такAlertDialog:
решение
(ОБРАТИТЕ ВНИМАНИЕ что я использую лямбда-выражения Java 8 в своем коде, поэтому вам, возможно, придется изменить части кода, если вы не используете лямбда-выражения еще.)
/** * 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 }использование
// 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();я публикую это здесь не только для того, чтобы поделиться своим решением, но и потому, что я хотел спросить вас, люди, Ваше мнение: это подход законно или проблематично в какой-то степени?
могу ли я предложить небольшое упрощение ответа @ashishduh:
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(); }это устраняет необходимость для пользователя (класса), чтобы быть знакомы с внутренними компонентами компонента и делает использование очень просто:
AlertDialogFragment.showAlert(title, message, this);P. S. В моем случае мне нужен был простой диалог предупреждение, так что я создал. Вы можете применить этот подход к Да / нет или любому другому типу, который вам нужен.