Ответы:
Да, используйте, DialogFragment
и в onCreateDialog
любом случае вы можете просто использовать AlertDialog Builder для создания простого AlertDialog
с кнопками подтверждения Да / Нет. Не очень много кода на всех.
Что касается обработки событий в вашем фрагменте, то существуют разные способы сделать это, но я просто определяю сообщение Handler
в my Fragment
, передаю его в DialogFragment
конструктор via, а затем передаю сообщения обратно в обработчик моего фрагмента в соответствии с различными событиями click. Опять разные способы сделать это, но у меня работает следующее.
В диалоговом окне удерживайте сообщение и создайте его экземпляр в конструкторе:
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();
}
}
редактировать
И, как Message
можно продать, вы можете сохранить его onSaveInstanceState
и восстановить его
outState.putParcelable("okMessage", okMessage);
Затем в onCreate
if (savedInstanceState != null) {
okMessage = savedInstanceState.getParcelable("okMessage");
}
target
которая будет нулевой, если вы загрузите ее из Bundle. Если цель сообщения равна нулю и вы используете sendToTarget
, вы получите исключение NullPointerException - не потому, что сообщение равно нулю, а потому, что его целью является.
Вы можете создавать общие подклассы 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
.
YES_NO_CALL
, getFragmentManager()
а onActivityResult
?
YES_NO_CALL
это пользовательский int, который является кодом запроса. getFragmentManager()
получает диспетчер фрагментов для действия и onActivityResult()
является методом обратного вызова жизненного цикла фрагмента.
С момента внедрения API уровня 13 :
ShowDialog метод от деятельности является устаревшим . Вызывать диалог в другом месте кода не рекомендуется, так как вам придется управлять диалогом самостоятельно (например, изменение ориентации).
Разница DialogFragment - AlertDialog
Они так сильно отличаются? Из ссылки Android относительно DialogFragment :
DialogFragment - это фрагмент, который отображает диалоговое окно, плавающее поверх окна его активности. Этот фрагмент содержит объект Dialog, который отображается в зависимости от состояния фрагмента. Контроль диалога (принятия решения , когда нужно показать, скрыть, отклонить его) должно быть сделано через API здесь , а не прямые вызовы на диалоге.
Другие заметки
Я бы порекомендовал использовать DialogFragment
.
Конечно, создать диалог «Да / Нет» с ним довольно сложно, учитывая, что это должно быть довольно простой задачей, но создание аналогичного диалогового окна с Dialog
удивительно сложным также.
(Жизненный цикл активности усложняет задачу - вы должны разрешить Activity
управление жизненным циклом диалогового окна - и нет способа передать пользовательские параметры, например пользовательское сообщение, Activity.showDialog
если используются уровни API ниже 8)
Приятно то, что вы, как правило, DialogFragment
довольно легко можете создать свою собственную абстракцию на вершине .
String
параметр. Например, когда пользователь нажимает «Да», диалоговое окно вызывает метод Activity с параметром «accept». Эти параметры указываются при отображении диалогового окна, например AskDialog.ask («Согласны ли вы с этими условиями?», «Согласен», «не согласен»);
FragmentManager
's findFragmentByTag
. Но да, для этого требуется немало кода.
Fragment
this
и получить Activity
extends
свой Interface
. Осторожно с многопоточностью, вы можете отбрасывать интерфейсные вызовы, когда они не нужны, если ваш параллелизм не проверяется. Не уверен, что это делает с памятью и спагетти с круговой зависимостью, кто-то еще хотел бы присоединиться? Другой вариант - Message
/ Handler
но у вас все еще могут быть проблемы с параллелизмом.
В своем проекте я AlertDialog.Builder
уже много использовал, прежде чем обнаружил, что это проблематично. Тем не менее, я не хотел менять столько кода в моем приложении. Кроме того, я на самом деле являюсь поклонником передачи в OnClickListeners
качестве анонимных классов там, где они необходимы (то есть при использовании setPositiveButton()
и setNegativeButton()
т. Д.), Вместо того, чтобы реализовывать тысячи методов обратного вызова для связи между фрагментом диалога и фрагментом-держателем, который может, в моё мнение, приведёт к очень запутанному и сложному коду. Особенно, если у вас есть несколько разных диалогов в одном фрагменте, а затем нужно различать в реализациях обратного вызова, какой диалог отображается в данный момент.
Поэтому я объединил разные подходы для создания универсального 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);
PS В моем случае мне понадобилось простое диалоговое окно с предупреждением, вот что я создал. Вы можете применить подход к Да / Нет или любому другому типу, который вам нужен.
Используйте диалог для простых диалогов да или нет.
Когда вам нужны более сложные представления, в которых вам нужно овладеть жизненным циклом, таким как oncreate, запрос разрешений, любое переопределение жизненного цикла, я бы использовал фрагмент диалога. Таким образом, вы разделяете разрешения и любой другой код, который должен работать в диалоговом окне, не связываясь с активностью вызова.
Dialog
илиAlertDialog.Builder::create()::show()
создаст диалоговое окно, которое исчезает при повороте экрана.