Introduction to Android Application Development, Fourth Edition (2014)

Part III. Android User Interface Design Essentials

Chapter 10. Displaying Dialogs

Android application user interfaces need to be elegant and easy to use. One important technique developers can use is to implement dialogs to inform the user or allow the user to perform actions such as edits without redrawing the main screen. In this chapter, we discuss how to incorporate dialogs into your applications.

Choosing Your Dialog Implementation

The Android platform is growing and changing quickly. New revisions of the Android SDK are released on a frequent basis. This means that developers are always struggling to keep up with the latest that Android has to offer. The Android platform has been in a period of transition from a traditional smartphone platform to a “smart device” platform that will support a much wider variety of devices, such as tablets, TVs, and toasters. To this end, one of the most important additions to the platform is the concept of the Fragment. We discussed fragments in detail in the previous chapter, but they have wide ramifications in terms of Android application user interface design. One area of application design that has received an overhaul during this transition is the way in which dialogs are implemented.

There are two methods for incorporating dialogs into your application—the legacy method and the method recommended for developers moving forward:

Image Using the legacy method, which has existed since the first Android SDK was released, an Activity class manages its dialogs in a dialog pool. Dialogs are created, initialized, updated, and destroyed using Activity class callback methods. Dialogs are not shared among activities. This type of dialog implementation works for all versions of the Android platform; however, many of the methods used in this type of solution have been deprecated as of API Level 13 (Android 3.2). The Android API guides no longer discuss using this legacy method for adding dialogs to an application. Continuing to use the deprecated Activity dialog methods is not recommended and will not be discussed in this book.

Image Using the Fragment-based method, which was introduced in API Level 11 (Android 3.0), dialogs are managed using the FragmentManager class (android.app.FragmentManager). A Dialog becomes a special type of Fragment that must still be used within the scope of an Activity class, but its lifecycle is managed like that of any other Fragment. This type of Dialog implementation works with the newest versions of the Android platform and is backward compatible with older devices as long as you incorporate the latest Android Support Package into your application to gain access to these new classes for use with older Android SDKs. Fragment-based dialogs are the recommended choice for moving forward with the Android platform.


Image Note

Unlike with some other platforms, which routinely remove deprecated methods after a few releases, deprecated methods within the Android SDK can normally be used safely for the foreseeable future, as necessary. That said, developers should understand the ramifications of using deprecated methods and techniques, which may include difficulty in upgrading application functionality to use the latest SDK features later on, slower performance as newer features are streamlined and legacy ones are left as is, and the possibility of the application “showing its age.” Deprecated methods are also unlikely to receive any sort of fixes or updates.


We will cover the Fragment-based methods in this chapter. If you are developing new applications or updating existing applications to use the latest technologies the Android SDK has to offer, we highly recommend implementing the Fragment-based method and using the Android Support Package to support older versions of the Android platform.

Exploring the Different Types of Dialogs

Regardless of which way you implement them, a number of different Dialog types are available within the Android SDK. Each type has a special function that most users should be somewhat familiar with. The Dialog types available as part of the Android SDK include the following:

Image Dialog: the basic class for all Dialog types. A basic Dialog (android.app.Dialog) is shown in the top left of Figure 10.1.

Image

Figure 10.1 A sample of different Dialog types available in Android.

Image AlertDialog: a Dialog with one, two, or three Button controls. An AlertDialog (android.app.AlertDialog) is shown in the top center of Figure 10.1.

Image CharacterPickerDialog: a Dialog for choosing an accented character associated with a base character. A CharacterPickerDialog (android.text.method.CharacterPickerDialog) is shown in the top right of Figure 10.1.

Image DatePickerDialog: a Dialog with a DatePicker control. A DatePickerDialog (android.app.DatePickerDialog) is shown in the bottom left of Figure 10.1.

Image ProgressDialog: a Dialog with a determinate or indeterminate ProgressBar control. An indeterminate ProgressDialog (android.app.ProgressDialog) is shown in the bottom center of Figure 10.1.

Image TimePickerDialog: a Dialog with a TimePicker control. A TimePickerDialog (android.app.TimePickerDialog) is shown in the bottom right of Figure 10.1.

Image Presentation: Added in API Level 17, a Presentation (android.app.Presentation) dialog is a type of Dialog used for presenting content to a secondary display.

If none of the existing Dialog types is adequate, you can create custom Dialog windows, with your specific layout requirements.

Working with Dialogs and Dialog Fragments

An Activity can use dialogs to organize information and react to user-driven events. For example, an Activity might display a dialog informing the user of a problem or asking the user to confirm an action such as deleting a data record. Using dialogs for simple tasks helps keep the number of application activities manageable.

Moving forward, most Activity classes should be “Fragment aware.” In most cases dialogs should be coupled with user-driven events within specific fragments. There is a special subclass of Fragment called a DialogFragment (android.app.DialogFragment) that can be used for this purpose.

A DialogFragment is the best way to define and manage dialogs for use within your user interface.


Image Tip

Many of the code examples provided in this section are taken from the SimpleFragDialogs application. The source code for the SimpleFragDialogs application is provided for download on the book’s website.


Tracing the Lifecycle of a Dialog and DialogFragment

Each Dialog must be defined within the DialogFragment in which it is used. A Dialog may be launched once or used repeatedly. Understanding how a DialogFragment manages the Dialog lifecycle is important to implementing a Dialog correctly.

The Android SDK manages a DialogFragment in the same way that fragments are managed. We can be sure that a DialogFragment follows nearly the same lifecycle as a Fragment. Let’s look at the key methods that a DialogFragment must use to manage a Dialog:

Image The show() method is used to display the Dialog.

Image The dismiss() method is used to stop showing the Dialog.

Adding a DialogFragment with a Dialog to an Activity involves several steps:

1. Define a class that extends DialogFragment. You can define this class within your Activity, but if you plan to reuse this DialogFragment in other activities, define this class in a separate file. This class must define a new DialogFragment class method that instantiates and returns a new instance of itself.

2. Define a Dialog within the DialogFragment class. Override the onCreateDialog() method and define your Dialog here. Simply return the Dialog from this method. You are able to define various Dialog attributes for your Dialog using methods such as setTitle(), setMessage(), orsetIcon().

3. In your Activity class, instantiate a new DialogFragment instance, and once you have the DialogFragment instance, show the Dialog using the show() method.

Defining a DialogFragment

A DialogFragment class can be defined within an Activity or within a Fragment. The type of Dialog you are creating will determine the type of data that you must supply to the Dialog definition inside the DialogFragment class.

Setting Dialog Attributes

A Dialog is not too useful without setting the contextual elements. One way of doing this is by defining one or more of the attributes made available by the Dialog class. The base Dialog class and all of the Dialog subclasses define a setTitle() method. Setting the title usually helps a user determine what the Dialog is used for. The particular type of Dialog you are implementing determines the other methods that are made available to you for setting different Dialog attributes. In addition, setting attributes is also important for accessibility purposes, for example, to translate text to speech.

Showing a Dialog

You can display any Dialog within an Activity by calling the show() method of the DialogFragment class on a valid DialogFragment object identifier.

Dismissing a Dialog

Most types of dialogs have automatic dismissal circumstances. However, if you want to force a Dialog to be dismissed, simply call the dismiss() method on the Dialog identifier.

Here’s an example of a simple class called SimpleFragDialogActivity that illustrates how to implement a simple DialogFragment with a Dialog control that is launched when a Button called Button_AlertDialog (defined in a layout resource) is clicked:

public class SimpleFragDialogActivity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        // Handle Alert Dialog Button
        Button launchAlertDialog = (Button) findViewById(
           R.id.Button_AlertDialog);
        launchAlertDialog.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                DialogFragment newFragment = 
                    AlertDialogFragment.newInstance();
                showDialogFragment(newFragment);
            }
        });
     }

    public static class AlertDialogFragment extends DialogFragment {
        public static AlertDialogFragment newInstance() {
            AlertDialogFragment newInstance = new AlertDialogFragment();
            return newInstance;
        }
        @Override
        public Dialog onCreateDialog(Bundle savedInstanceState) {                 

            AlertDialog.Builder alertDialog = 
                    new AlertDialog.Builder(getActivity());
            alertDialog.setTitle("Alert Dialog");
            alertDialog.setMessage("You have been alerted.");
            alertDialog.setIcon(android.R.drawable.btn_star);
            alertDialog.setPositiveButton(android.R.string.ok,
                    new DialogInterface.OnClickListener() {
                @Override         
                public void onClick(DialogInterface dialog, int which) {
                    Toast.makeText(getActivity(),
                            "Clicked OK!", Toast.LENGTH_SHORT).show();
                    return;
                }
            });
         return alertDialog.create();
         }
    }

    void showDialogFragment(DialogFragment newFragment) {
        newFragment.show(getFragmentManager(), null);
    }
}

The full implementation of this AlertDialog, as well as many other types of dialogs, can be found in the sample code provided on the book’s website.

Working with Custom Dialogs

When the Dialog types do not suit your purpose exactly, you can create a custom Dialog. One easy way to create a custom Dialog is to begin with an AlertDialog and use an AlertDialog.Builder class to override its default layout. In order to create a custom Dialog this way, the following steps must be performed:

1. Design a custom layout resource to display in the AlertDialog.

2. Define the custom Dialog identifier in the Activity or Fragment.

3. Use a LayoutInflater to inflate the custom layout resource for the Dialog.

4. Launch the Dialog using the show() method.

Figure 10.2 shows a custom Dialog implementation that accepts values into two EditText controls and, when OK is clicked, displays whether the two input values are equal.

Image

Figure 10.2 A custom Dialog implementation.

Working with Support Package Dialog Fragments

The previous example will work only on devices that are running Android 3.0 (API Level 11) or newer. If you want your DialogFragment implementation to work on devices running older versions of Android, you must make a few small changes to the code. Doing so will allow yourDialogFragment to work on devices all the way back to Android 1.6 (API Level 4).


Image Tip

Many of the code examples provided in this section are taken from the SupportFragDialog application. The source code for the SupportFragDialog application is provided for download on the book’s website.


Let’s look at a quick example of how you might implement a simple AlertDialog. In order to show an advantage of using the Fragment-based Dialog technique, we pass some data to the Dialog that demonstrates multiple instances of the DialogFragment class running within a singleActivity.

First, import the support version of the DialogFragment class (android.support.v4.app.DialogFragment) that is part of the Support Library. Then, just as before, you need to implement your own DialogFragment class. This class simply needs to be able to return an instance of the object that is fully configured and needs to implement the onCreateDialog method, which returns the fully configured AlertDialog, much as it did using the legacy method. The following code is a full implementation of a simple DialogFragment that manages an AlertDialog:

public class MyAlertDialogFragment extends DialogFragment {

    public static MyAlertDialogFragment
        newInstance(String fragmentNumber) {
        MyAlertDialogFragment newInstance = new MyAlertDialogFragment();
        Bundle args = new Bundle();
        args.putString("fragnum", fragmentNumber);
        newInstance.setArguments(args);
        return newInstance;
    }

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        final String fragNum = getArguments().getString("fragnum");

        AlertDialog.Builder alertDialog = new AlertDialog.Builder(
            getActivity());
        alertDialog.setTitle("Alert Dialog");
        alertDialog.setMessage("This alert brought to you by "
            + fragNum );
        alertDialog.setIcon(android.R.drawable.btn_star);
        alertDialog.setPositiveButton(android.R.string.ok,
                new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                ((SimpleFragDialogActivity) getActivity())
                    .doPositiveClick(fragNum);
                return;
            }
        });
        return alertDialog.create();
    }
}

Now that you have defined your DialogFragment, you can use it within your Activity much as you would any Fragment—but this time, you must use the support version of the FragmentManager class, by calling the getSupportFragmentManager() method.

In your Activity, you need to import two support classes for this implementation to work: android.support.v4.app.DialogFragment and android.support.v4.app.FragmentActivity. Be sure to extend your Activity class from FragmentActivity, and not Activity as in the previous example, or your code will not work. The FragmentActivity class is a special class that makes fragments available with the Support Package.

The following FragmentActivity class, called SupportFragDialogActivity, has a layout resource that contains two Button controls, each of which triggers a new instance of the MyAlertDialogFragment to be generated and shown. The show() method of the DialogFragment is used to display theDialog, adding the Fragment to the support version of the FragmentManager, and passing in a little bit of information to configure the specific instance of the DialogFragment and its internal AlertDialog.

public class SupportFragDialogActivity extends FragmentActivity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        // Handle Alert Dialog Button
        Button launchAlertDialog = (Button) findViewById(
            R.id.Button_AlertDialog);
        launchAlertDialog.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                String strFragmentNumber = "Fragment Instance One";
                DialogFragment newFragment = MyAlertDialogFragment
                    .newInstance(strFragmentNumber);
             showDialogFragment(newFragment, strFragmentNumber);
            }
        });

        // Handle Alert Dialog 2 Button
        Button launchAlertDialog2 = (Button) findViewById(
            R.id.Button_AlertDialog2);
        launchAlertDialog2.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                String strFragmentNumber = "Fragment Instance Two";
                DialogFragment newFragment = MyAlertDialogFragment
                    .newInstance(strFragmentNumber);
             showDialogFragment(newFragment, strFragmentNumber);
            }
        });
    }

    void showDialogFragment(DialogFragment newFragment, 
            String strFragmentNumber) {
        newFragment.show(getSupportFragmentManager(), strFragmentNumber);
    }

    public void doPositiveClick(String strFragmentNumber) {
        Toast.makeText(getApplicationContext(),
            "Clicked OK! (" + strFragmentNumber + ")",
            Toast.LENGTH_SHORT).show();
    }
}

Figure 10.3 shows a FragmentActivity using a DialogFragment to display a Dialog to the user with the Support Package.

Image

Figure 10.3 Using DialogFragment instances in an Activity.

DialogFragment instances can be traditional pop-ups (as shown in the example provided) or they can be embedded like any other Fragment. Why might you want to embed a Dialog? Consider the following example: You’ve created a picture gallery application and implemented a customDialog that displays a larger image when you click a thumbnail. On small-screen devices, you might want this to be a pop-up Dialog, but on a tablet or TV, you might have the screen space to show the larger graphic off to the right or below the thumbnails. This would be a good opportunity to take advantage of code reuse and simply embed your Dialog.

Summary

Dialogs are useful controls for keeping your Android application user interfaces clean and user-friendly. Many types of Dialog controls are defined in the Android SDK, and you can create custom Dialog controls if none of the canned controls suit your purposes.

Developers should be aware that there are two different but similar approaches to implementing DialogFragment components within applications. The first approach is not backward compatible, but it does work well with devices that use Honeycomb and beyond. The second approach involves using a special type of DialogFragment and FragmentActivity. This method is backward compatible and provides the DialogFragment class for legacy applications that you may be maintaining.

Quiz Questions

1. What are the different types of dialogs available within the Android SDK?

2. True or false: When using a DialogFragment, you define the Dialog in the Activity onCreateDialog() method.

3. What is the method used to stop showing a Dialog?

4. What Dialog type should you use when creating a custom Dialog?

5. True or false: To display a backward-compatible DialogFragment, your Activity class must extend from the FragmentActivity Support Package class.

6. What is the method call for retrieving the FragmentManager when using the Support Package?

Exercises

1. Using the Android documentation, determine what interface classes the DialogFragment class implements.

2. Create an application that displays an AlertDialog and implement the DialogInterface.OnCancelListener to display a cancel message set with setCancelMessage, saying that the Dialog has been canceled when the user cancels the Dialog.

3. Create an application that is single-pane on small devices and two-pane on large devices. Implement a simple DialogFragment with text that displays as a Dialog on small devices, but on large devices, embed the Fragment into the right pane of the two-pane layout.

References and More Information

Android SDK Reference regarding the application Dialog class:

http://d.android.com/reference/android/app/Dialog.html

Android SDK Reference regarding the application AlertDialog class:

http://d.android.com/reference/android/app/AlertDialog.html

Android SDK Reference regarding the application DatePickerDialog class:

http://d.android.com/reference/android/app/DatePickerDialog.html

Android SDK Reference regarding the application TimePickerDialog class:

http://d.android.com/reference/android/app/TimePickerDialog.html

Android SDK Reference regarding the application ProgressDialog class:

http://d.android.com/reference/android/app/ProgressDialog.html

Android SDK Reference regarding the application CharacterPickerDialog class:

http://d.android.com/reference/android/text/method/CharacterPickerDialog.html

Android SDK Reference regarding the application DialogFragment class:

http://d.android.com/reference/android/app/DialogFragment.html

Android API Guides: “Dialogs”:

http://d.android.com/guide/topics/ui/dialogs.html

Android DialogFragment Reference: “Selecting Between Dialog or Embedding”:

http://d.android.com/reference/android/app/DialogFragment.html#DialogOrEmbed