Passing Data From DialogFragment To Calling Activity

Coming form the C# world I thought this would be a cinch just use a delegate object and a callback done and done. Well as it turns out I did not know that Java does not have the concept of delegates and as a result neither does Android.

So now what? Well after I RTFM, I found that there are two buttons on a dialog that can accept a DialogInterface.OnClickListener() the two methods are setNegativeButton() and setPositiveButton() which are methods defined on the AlertDialog.Builder class.

Cool, so now I have a way to detect when a button on the dialog is invoked all I need is a way to send data back to the calling activity when one of those button is pressed. Well, I decided to define an interface to help facilitate this behavior, in this case the DialogInputListener interface and have the activity that is spawning the dialog implement that interface. I know it does not make sense right now but read on and the dots will be connected. Below is the interface.

public interface DialogInputListener {
     void onPositiveClick(Object data);
     void onNegativeClick(Object data);
}

Below is the Activity that creates the dialog

public class AddWeightsActivity extends AppCompatActivity implements DialogInputListener {

    @Override
    public void onPositiveClick(Object data) {

        if(data != null)
        {
            if(data.toString().equals("Delete")){
                String currentSetName = currentWeightSet.GetSetName();
                if(RunDeleteWeightSetSteps(currentSetName)) {
                    Toast.makeText(this, "Deleted " + currentSetName, Toast.LENGTH_SHORT).show();
                }
            }else {
                RunNewWeightSetSteps(data.toString());
            }
        }

    }

    @Override
    public void onNegativeClick(Object data) {
      //do nothing
    }

    private void ShowNewWeightSetDialog()
    {
        NewWeightSetDialog diag = new NewWeightSetDialog();
        diag.show(getSupportFragmentManager(),"new weight set dialog");
    }

The method ShowNewWeightSetDialog() creates the new Dialog. Seems pretty straight forward right, but you might of noticed that I’m never passing the new Dialog a reference to the Activity. So what is going on here how do the two pass data between one another. When you invoke the show() method and get the fragment manager an overridable method called onAttach (defined on DialogFragment superclass) is invoked and the Context that spawned the DialogFragment is passed into it.

I chose to use an Object to pass in the data, I did this because I didn’t know at the time of writing this what I would ultimately be passing in. This allows me to cast the object to whatever I need for testing since I’m in control of what is being sent.

Here is my class that extends DialogFragment. I included the whole class to give a more comprehensive view.

public class NewWeightSetDialog  extends DialogFragment
{
    DialogInputListener listener;
    @Override
    public void onAttach(Context context) {
        if(context instanceof DialogInputListener)
        {
            listener = (DialogInputListener)context;
        }
        super.onAttach(context);
    }

    @NonNull
    @Override
    public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
        builder.setTitle("Enter new Weight Set Name");

        final EditText dialogInput;
        dialogInput = new EditText(getContext());
        builder.setView(dialogInput);
        builder.setPositiveButton("Done", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int i) {
                String txtInput = dialogInput.getText().toString();
                if(txtInput != null)
                {
                    if(listener != null)
                    {
                        listener.onPositiveClick(txtInput);
                    }
                }

            }
        });

        builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int i) {
                dialogInterface.cancel();
            }
        });

        return builder.create();
    }
}

Since Context is one of the superclasses in the inheritance hierarchy for an AppCompactActivity all we need to do is check if the Context is an instance of DialogInputListener, if it is, cast the context back to it and assign it to a instance variable.

So now everything should be wired up automatically when you instantiate an instance of the DialogFragment. All that is left to do is figure out when you need to send data back to the Activity. In my case you can see the onPositiveClick() method being called when the PositiveButton is pressed.

There are other ways to do the same thing, you could define a constructor that accepts different handlers. I have not looked into it but you might be able to use some type of event/broadcast setup. I chose this because I think its quick and easy to get things running and it was pretty reusable for my particular project. Any Activity that creates a Dialog can just implement that interface and now I have at least a one way communication between the two objects.

Cheers!