DialogFragments Tutorial

Goal – to give you a short overview of the following:

  • using a custom view alongside a standard title and buttons
  • saving state

Pre-Requisites – familiarity with the following:

  • Activities
  • Fragments
  • AlertDialog.Builder

Setting up the Basics

Let’s go ahead and set up the four files we’ll be using for this tutorial.

/**
 * Demo activity that allows user to display a dialog.
 *
 * @author brandon.rosenbaum
 *
 */
public class DialogFragmentActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Button buttonShowDialog = (Button) findViewById(R.id.button_show_dialog);
        buttonShowDialog.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                showDialog();
            }
        });
    }

    /**
     * Creates a new instance of our dialog and displays it.
     */
    private void showDialog() {
        DialogFragment newFragment = ExampleDialogFragment.newInstance();
        newFragment.show(getFragmentManager(), "dialog");
    }

    /**
     * Dialog class that contains a custom view, while keeping standard
     * dialog elements such as the title and positive/negative buttons.
     *
     * @author brandon.rosenbaum
     *
     */
    public static class ExampleDialogFragment extends DialogFragment {

        private static final String KEY_SAVE_RATING_BAR_VALUE = "KEY_SAVE_RATING_BAR_VALUE";

        private RatingBar mRatingBar;

        public static ExampleDialogFragment newInstance() {
            ExampleDialogFragment frag = new ExampleDialogFragment();
            return frag;
        }

        @Override
        public Dialog onCreateDialog(Bundle savedInstanceState) {
            AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(getActivity());

            View view = getActivity().getLayoutInflater().inflate(R.layout.fragment_example_dialog, null);

            mRatingBar = (RatingBar) view.findViewById(R.id.ratingBar1);
            if (savedInstanceState != null) {
                if (savedInstanceState.containsKey(KEY_SAVE_RATING_BAR_VALUE)) {
                    mRatingBar.setRating(savedInstanceState.getFloat(KEY_SAVE_RATING_BAR_VALUE));
                }
            }

            alertDialogBuilder.setView(view);
            alertDialogBuilder.setTitle(getString(R.string.dialog_title));
            alertDialogBuilder.setPositiveButton(getString(R.string.dialog_positive_text), new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    Toast.makeText(getActivity(), getString(R.string.dialog_positive_toast_message), Toast.LENGTH_SHORT).show();
                    dialog.dismiss();
                }
            });
            alertDialogBuilder.setNegativeButton(getString(R.string.dialog_negative_text), new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    Toast.makeText(getActivity(), getString(R.string.dialog_negative_toast_message), Toast.LENGTH_SHORT).show();
                    dialog.cancel();
                }
            });

            return alertDialogBuilder.create();
        }

        @Override
        public void onSaveInstanceState(Bundle outState) {
            outState.putFloat(KEY_SAVE_RATING_BAR_VALUE, mRatingBar.getRating());
            super.onSaveInstanceState(outState);
        }
    }
}
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".DialogFragmentActivity" >
    <Button
        android:id="@+id/button_show_dialog"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/show_dialog" />
</RelativeLayout>
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="app_name">DialogFragments</string>
    <string name="action_settings">Settings</string>
    <string name="show_dialog">Show Dialog</string>
    <string name="dialog_message">This is where a sample message could go. We could have just used an AlertDialog perhaps, but we want to show off the ability of setting a custom view while keeping other standard dialog elements. For example, we are using the standard Title and Yes/No buttons typically found in AlertDialog. To make things fun, we\'ll throw in a RatingBar. Try rotating your phone!</string>
    <string name="dialog_title">Custom Title</string>
    <string name="dialog_positive_text">Yes</string>
    <string name="dialog_positive_toast_message">You clicked Yes!</string>
    <string name="dialog_negative_text">No</string>
    <string name="dialog_negative_toast_message">You clicked No...</string>
</resources>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >
    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="10dp"
        android:text="@string/dialog_message" />
    <RatingBar
        android:id="@+id/ratingBar1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center" />
</LinearLayout>

Running the project should show a simple screen with a button centered on it. Clicking that button opens up our dialog.

You will notice that there are some elements that are pretty standard to all dialogs in ours, e.g. a title and positive/negative buttons, using your phone’s default UI scheme. Instead of a simple message, however, we have some custom content that includes some text and a RatingBar.

Let’s take a deeper look at how we’re getting a custom view with a rating bar in our dialog.

Standard Dialog, Custom View

DialogFragment actually comes with two ways (and possibly more) of setting your UI. For dialogs where almost all aspects need to be customized, you can use the onCreateView(...) method. This is where most Fragments do their UI setup as well.

However, if you need a fairly standard AlertDialog with just a small tweak to it (like our example), there is another method that you can see we are using – onCreateDialog(...). This allows you to build your AlertDialog and let the system handle the view creation. So let’s dive into the onCreateDialog(...).

Most of the code in here is just setting up our AlertDialog.Builder. This tutorial assumes that you’re already familiar with how to build an AlertDialog. We’re going to focus on lines 53 and 62, as these pertain to our custom view.

Line 53: inflates our custom view from the layout file fragment_example_dialog.xml
Line 62: sets our inflated view as the custom view for our dialog.

Pretty straightforward, right? It’s that easy to use a custom view! Now let’s look at our last topic.

Saving State

It all starts here with our newInstance() function.

public static ExampleDialogFragment newInstance() {
    ExampleDialogFragment frag = new ExampleDialogFragment();
    return frag;
}

This follows the same design pattern as shown on the Android Developer site and is useful later on if you need to add arguments to your DialogFragment instantiation.

The actual saving of data happens because of these lines, though.

private static final String KEY_SAVE_RATING_BAR_VALUE = "KEY_SAVE_RATING_BAR_VALUE";

KEY_SAVE_RATING_BAR_VALUE is our key value. This is how we know where to save/retrieve the rating data from in our Bundle.

@Override
public void onSaveInstanceState(Bundle outState) {
    outState.putFloat(KEY_SAVE_RATING_BAR_VALUE, mRatingBar.getRating());
    super.onSaveInstanceState(outState);
}

When something such as a screen rotation occurs, this method gets called. We just add our own little bit of code that makes sure to take our Rating Bar and store its value in the Bundle. Android will handle this Bundle and give it back to us at the proper time.

if (savedInstanceState != null) {
    if (savedInstanceState.containsKey(KEY_SAVE_RATING_BAR_VALUE)) {
        mRatingBar.setRating(savedInstanceState.getFloat(KEY_SAVE_RATING_BAR_VALUE));
    }
}

When our DialogFragment gets recreated, we check to see if our key is in savedInstanceState. If it is, then we pull it out and make sure to set the rating to it’s previous value. Viola!

Summary

This tutorial should have given you a good, base understanding of how DialogFragments can be used to mix & match custom and standard dialog elements. You also should see how data in a DialogFragment is persisted throughout a screen rotation or similar event.

Also, if you need to incorporate your DialogFragment with the support library, try checking out this post on the official Android Developers blog: http://android-developers.blogspot.com/2012/05/using-dialogfragments.html

Happy coding!

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s