272

Google recommends that we use DialogFragment instead of a simple Dialog by using Fragments API, but it is absurd to use an isolated DialogFragment for a simple Yes-No confirmation message box. What is the best practice in this case?

skayred
  • 10,603
  • 10
  • 52
  • 94
  • 9
    In short, among other things, simple `Dialog` or `AlertDialog.Builder::create()::show()` will create a dialog that disappears when you rotate the screen. – WSBT Jan 08 '18 at 07:54

10 Answers10

93

Yes, use DialogFragment and in onCreateDialog you can simply use an AlertDialog builder anyway to create a simple AlertDialog with Yes/No confirmation buttons. Not very much code at all.

With regards handling events in your fragment there would be various ways of doing it but I simply define a message Handler in my Fragment, pass it into the DialogFragment via its constructor and then pass messages back to my fragment's handler as approprirate on the various click events. Again various ways of doing that but the following works for me.

In the dialog hold a message and instantiate it in the constructor:

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

Implement the onClickListener in your dialog and then call the handler as appropriate:

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

Edit

And as Message is parcelable you can save it out in onSaveInstanceState and restore it

outState.putParcelable("okMessage", okMessage);

Then in onCreate

if (savedInstanceState != null) {
    okMessage = savedInstanceState.getParcelable("okMessage");
}
Alexander Farber
  • 21,519
  • 75
  • 241
  • 416
PJL
  • 18,735
  • 17
  • 71
  • 68
  • Writing a `Message` using `putParcelable` does not save its target. When you restore the message and try to send it later using `sendToTarget()` you will get a `NullPointerException`. – hrnt Nov 02 '11 at 12:37
  • In `onCreate` I set `okMessage` from the bundle if it is non-null. That way my `okMessage` is always non-null and it works for me. – PJL Nov 02 '11 at 12:40
  • 4
    The problem is not okMessage - the problem is okMessage's `target` which will be null if you load it from a Bundle. If the target of a Message is null, and you use `sendToTarget`, you will get a NullPointerException - not because the Message is null, but because its target is. – hrnt Nov 02 '11 at 12:44
  • OK, although I'm not seeing this problem and maybe don't quite understand what you mean by the `target` is null. My `okMessage` is non-null once loaded from the bundle and then on clicking it happily sends the message to my fragment's handler. – PJL Nov 02 '11 at 13:00
  • Yeah, sorry, it seems that Android can recover the Message target as well. In any case, there is an issue that the Handler refers to nonexistent Activity/Fragment if the Activity is restarted while the dialog is shown. Is this a problem? Well, depends on what the Handler's handleMessage does :) – hrnt Nov 02 '11 at 13:35
  • My fragment gets recreated but I've still got the same instance to the handler which gets an up-to-date reference to the fragment as necessary. As you say it depends on what the handler is doing. – PJL Nov 02 '11 at 14:08
  • Although given that the dialog can get destroyed I've opted to update the handler rather than save the messages given that the target of the messages can be invalidated. – PJL Dec 21 '11 at 17:47
  • 3
    What the advantages of using DialogFragment instead of a Dialog? – Raphael Petegrosso Dec 07 '12 at 14:43
  • 100
    The advantage of using a DialogFragment is that all the life cycle of the dialog will be handled for you. You will never get the error 'dialog has leaked...' again. Go to DialogFragment and forget Dialogs. – Snicolas Mar 13 '13 at 10:06
  • @PJL Now with lint, if you provide a constructor with arguments in a (Dialog)Fragment, you get an error. You could disable it with "ValidFragment" but I think it shows this is unwanted, as hrnt mentioned. – pjv Jun 29 '13 at 14:18
  • 7
    I think setArguments() and getArguments() should be used instead of passing the okMessage in via the constructor. – pjv Jun 29 '13 at 14:22
  • Fragments should not ever have (non-zero-arguments) constructors – Display Name Nov 04 '14 at 18:47
  • 1
    Well I user Builder pretty easily and I handle activity management with this android:configChanges="locale|keyboardHidden|orientation|screenSize" and I don't see any problems in applications... – Renetik May 07 '15 at 10:36
  • @aProgrammer because we're Android developers and therefore masochists – CCJ Nov 07 '16 at 16:13
73

You can create generic DialogFragment subclasses like YesNoDialog and OkDialog, and pass in title and message if you use dialogs a lot in your app.

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

Then call it using the following:

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

And handle the result in onActivityResult.

ashishduh
  • 6,629
  • 3
  • 30
  • 35
  • Yes, DialogFragment handles all lifecycle events for you. – ashishduh Jan 30 '14 at 14:45
  • 1
    I think it doesn't because after rotation old Dialog still exists and it keeps assignent to old not existing fragment (dialog.setTargetFragment(this, YES_NO_CALL);) so after rotation getTargetFragment().onActivityResult doesn't work – Malachiasz Jan 30 '14 at 15:21
  • I use this code for all my dialogs and they work fine when screen is rotated. `setTargetFragment` uses `FragmentManager` methods to retain state of Fragments. – ashishduh Jan 30 '14 at 19:32
  • I think that only way for it to work is to have the Fragment setRetainInstance(true) or (Activity not being destroyed). And this is unwanted in many cases. – Malachiasz Feb 03 '14 at 10:20
  • check this tutorial : http://www.android-ios-tutorials.com/897/using-dialogfragments-android/ – Houcine Sep 16 '14 at 17:04
  • 7
    what are `YES_NO_CALL`, `getFragmentManager()` and `onActivityResult` ? – msysmilu Mar 16 '15 at 15:33
  • 2
    `YES_NO_CALL` is a custom int that is the request code. `getFragmentManager()` gets the fragment manager for the activity, and `onActivityResult()` is a fragment lifecycle callback method. – ashishduh Mar 16 '15 at 20:15
  • 3
    Replace getFragmentManager() with getSupportFragmentManager(); – Avinash Verma Jun 20 '17 at 14:18
  • @Malachiasz According to the doc, the arguments supplied with `setArguments()` will be retained across fragment destroy and creation. – WSBT Jan 08 '18 at 07:51
  • @ashishduh i like the concept of using target fragment and activity result. Also it not better to create listener and implement them in the fragment ? Any pros and cons of each approach ?? – Debjit Mar 03 '18 at 23:55
  • I wonder why this isn't in support library yet instead of `AlertDialog`. – Mygod Sep 17 '18 at 15:23
  • Is there a reason to wrap the result of AlertDialog.Builder in a DialogFragment? Why dialog isn't built in the where we want to use it and call the Method .show() of the builder? – finder2 Sep 19 '19 at 09:32
34

Use DialogFragment over AlertDialog:


  • Since the introduction of API level 13:

    the showDialog method from Activity is deprecated. Invoking a dialog elsewhere in code is not advisable since you will have to manage the the dialog yourself (e.g. orientation change).

  • Difference DialogFragment - AlertDialog

    Are they so much different? From Android reference regarding DialogFragment:

    A DialogFragment is a fragment that displays a dialog window, floating on top of its activity's window. This fragment contains a Dialog object, which it displays as appropriate based on the fragment's state. Control of the dialog (deciding when to show, hide, dismiss it) should be done through the API here, not with direct calls on the dialog.

  • Other notes

    • Fragments are a natural evolution in the Android framework due to the diversity of devices with different screen sizes.
    • DialogFragments and Fragments are made available in the support library which makes the class usable in all current used versions of Android.
Tobrun
  • 18,291
  • 10
  • 66
  • 81
29

I would recommend using DialogFragment.

Sure, creating a "Yes/No" dialog with it is pretty complex considering that it should be rather simple task, but creating a similar dialog box with Dialog is surprisingly complicated as well.

(Activity lifecycle makes it complicated - you must let Activity manage the lifecycle of the dialog box - and there is no way to pass custom parameters e.g. the custom message to Activity.showDialog if using API levels under 8)

The nice thing is that you can usually build your own abstraction on top of DialogFragment pretty easily.

hrnt
  • 9,882
  • 2
  • 31
  • 38
  • How you will handle alert dialog callbacks (yes, no)? – Alexey Zakharov Nov 02 '11 at 08:49
  • The easiest way would be to implement a method in the hosting Activity that takes a `String` parameter. When the user clicks "Yes", for example, the dialog calls the Activity's method with parameter "agree". These parameters are specified when showing the dialog, for example AskDialog.ask("Do you agree to these terms?", "agree", "disagree"); – hrnt Nov 02 '11 at 08:54
  • 5
    But i need callback inside fragment, not activity. I can use setTargetFragment and cast it to interface. But it is hell. – Alexey Zakharov Nov 02 '11 at 08:59
  • You could also fetch the target fragment by setting a tag to the target and using `FragmentManager`'s `findFragmentByTag`. But yeah, it requires a fair bit of code. – hrnt Nov 02 '11 at 09:53
  • @AlexeyZakharov I know this is about 5 years late but you could pass the `Fragment` `this` and have your `Activity` `extends` your `Interface`. Careful of threading though, you could be popping off interface calls when you don't necessarily want them if your concurrency is not in check. Not sure what this does with memory and circular dependency spaghetti though, would anyone else like to chime in? The other option is `Message`/ `Handler` but you still may have concurrency issues. – tricknology Mar 24 '16 at 03:55
8

Generic AlertDialogFragment with Builder Pattern

In my project, I already used AlertDialog.Builder already a lot before I found out that it's problematic. However, I did not want to change that much code anywhere in my app. Additionally, I actually am a fan of passing OnClickListeners as anonymous classes where they are needed (that is, when using setPositiveButton(), setNegativeButton() etc.) instead of having to implement thousands of callback methods to communicate between a dialog fragment and the holder fragment, which can, in my opinion, lead to very confusing and complex code. Especially, if you have multiple different dialogs in one fragment and then need to distinguish in the callback implementations between which dialog currently being shown.

Therefore, I combined different approaches to create a generic AlertDialogFragment helper class which can be used exactly like AlertDialog:


SOLUTION

(PLEASE NOTE that I am using Java 8 lambda expressions in my code, so you might have to change parts of the code if you are not using lambda expressions yet.)

/**
 * 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
}

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

I am posting this here not only to share my solution, but also because I wanted to ask you people for your opinion: Is this approach legit or problematic to some extent?

flxapps
  • 1,066
  • 1
  • 11
  • 24
  • 5
    This is a very interesting idea, but I don't think the API design works. If you pass an OnClickListener to setPositiveButton(), when the device is rotated and the fragment is recreated from the Bundle args, the OnClickListeners won't be properly recreated from the Parcelable. The fundamental issue is that you can't recreate a listener during rotation, but the API interface (which takes interfaces) demands it. I wish this wasn't the case (as I like the idea). – Xargs May 24 '16 at 00:05
  • 3
    Nice idea, but as @Xargs says, it does not work. The passed-in listeners are not recreated correctly on rotation. – Graham Borland Jun 21 '16 at 12:55
  • My results are that it actually works on rotation and on resuming to the app (after going to the home screen for example), but not when the activity is restored after it has been completely destroyed (then the OnClickListeners are indeed lost). (Tested on Android 4.4.4 and Android 5.1.1) – flxapps Feb 03 '17 at 10:34
  • I haven't tested this exact implementation but from what I've tested, a parcelable listener passed to a fragment bundle is called correctly on recreate. I have no idea why but it seems to work. – Saad Farooq Nov 20 '17 at 22:02
  • @flxapps, in case of custom view how can you get the child views and change their properties or apply listeners? In your class you are not returning any dialog instance and that could cause an exception if someone will try to get child views – Zubair Rehman Aug 31 '18 at 09:45
8

May I suggest a little simplification of @ashishduh's answer:

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

It removes the need for the user (of the class) to be familiar with the internals of the component and makes usage really simple:

AlertDialogFragment.showAlert(title, message, this);

P.S. In my case I needed a simple alert dialog so that's what I created. You can apply the approach to a Yes/No or any other type you need.

AXE
  • 8,335
  • 6
  • 25
  • 32
8

DialogFragment is basically a Fragment that can be used as a dialog.

Using DialogFragment over Dialog due to the following reasons:

  • DialogFragment is automatically re-created after configuration changes and save & restore flow
  • DialogFragment inherits full Fragment’s lifecycle
  • No more IllegalStateExceptions and leaked window crashes. This was pretty common when the activity was destroyed with the Alert Dialog still there.

More detail

akhilesh0707
  • 6,709
  • 5
  • 44
  • 51
3

Use Dialog for simple yes or no dialogs.

When you need more complex views in which you need get hold of the lifecycle such as oncreate, request permissions, any life cycle override I would use a dialog fragment. Thus you separate the permissions and any other code the dialog needs to operate without having to communicate with the calling activity.

Gustavo Baiocchi Costa
  • 1,379
  • 3
  • 16
  • 34
3

Dialog: A dialog is a small window that prompts the user to make a decision or enter additional information.

DialogFragment: A DialogFragment is a special fragment subclass that is designed for creating and hosting dialogs. It allows the FragmentManager to manage the state of the dialog and automatically restore the dialog when a configuration change occurs.

Gk Mohammad Emon
  • 6,084
  • 3
  • 42
  • 42
2

DialogFragment comes with the power of a dialog and a Fragment. Basically all the lifecycle events are managed very well with DialogFragment automatically, like change in screen configuration etc.

Shubham Goel
  • 1,962
  • 17
  • 25