How can I reuse an AlertDialog for Yes/No on Android?
Asked Answered
P

2

6

I'm trying to find the way to reuse a Dialog that shows customized titles, then send the Yes/No click to the function that has launched the Dialog.

I have two buttoms, Save and Dismiss, and both call a Yes/No Dialog, one showing "Do you want to save" and the other "Dismiss changes?".

I think my procedure is very "dirty" but I guess it can work, but my problem is the "View view" variable, I don't know how to pass it from the Activity to the Dialog, so I can use it to recall the function that launched the Dialog.

Thanks in advance, HerniHdez

.java of my activity (fragment of it)

public void open_HH_Fragment_YesNo(View view, String aux_title, String aux_function)
{
    Bundle bundle=new Bundle();
    bundle.putString("setMessage", aux_title);
    bundle.putString("callingFunction", aux_function);

    DialogFragment newFragment = new HH_Fragment_YesNo();
    newFragment.setArguments(bundle);
    newFragment.show(getSupportFragmentManager(), "HH_Fragment_YesNo");
}

public void SaveChanges(View view, String aux_YesNo)
{
    if (aux_YesNo == "")
    {
        Toast.makeText(this, "Save changes?", Toast.LENGTH_SHORT).show();
        open_HH_Fragment_YesNo(view, "Save changes?", "SaveChanges");
    }
    else if (aux_YesNo == "Yes")
    {
        Toast.makeText(this, "Saving changes", Toast.LENGTH_SHORT).show();
    }
    else if (aux_YesNo == "No")
    {
        Toast.makeText(this, "Save Cancelled", Toast.LENGTH_SHORT).show();
    }
}

public void DismissChanges(View view, String aux_YesNo)
{
    if (aux_YesNo == "")
    {
        Toast.makeText(this, "Dismiss changes?", Toast.LENGTH_SHORT).show();
        open_HH_Fragment_YesNo(view, "Dismiss changes?", "DismissChanges");
    }
    else if (aux_YesNo == "Yes")
    {
        Toast.makeText(this, "Dismiss OK", Toast.LENGTH_SHORT).show();
        Close(view);
    }
    else if (aux_YesNo == "No")
    {
        Toast.makeText(this, "Dismiss Cancelled", Toast.LENGTH_SHORT).show();
    }
}

// The dialog fragment receives a reference to this Activity through the
// Fragment.onAttach() callback, which it uses to call the following methods
// defined by the HH_Fragment_YesNo.YesNoDialogListener interface
@Override
public void onDialogPositiveClick(DialogFragment dialog, View view, String aux_function)
{
    // User touched the dialog's positive button
    Toast.makeText(this, "User clicked on Yes", Toast.LENGTH_SHORT).show();

    if (aux_function == "SaveChanges")
    {
        SaveChanges(view, "Yes");
    }
    else if (aux_function == "DismissChanges")
    {
        DismissChanges(view, "Yes");
    }
}

@Override
public void onDialogNegativeClick(DialogFragment dialog, View view, String aux_function)
{
    Toast.makeText(this, "User clicked on NO", Toast.LENGTH_SHORT).show();

    if (aux_function == "SaveChanges")
    {
        SaveChanges(view, "No");
    }
    else if (aux_function == "DismissChanges")
    {
        DismissChanges(view, "No");
    }
}

.java of my Dialog (complete)

public class HH_Fragment_YesNo extends DialogFragment
{
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState)
    {
    // Use the Builder class for convenient dialog construction
    AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
    String setMessage = getArguments().getString("setMessage");
    final String callingFunction = getArguments().getString("callingFuntion");

    builder
        .setMessage(setMessage)                                             // R.string.dialog_fire_missiles
        .setPositiveButton("Sí", new DialogInterface.OnClickListener()      // R.string.fire
        {
            public void onClick(DialogInterface dialog, int id)
            {
                // Exit without saving
                mListener.onDialogPositiveClick(HH_Fragment_YesNo.this, view, callingFunction);
            }
        })
        .setNegativeButton("No", new DialogInterface.OnClickListener()      // R.string.cancel
        {
            public void onClick(DialogInterface dialog, int id)
            {
                // User cancelled the dialog
                mListener.onDialogNegativeClick(HH_Fragment_YesNo.this, view, callingFunction);
            }
        });

    // Create the AlertDialog object and return it
    return builder.create();
}


/* The activity that creates an instance of this dialog fragment must
 * implement this interface in order to receive event callbacks.
 * Each method passes the DialogFragment in case the host needs to query it. */
public interface YesNoDialogListener
{
    public void onDialogPositiveClick(DialogFragment dialog, View view, String aux_Function);
    public void onDialogNegativeClick(DialogFragment dialog, View view, String aux_Function);
}


// Use this instance of the interface to deliver action events
YesNoDialogListener mListener;


// Override the Fragment.onAttach() method to instantiate the NoticeDialogListener
@Override
public void onAttach(Activity activity)
{
    super.onAttach(activity);
    // Verify that the host activity implements the callback interface
    try
    {
        // Instantiate the NoticeDialogListener so we can send events to the host
        mListener = (YesNoDialogListener) activity;
    }
    catch (ClassCastException e)
    {
        // The activity doesn't implement the interface, throw exception
        throw new ClassCastException(activity.toString() + " must implement NoticeDialogListener");
    }
}
}
Patsy answered 24/8, 2013 at 1:6 Comment(0)
W
5

Complete solution Try this

1) Createa Interface

import android.content.DialogInterface;

public interface AlertMagnatic {

    public abstract void PositiveMethod(DialogInterface dialog, int id);
    public abstract void NegativeMethod(DialogInterface dialog, int id);
}

2) Generalize method for confirm dialog.

public static void getConfirmDialog(Context mContext,String title, String msg, String positiveBtnCaption, String negativeBtnCaption, boolean isCancelable, final AlertMagnatic target) {
        AlertDialog.Builder builder = new AlertDialog.Builder(mContext);

        int imageResource = android.R.drawable.ic_dialog_alert;
        Drawable image = mContext.getResources().getDrawable(imageResource);

        builder.setTitle(title).setMessage(msg).setIcon(image).setCancelable(false).setPositiveButton(positiveBtnCaption, new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int id) {
                target.PositiveMethod(dialog, id);
            }
        }).setNegativeButton(negativeBtnCaption, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int id) {
                target.NegativeMethod(dialog, id);
            }
        });

        AlertDialog alert = builder.create();
        alert.setCancelable(isCancelable);
        alert.show();
        if (isCancelable) {
            alert.setOnCancelListener(new OnCancelListener() {

                @Override
                public void onCancel(DialogInterface arg0) {
                    target.NegativeMethod(null, 0);
                }
            });
        }
    }

3) How to use

getConfirmDialog(getString(R.string.logout), getString(R.string.logout_message), getString(R.string.yes), getString(R.string.no), false,
                new AlertMagnatic() {

                    @Override
                    public void PositiveMethod(final DialogInterface dialog, final int id) {}

                    @Override
                    public void NegativeMethod(DialogInterface dialog, int id) {

                    }
                });
Woodbine answered 24/8, 2013 at 5:29 Comment(10)
Thanks Biraj, I'm not sure about where I have to put every part of the code. * Does the Interface go in a different file or in the activity's java file?Interglacial
I recommended to put it as separate file in your package.Woodbine
I guess "Generalize method for confirm dialog" goes in the java file of the Dialog and "how to use" in the activity file, right?Interglacial
What is the pourpose of the variable "id"? How can I call the Dialog from my xml to pass all the parameters? (android:onClick="SaveChanges")Interglacial
Do not use android:onClick="SaveChanges" property because it will support android version <= 1.6 will not work in ICSWoodbine
"Context mContext" is missing in the "How to use" when calling the function getConfirmDialog. I'm not sure when can I get that value.Interglacial
I found a way to resolver the mContext variable issue. I use myactivityname.this (if I use only "this" it throws “android.view.WindowManager$BadTokenException: Unable to add window — token null is not for an application”) getConfirmDialog(myactivityname.this, getString(R.string.logout), getString(R.string.logout_message), getString(R.string.yes), getString(R.string.no), false, new AlertMagnatic() {Interglacial
I still don't know how to get what function is calling the AlertDialog to proceed. I mean, If I call the Yes/No dialog using my function SaveChanges, "Yes" will call the code to save and "No" will cancel the dialog, but if I call the Yes/No dialog using my function DismissChanges, "Yes" will call the code to close without saving and "No" will cancel the dialog. I thought it was the "int id", but for the same calling function "Yes" returns id=-1 and "No" returns id=-2.Interglacial
It is very simple whenever "yes" button clicked you will get callback in PositiveMethod() and "no" button callback to NegativeMethod().You don't worry about id.Woodbine
Probably I didn't explain my question correctly. In the function getConfirmDialog the methods Positive and Negative are always the same. If I call it from my function SaveChanges, Positive will do the same action than if I call it from my function DismissChanges. My question is how to control what function is calling the getConfirmDialog function. That's why I thought ID could be the answer.Interglacial
H
5

Since this page is the first hit on Google, and it seems like such a common task with so little written about it, I'll share the way I've found to show a reusable DialogFragment.

Using interfaces gets really messy if you want to reuse the same Dialog multiple times from the same class, but perform a different action everytime. This solution is a simple and straight-forward way to get around that issue without introducing any disadvantages.

Edit 2017-02-25: This answer was previously using an Abstract class to implement confirm() and cancel(), however newer versions of Android will crash with the following error if you try to use an anonymous class as a DialogFragment:

java.lang.IllegalStateException: Fragment null must be a public static class to be properly recreated from instance state.

so I've modified the solution to use Runnables, which works really nice in Java8 but is also feasible without it

First, create a class which implements the creation of the Dialog itself:

/**
 * This is a reusable convenience class which makes it easy to show a confirmation dialog as a DialogFragment.
 * Create a new instance, call setArgs(...), setConfirm(), and setCancel() then show it via the fragment manager as usual.
 */
public class ConfirmationDialog extends DialogFragment {
    // Do nothing by default
    private Runnable mConfirm = new Runnable() {
        @Override
        public void run() {
        }
    };
    // Do nothing by default
    private Runnable mCancel = new Runnable() {
        @Override
        public void run() {
        }
    };

    public void setArgs(String message) {
        setArgs("" , message);
    }

    public void setArgs(String title, String message) {
        Bundle args = new Bundle();
        args.putString("message", message);
        args.putString("title", title);
        setArguments(args);
    }

    public void setConfirm(Runnable confirm) {
        mConfirm = confirm;
    }

    public void setCancel(Runnable cancel) {
        mCancel = cancel;
    }

    @Override
    public MaterialDialog onCreateDialog(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Resources res = getActivity().getResources();
        String title = getArguments().getString("title");
        return new MaterialDialog.Builder(getActivity())
                .title(title.equals("") ? res.getString(R.string.app_name) : title)
                .content(getArguments().getString("message"))
                .positiveText(res.getString(R.string.dialog_ok))
                .negativeText(res.getString(R.string.dialog_cancel))
                .callback(new MaterialDialog.ButtonCallback() {
                    @Override
                    public void onPositive(MaterialDialog dialog) {
                        mConfirm.run();
                    }

                    @Override
                    public void onNegative(MaterialDialog dialog) {
                        mCancel.run();
                    }
                })
                .show();
    }
}

Secondly, you should make a common method in your Activity which shows a DialogFragment using the fragment manager:

/**
 * Global method to show dialog fragment
 * @param newFragment  the DialogFragment you want to show
 */
public void showDialogFragment(DialogFragment newFragment) {
    // DialogFragment.show() will take care of adding the fragment
    // in a transaction. We also want to remove any currently showing
    // dialog, so make our own transaction and take care of that here.
    FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
    Fragment prev = getSupportFragmentManager().findFragmentByTag("dialog");
    if (prev != null) {
        ft.remove(prev);
    }
    // save transaction to the back stack
    ft.addToBackStack("dialog");
    newFragment.show(ft, "dialog");
}

Then you can show a confirmation dialog from anywhere in your Activity like this:

ConfirmationDialog dialog = new ConfirmationDialog ();
dialog.setArgs(stringDialogTitle, stringDialogMessage);
Runnable confirm = new Runnable() {
    @Override
    public void run() {
        doStuff();
    }
};
dialog.setConfirm(confirm);
showDialogFragment(dialog);

If you have Java8 you can use lambdas for the functions which will make the code much less verbose. See here for an example.

Henbit answered 8/11, 2014 at 3:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.