Android Dialog Creation

Being able to create a dialog is important for many applications, but it can be difficult to first learn what is the best way to create a dialog. This is in part caused by the functions showDialog and dismissDialog in Activity being deprecated as of Android API level 13 (Android 3.2). Additionally, displaying a Dialog using dialog.show() is a bad way to display a dialog, and may cause you many problems.

In this tutorial I will go through how to create a general dialog and provide some of my suggestions. Please note that I will be providing code that works for API level 14 and higher: if you want to make an app for devices lower than API 11, you will need to use the support library to allow using fragments. I will assume that you know how to do basic things in Android, such as setting up an Activity or how to create a basic layout.

The complete Android Studio project for this tutorial is available on Github.

Why not use Dialog.show()?

The main problem with using the show function of a Dialog (or any of its subclasses like AlertDialog) is that you would need to keep track of the complete life-cycle of the Dialog yourself. For example, you would need to save and restore the dialog when the screen orientation is changed.

As well, when using Dialog.show(), you may occasionally encounter an exception where the Dialog was not linked to an Activity, even if you handle the dialog’s life-cycle yourself.

Instead, you should use a DialogFragment to display a dialog to the user.

DialogFragment

A DialogFragment is basically a Fragment wrapped around a Dialog. It will correctly handle the Dialog‘s life-cycle for you and can be easily displayed on top of the current view exactly like a regular Dialog. There are two different ways you can create a DialogFragment or get it to display itself: using onCreateView or by using onCreateDialog.

However, you will likely want to pass arguments to your DialogFragment, and it is not as simple as calling a setter on your DialogFragment.

DialogFragment arguments

Like a regular Fragment, the best way to provide arguments to a DialogFragment is to pass a bundle to the setArguments function, which ensures that the DialogFragment can be properly recreated even if Android needs to redraw it. It is very simple to add arguments to a Fragment:

// When setting up the DialogFragment
Bundle args = new Bundle();
args.putString("output", output);
// Add any other arguments here
dialogFragment.setArguments(args);

then to load the argument, you can load it in the onCreate function in your DialogFragment. Please note that the argument Bundle savedInstanceState is NOT related to the arguments that you gave to the DialogFragment and is instead used to save any state you have when this DialogFragment is reset.

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    output = getArguments().getString("output", 0);
    // Load any more arguments here
}

Please note that if you want to give your DialogFragment a handler, you cannot pass it in using a Bundle. However, you will still need to use a method that allows you to access it every time the DialogFragment is recreated so you cannot just set the handler when you first create the DialogFragment. I will discuss how to do this in my next tutorial, but you can use onAttach or getActivity.

DialogFragment newInstance

The best way to instantiate a DialogFragment in your code is by creating a static newInstance function in the DialogFragment because it will ensure that the DialogFragment is given all of the arguments it needs. So using the previous example where the DialogFragment is given a String output, the newInstance function would look like:

public static MyDialogFragment newInstance(String output) {
    MyDialogFragment dialogFragment = new MyDialogFragment();

    Bundle args = new Bundle();
    args.putString("output", output);
    dialogFragment.setArguments(args);

    return dialogFragment;
}

where MyDialogFragment is the name of your DialogFragment implementation. The name of this function doesn’t matter, but you should make sure that it is standard across your program. Here is an example of a DialogFragment that has a newInstance function.

Using onCreateView

The first method that you can use to design how your DialogFragment looks is by using onCreateView. This function allow you to create the dialog as if it were a regular fragment and all you need to do is create a regular view. I prefer using this function when I want my DialogFragment to just display a view because it is easier to set up and more versatile.

Here is an example which could be combined with the earlier code for saving/loading arguments to create a useable DialogFragment, which will be dismissed when the user clicks on the button:

public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.dialog_standard_view, container, false);

    // Set up the TextView
    TextView outputTV = (TextView) view.findViewById(R.id.output);
    outputTV.setText(output);

    // Set up the button
    Button button = (Button) view.findViewById(R.id.button);
    button.setOnClickListener(new View.OnClickListener() {
        public void onClick(View v) {
            // Perform any other actions here
            dismiss(); // Without this dismiss, will not go away
        }
    });

    return view;
}

The layout standard_view_dialog is very basic: all it has is a TextView with the id “output” and a Button with the id “button”. The background of the layout a deep teal. The DialogFragment looks like:
Screenshot of a DialogFragment using the given onCreateView code

As you can see, there is a white box at the top of dialog, which is for the title. To add a title to your dialog, you could add the line

getDialog().setTitle("Title");

into onCreateView. To remove the title completely, you would need to override onCreateDialog and tell the Dialog to not display its title:

@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
    Dialog d = super.onCreateDialog(savedInstanceState);
    d.requestWindowFeature(Window.FEATURE_NO_TITLE);
    return d;
}

A complete DialogFragment using onCreateView is available on Github.

Using onCreateDialog

When using onCreateDialog, you have to return a Dialog instead of a View. This means that you can take advantage of the many Dialogs that Android provides, such as DatePickerDialog or TimePickerDialog. The most general of the available Dialogs is AlertDialog, which is easy to create using AlertDialog.Builder. You do not, however, need to use the builder and can instead set all information using an instance of AlertDialog.

The great thing of using an AlertDialog is how easy it is to use:

@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
    AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
        builder.setTitle("Title")
                .setMessage(output)
                .setPositiveButton("Positive", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        // Perform any other actions here
                        // Will be dismissed automatically
                    }
                })

                .setNeutralButton("Neutral", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        // Perform any other actions here
                        // Will be dismissed automatically
                    }
                })

                .setNegativeButton("Negative", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        /// Perform any other actions here
                        // Will be dismissed automatically
                    }
                });

        return builder.create();
}

Will, when combined with the example code I had for setting arguments, create a DialogFragment which looks like:
Screenshot of a DialogFragment using the given onCreateDialog code

One thing to note with an AlertDialog is once a user presses the Positive/Neutral/Negative button it will go away automatically.

The full code for this DialogFragment is also available on Github.

Displaying a DialogFragment as a Dialog

After you have implemented onCreateView or onCreateDialog, you can now display your DialogFragment on top of the current view using the following code, where MyDialogFragment is the name of your DialogFragment class.

MyDialogFragment dialogFragment = MyDialogFragment.newInstance("All arguments");
dialogFragment.show(getFragmentManager(), "dialog");
Embedding a DialogFragment

The biggest advantage of using onCreateView over onCreateDialog is that it allows you to embed your DialogFragment as if it was a normal Fragment by using the following lines, where R.id.frame is the id of the view you want to play it in, and fragment is your DialogFragment.

MyDialogFragment fragment = MyDialogFragment.newInstance("All arguments");
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.add(R.id.frame, fragment);
ft.commit();

If your DialogFragment uses onCreateView, it will look the same with the exception that there will be no title. However, if your DialogFragment uses onCreateDialog, it will not show up at all because onCreateDialog is only called when the DialogFragment is displayed like a dialog.

WARNING: If you use getDialog().setTitle(...) in onCreateView, there will be an exception when that DialogFragment is embedded.

Using onCreateView and onCreateDialog

If you try to create a DialogFragment using my example code for both onCreateView and onCreateDialog you will probably get the error “android.util.AndroidRuntimeException: requestFeature() must be called before adding content” when Android tries to display the DialogFragment. This error occurs when Android tries to inflate the view from onCreateView after having already creating the dialog view.

If you want to display the same view when the DialogFragment is displayed as a dialog and is embedded, you should just implement onCreateView. However, if you want the DialogFragment to display differently when it is displayed as a dialog compared to when it is embedded you should change your onCreateView to be like this:

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    if (getShowsDialog()) {
        return super.onCreateView(inflater, container, savedInstanceState);
    } else {
        // Set up the view to be displayed when the DialogFragment is embedded
    }
}

and set up the onCreateDialog to set up the DialogFragment how you want it to look like when the DialogFragment is displayed like a dialog. An example of a DialogFragment doing this is available here.

This works because getShowsDialog is true only if the DialogFragment should be displayed like a dialog, which happens when onCreateDialog will not get called.

Support Library

Android introduced Fragments (and DialogFragment) in API version 11, so if you want to create an app for API 10 or lower, you will need to use the support library. So use

import android.support.v4.app.{FragmentActivity/FragmentManager/DialogFragment}

instead of

import android.app.{Activity/FragmentManager/DialogFragment}

Furthermore, your Activity will need to extend FragmentActivity instead of extending Activity.

Once again, all of the code in this tutorial is available here, and if you have any questions please ask them in the comments!

In my next tutorial, I will cover how to send information back to your current Activity, or even to another fragment, from a DialogFragment.

Categories: Android |

Leave a Reply

Your email address will not be published. Required fields are marked *

[TOP]