In Android, as with any other platform, Dialogs are fairly common. Dialog Boxes can be of different types doing different things and might be needed all the time in your project. So there’s a big incentive to making them reusable.
Building an Android Dialog Box is simple. You create a class that extends DialogFragment
, you define it’s views and titles and so on and you have the Activity or Fragment that you need call it whenever it needs to.
Let’s take a situation where you need to add or edit the name of an existing field and we use a dialog because that’s the only field you’re using or maybe you want someone to enter some information, etc.
Generally speaking, the code that you’d write to create a simple Dialog with an EditText
(which is Android’s text input control) and that has an OK/Cancel button would look like this:
// MyEditDialogFragment.java
mEditText = new EditText(getContext());
listener = (MainActivity) getContext();
return new AlertDialog.Builder(getContext())
.setView(mEditText)
.setPositiveButton(getString(android.R.string.ok), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// Do something when user clicks OK.
}
})
.setNegativeButton(getString(android.R.string.cancel), null)
Let’s assume we have some text we want to edit and we want to do that with a DialogFragment
. So we have some text, and we have a button. When we click the button, we get a Dialog box to edit the text. It would look something like this:

So in the scenario described above we have a MainActivity
that calls a DialogFragment
where we enter new text and change the text in the Activity
. The question then is, “How do we return the data from the Activity
?”
It’s important to mention here that it is recommended that you use Fragment
s. Fragment
s make passing data between the calling Fragment
and the DialogFragment
relatively easy. But sometimes, one makes the mistake of not using Fragment
s and now you have to either rewrite your entire Activity
to use Fragment
s or figure out how to return data from the DialogFragment
to an Activity
, as I had to recently.
The recommended way to do that is described here. Simply put, we define a listener object in the DialogFragment
to call some function in the MainActvitiy
. The MainActvivity
will implement our listener interface, in this case OnDialogCompletedListener
and will provide an implementation for the onCompleted(String)
method that will be called by the DialogFragment
.
We change the code above to what is given below:
// MyEditDialogFragment.java
return new AlertDialog.Builder(getContext())
.setTitle("New Text")
.setView(mEditText)
.setPositiveButton(getString(android.R.string.ok), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
String text = mEditText.getText().toString();
listener.onCompleted(text);
}
})
.setNegativeButton(getString(android.R.string.cancel), null)
In the code above, we get the text from the EditText
and pass it to the onComplete(String)
function, which is defined in the MainActivity
. That function simply changes the text. Since MainActivity
will implement the OnDialogCompletedListener
interface, it will have to override that function.
// MainActivity.java
@Override
public void onCompleted(String text) {
mTextView.setText(text);
}
What’s important to note here is that our DialogFragment
calls the onCompleted(String)
on the MainActivity
so it’s inherently tied to the MainActivity, especially the one onCompleted
function.
If you wanted to add the data entered in the Dialog to a database, this wouldn’t be substantially different to calling a method from the Activity to do that. The problem with that approach is that you have the same method called, each time the Dialog is created. If you wanted to do something else, you’d have a problem.
Meaning that if I wanted to do something else, say “add a new text box”, we couldn’t use the same DialogFragment
So clearly the solution is to set our own listener and pass it to the fragment. The way we can do that is simply to inject our own listener. Say we define a setOnDialogCompleteListener()
for our DialogFragment
which is simply this:
// MainActivity.java
public void setOnDialogCompletedListener (OnDialogCompletedListener listener) {
this.listener = listener;
}
Now when creating the Dialog in the MainActivity
, we simply set our own listener as we would do for a Button
or any other View.
// MainActivity.java
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
FragmentManager manager = getSupportFragmentManager();
MyEditDialogFragment fragment = new MyEditDialogFragment();
fragment.setOnDialogCompletedListener(new OnDialogCompletedListener() {
@Override
public void onCompleted(String text) {
mTextView.setText(text)
}
});
fragment.show(manager, TAG);
}
});
Now the DialogFragment
is not tied to the MainActivity
. Now it can be reused anywhere we like.
// SomeActivity.java
fragment.setOnDialogCompletedListener(new OnDialogCompletedListener() {
@Override
public void onCompleted(String text) {
FirebaseDatabase.getInstance().getReference()
.child("posts")
.child(getKey())
.child("name")
.setValue(text);
}
});
In the code above, now we’re using the same MyEditDialogFragment
to update data in a database, in different Activity
altogether without changing anything about the Dialog.
Optionally, we could define it in a newInstance
method.
// MyEditDialogFragment.java
public MyEditDialogFragment(OnDialogCompletedListener listener) {
this.listener = listener;
}
// Note: a default constructor is also defined
public static MyEditDialogFragment newInstance(String text, OnDialogCompletedListener listener) {
Bundle args = new Bundle();
MyEditDialogFragment fragment = new MyEditDialogFragment(listener);
fragment.setArguments(args);
return fragment;
}
This generates an Android Lint warning saying that non-default constructors should not be used since when re-instantiating the fragment, they will not be called. However since we’re explicitly calling them in the newInstance()
method, we should be fine.
Our newInstance()
method takes two parameters: A title for the DialogFragment
and the listener. Now when we instantiate the DialogFragment
we will also have to provide an OnDialogCompletedListener
for it.
// MainActivity.java
MyEditDialogFragment fragment = MyEditDialogFragment.newInstance("Edit Subtitle",
new OnDialogCompletedListener() {
@Override
public void onCompleted(String text) {
mSubtitleTextView.setText(text);
}
});
}